diff -Nur linux-3.10.30/Documentation/ABI/testing/sysfs-class-mtd linux-3.10.30-cubox-i/Documentation/ABI/testing/sysfs-class-mtd --- linux-3.10.30/Documentation/ABI/testing/sysfs-class-mtd 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/ABI/testing/sysfs-class-mtd 2014-03-08 20:32:51.000000000 +0100 @@ -104,7 +104,7 @@ One of the following ASCII strings, representing the device type: - absent, ram, rom, nor, nand, dataflash, ubi, unknown + absent, ram, rom, nor, nand, mlc-nand, dataflash, ubi, unknown What: /sys/class/mtd/mtdX/writesize Date: April 2009 @@ -128,9 +128,8 @@ Contact: linux-mtd@lists.infradead.org Description: Maximum number of bit errors that the device is capable of - correcting within each region covering an ecc step. This will - always be a non-negative integer. Note that some devices will - have multiple ecc steps within each writesize region. + correcting within each region covering an ECC step (see + ecc_step_size). This will always be a non-negative integer. In the case of devices lacking any ECC capability, it is 0. @@ -173,3 +172,15 @@ This is generally applicable only to NAND flash devices with ECC capability. It is ignored on devices lacking ECC capability; i.e., devices for which ecc_strength is zero. + +What: /sys/class/mtd/mtdX/ecc_step_size +Date: May 2013 +KernelVersion: 3.10 +Contact: linux-mtd@lists.infradead.org +Description: + The size of a single region covered by ECC, known as the ECC + step. Devices may have several equally sized ECC steps within + each writesize region. + + It will always be a non-negative integer. In the case of + devices lacking any ECC capability, it is 0. diff -Nur linux-3.10.30/Documentation/DocBook/mtdnand.tmpl linux-3.10.30-cubox-i/Documentation/DocBook/mtdnand.tmpl --- linux-3.10.30/Documentation/DocBook/mtdnand.tmpl 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/DocBook/mtdnand.tmpl 2014-03-08 20:32:51.000000000 +0100 @@ -1222,10 +1222,6 @@ #define NAND_BBT_VERSION 0x00000100 /* Create a bbt if none axists */ #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 -Nur linux-3.10.30/Documentation/arm/small_task_packing.txt linux-3.10.30-cubox-i/Documentation/arm/small_task_packing.txt --- linux-3.10.30/Documentation/arm/small_task_packing.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/arm/small_task_packing.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,136 @@ +Small Task Packing in the big.LITTLE MP Reference Patch Set + +What is small task packing? +---- +Simply that the scheduler will fit as many small tasks on a single CPU +as possible before using other CPUs. A small task is defined as one +whose tracked load is less than 90% of a NICE_0 task. This is a change +from the usual behavior since the scheduler will normally use an idle +CPU for a waking task unless that task is considered cache hot. + + +How is it implemented? +---- +Since all small tasks must wake up relatively frequently, the main +requirement for packing small tasks is to select a partly-busy CPU when +waking rather than looking for an idle CPU. We use the tracked load of +the CPU runqueue to determine how heavily loaded each CPU is and the +tracked load of the task to determine if it will fit on the CPU. We +always start with the lowest-numbered CPU in a sched domain and stop +looking when we find a CPU with enough space for the task. + +Some further tweaks are necessary to suppress load balancing when the +CPU is not fully loaded, otherwise the scheduler attempts to spread +tasks evenly across the domain. + + +How does it interact with the HMP patches? +---- +Firstly, we only enable packing on the little domain. The intent is that +the big domain is intended to spread tasks amongst the available CPUs +one-task-per-CPU. The little domain however is attempting to use as +little power as possible while servicing its tasks. + +Secondly, since we offload big tasks onto little CPUs in order to try +to devote one CPU to each task, we have a threshold above which we do +not try to pack a task and instead will select an idle CPU if possible. +This maintains maximum forward progress for busy tasks temporarily +demoted from big CPUs. + + +Can the behaviour be tuned? +---- +Yes, the load level of a 'full' CPU can be easily modified in the source +and is exposed through sysfs as /sys/kernel/hmp/packing_limit to be +changed at runtime. The presence of the packing behaviour is controlled +by CONFIG_SCHED_HMP_LITTLE_PACKING and can be disabled at run-time +using /sys/kernel/hmp/packing_enable. +The definition of a small task is hard coded as 90% of NICE_0_LOAD +and cannot be modified at run time. + + +Why do I need to tune it? +---- +The optimal configuration is likely to be different depending upon the +design and manufacturing of your SoC. + +In the main, there are two system effects from enabling small task +packing. + +1. CPU operating point may increase +2. wakeup latency of tasks may be increased + +There are also likely to be secondary effects from loading one CPU +rather than spreading tasks. + +Note that all of these system effects are dependent upon the workload +under consideration. + + +CPU Operating Point +---- +The primary impact of loading one CPU with a number of light tasks is to +increase the compute requirement of that CPU since it is no longer idle +as often. Increased compute requirement causes an increase in the +frequency of the CPU through CPUfreq. + +Consider this example: +We have a system with 3 CPUs which can operate at any frequency between +350MHz and 1GHz. The system has 6 tasks which would each produce 10% +load at 1GHz. The scheduler has frequency-invariant load scaling +enabled. Our DVFS governor aims for 80% utilization at the chosen +frequency. + +Without task packing, these tasks will be spread out amongst all CPUs +such that each has 2. This will produce roughly 20% system load, and +the frequency of the package will remain at 350MHz. + +With task packing set to the default packing_limit, all of these tasks +will sit on one CPU and require a package frequency of ~750MHz to reach +80% utilization. (0.75 = 0.6 * 0.8). + +When a package operates on a single frequency domain, all CPUs in that +package share frequency and voltage. + +Depending upon the SoC implementation there can be a significant amount +of energy lost to leakage from idle CPUs. The decision about how +loaded a CPU must be to be considered 'full' is therefore controllable +through sysfs (sys/kernel/hmp/packing_limit) and directly in the code. + +Continuing the example, lets set packing_limit to 450 which means we +will pack tasks until the total load of all running tasks >= 450. In +practise, this is very similar to a 55% idle 1Ghz CPU. + +Now we are only able to place 4 tasks on CPU0, and two will overflow +onto CPU1. CPU0 will have a load of 40% and CPU1 will have a load of +20%. In order to still hit 80% utilization, CPU0 now only needs to +operate at (0.4*0.8=0.32) 320MHz, which means that the lowest operating +point will be selected, the same as in the non-packing case, except that +now CPU2 is no longer needed and can be power-gated. + +In order to use less energy, the saving from power-gating CPU2 must be +more than the energy spent running CPU0 for the extra cycles. This +depends upon the SoC implementation. + +This is obviously a contrived example requiring all the tasks to +be runnable at the same time, but it illustrates the point. + + +Wakeup Latency +---- +This is an unavoidable consequence of trying to pack tasks together +rather than giving them a CPU each. If you cannot find an acceptable +level of wakeup latency, you should turn packing off. + +Cyclictest is a good test application for determining the added latency +when configuring packing. + + +Why is it turned off for the VersatileExpress V2P_CA15A7 CoreTile? +---- +Simply, this core tile only has power gating for the whole A7 package. +When small task packing is enabled, all our low-energy use cases +normally fit onto one A7 CPU. We therefore end up with 2 mostly-idle +CPUs and one mostly-busy CPU. This decreases the amount of time +available where the whole package is idle and can be turned off. + diff -Nur linux-3.10.30/Documentation/devicetree/bindings/arm/cci.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/cci.txt --- linux-3.10.30/Documentation/devicetree/bindings/arm/cci.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/cci.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,172 @@ +======================================================= +ARM CCI cache coherent interconnect binding description +======================================================= + +ARM multi-cluster systems maintain intra-cluster coherency through a +cache coherent interconnect (CCI) that is capable of monitoring bus +transactions and manage coherency, TLB invalidations and memory barriers. + +It allows snooping and distributed virtual memory message broadcast across +clusters, through memory mapped interface, with a global control register +space and multiple sets of interface control registers, one per slave +interface. + +Bindings for the CCI node follow the ePAPR standard, available from: + +www.power.org/documentation/epapr-version-1-1/ + +with the addition of the bindings described in this document which are +specific to ARM. + +* CCI interconnect node + + Description: Describes a CCI cache coherent Interconnect component + + Node name must be "cci". + Node's parent must be the root node /, and the address space visible + through the CCI interconnect is the same as the one seen from the + root node (ie from CPUs perspective as per DT standard). + Every CCI node has to define the following properties: + + - compatible + Usage: required + Value type: + Definition: must be set to + "arm,cci-400" + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies base physical + address of CCI control registers common to all + interfaces. + + - ranges: + Usage: required + Value type: + Definition: A standard property. Follow rules in the ePAPR for + hierarchical bus addressing. CCI interfaces + addresses refer to the parent node addressing + scheme to declare their register bases. + + CCI interconnect node can define the following child nodes: + + - CCI control interface nodes + + Node name must be "slave-if". + Parent node must be CCI interconnect node. + + A CCI control interface node must contain the following + properties: + + - compatible + Usage: required + Value type: + Definition: must be set to + "arm,cci-400-ctrl-if" + + - interface-type: + Usage: required + Value type: + Definition: must be set to one of {"ace", "ace-lite"} + depending on the interface type the node + represents. + + - reg: + Usage: required + Value type: + Definition: the base address and size of the + corresponding interface programming + registers. + +* CCI interconnect bus masters + + Description: masters in the device tree connected to a CCI port + (inclusive of CPUs and their cpu nodes). + + A CCI interconnect bus master node must contain the following + properties: + + - cci-control-port: + Usage: required + Value type: + Definition: a phandle containing the CCI control interface node + the master is connected to. + +Example: + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + CPU0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + cci-control-port = <&cci_control1>; + reg = <0x0>; + }; + + CPU1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + cci-control-port = <&cci_control1>; + reg = <0x1>; + }; + + CPU2: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + cci-control-port = <&cci_control2>; + reg = <0x100>; + }; + + CPU3: cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + cci-control-port = <&cci_control2>; + reg = <0x101>; + }; + + }; + + dma0: dma@3000000 { + compatible = "arm,pl330", "arm,primecell"; + cci-control-port = <&cci_control0>; + reg = <0x0 0x3000000 0x0 0x1000>; + interrupts = <10>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; + }; + + cci@2c090000 { + compatible = "arm,cci-400"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x2c090000 0 0x1000>; + ranges = <0x0 0x0 0x2c090000 0x6000>; + + cci_control0: slave-if@1000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace-lite"; + reg = <0x1000 0x1000>; + }; + + cci_control1: slave-if@4000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x4000 0x1000>; + }; + + cci_control2: slave-if@5000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x5000 0x1000>; + }; + }; + +This CCI node corresponds to a CCI component whose control registers sits +at address 0x000000002c090000. +CCI slave interface @0x000000002c091000 is connected to dma controller dma0. +CCI slave interface @0x000000002c094000 is connected to CPUs {CPU0, CPU1}; +CCI slave interface @0x000000002c095000 is connected to CPUs {CPU2, CPU3}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt --- linux-3.10.30/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,64 @@ +Freescale Busfreq driver + +It is a generic driver that manages the frequency of the DDR, AHB and AXI buses in the iMX6x architecture. +It works for both SMP and UP systems and for both DDR3 and LPDDR2 memory types. + +Required properties are listed below: +- compatible: should be "fsl,imx6_busfreq" +- clocks: Lists the various clocks used by the busfreq driver +- interrupts - Lists the interrupts used by the busfreq driver. This is needed only for SMP architecutre. +- fsl,max_ddr_freq - The max ddr freq for this chip + +Examples: +For SOC imx6q.dtsi: + busfreq { /* BUSFREQ */ + compatible = "fsl,imx6_busfreq"; + clocks = <&clks 171>, <&clks 6>, <&clks 11>, <&clks 104>, <&clks 172>, <&clks 58>, + <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>; + clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", + "periph_pre", "periph_clk2", "periph_clk2_sel", "osc"; + interrupts = <0 107 0x04>, <0 112 0x4>, <0 113 0x4>, <0 114 0x4>; + interrupt-names = "irq_busfreq_0", "irq_busfreq_1", "irq_busfreq_2", "irq_busfreq_3"; + fsl,max_ddr_freq = <528000000>; + }; + +The Freescale Busfreq driver supports the following setpoints for the DDR freq: +enum bus_freq_mode { + BUS_FREQ_HIGH, -> The max freq the SOC supports + BUS_FREQ_MED, -> Medium setpoint (ex 400MHz for DDR3 when the max is 528MHz) + BUS_FREQ_AUDIO, -> Audio playback freq (50MHz) + BUS_FREQ_LOW, -> Low power IDLE freq (24MHz) +}; + +Currently the Freescale Busfreq driver implementation requires drivers to call the following APIs: +1. request_bus_freq(enum bus_freq_mode): + The driver is requesting the system and ddr freq to be set to the requested value. The driver should call this + API before it even enables its clocks. + +2. release_bus_freq(enum bus_freq_mode): + The driver no longer needs the system and ddr freq at the required value. The driver should call this API after + its work is done and it has disabled its clocks. + +Examples: +In the IPU driver, the requesting and releasing of the required bus frequency is tied into the runtime PM implementation: + +int ipu_runtime_suspend(struct device *dev) +{ + release_bus_freq(BUS_FREQ_HIGH); + dev_dbg(dev, "ipu busfreq high release.\n"); + + return 0; +} + +int ipu_runtime_resume(struct device *dev) +{ + request_bus_freq(BUS_FREQ_HIGH); + dev_dbg(dev, "ipu busfreq high requst.\n"); + + return 0; +} + +static const struct dev_pm_ops ipu_pm_ops = { + SET_RUNTIME_PM_OPS(ipu_runtime_suspend, ipu_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(ipu_suspend, ipu_resume) +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/arm/imx/gpc.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/imx/gpc.txt --- linux-3.10.30/Documentation/devicetree/bindings/arm/imx/gpc.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/imx/gpc.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,20 @@ +Freescale imx GPC bindings + +Optional properties: +- fsl,cpu_pupscr_sw2iso: for powering up CPU, number of 32K clock cycle PGC will wait before negating isolation signal. +- fsl,cpu_pupscr_sw: for powering up CPU, number of 32K clock cycle PGC will wait before asserting isolation signal. +- fsl,cpu_pdnscr_iso2sw: for powering down CPU, number of ipg clock cycle PGC will wait before negating isolation signal. +- fsl,cpu_pdnscr_iso: for powering down CPU, number of ipg clock cycle PGC will wait before asserting isolation signal. + +These properties are for adjusting the GPC PGC CPU power up/down setting, if there is no such property in dts, then default +value in GPC PGC registers will be used. + + +Example: + + &gpc { + fsl,cpu_pupscr_sw2iso = <0xf>; + fsl,cpu_pupscr_sw = <0xf>; + fsl,cpu_pdnscr_iso2sw = <0x1>; + fsl,cpu_pdnscr_iso = <0x1>; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/arm/pmu.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/pmu.txt --- linux-3.10.30/Documentation/devicetree/bindings/arm/pmu.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/pmu.txt 2014-03-08 20:32:51.000000000 +0100 @@ -16,6 +16,9 @@ "arm,arm1176-pmu" "arm,arm1136-pmu" - interrupts : 1 combined interrupt or 1 per core. +- cluster : a phandle to the cluster to which it belongs + If there are more than one cluster with same CPU type + then there should be separate PMU nodes per cluster. Example: diff -Nur linux-3.10.30/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt --- linux-3.10.30/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,19 @@ +ARM Dual Cluster System Configuration Block +------------------------------------------- + +The Dual Cluster System Configuration Block (DCSCB) provides basic +functionality for controlling clocks, resets and configuration pins in +the Dual Cluster System implemented by the Real-Time System Model (RTSM). + +Required properties: + +- compatible : should be "arm,rtsm,dcscb" + +- reg : physical base address and the size of the registers window + +Example: + + dcscb@60000000 { + compatible = "arm,rtsm,dcscb"; + reg = <0x60000000 0x1000>; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/bus/imx-weim.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/bus/imx-weim.txt --- linux-3.10.30/Documentation/devicetree/bindings/bus/imx-weim.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/bus/imx-weim.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,49 @@ +Device tree bindings for i.MX Wireless External Interface Module (WEIM) + +The term "wireless" does not imply that the WEIM is literally an interface +without wires. It simply means that this module was originally designed for +wireless and mobile applications that use low-power technology. + +The actual devices are instantiated from the child nodes of a WEIM node. + +Required properties: + + - compatible: Should be set to "fsl,imx6q-weim" + - reg: A resource specifier for the register space + (see the example below) + - clocks: the clock, see the example below. + - #address-cells: Must be set to 2 to allow memory address translation + - #size-cells: Must be set to 1 to allow CS address passing + - ranges: Must be set up to reflect the memory layout with four + integer values for each chip-select line in use: + + 0 + +Timing property for child nodes. It is mandatory, not optional. + + - fsl,weim-cs-timing: The timing array, contains 6 timing values for the + child node. We can get the CS index from the child + node's "reg" property. This property contains the values + for the registers EIM_CSnGCR1, EIM_CSnGCR2, EIM_CSnRCR1, + EIM_CSnRCR2, EIM_CSnWCR1, EIM_CSnWCR2 in this order. + +Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM: + + weim: weim@021b8000 { + compatible = "fsl,imx6q-weim"; + reg = <0x021b8000 0x4000>; + clocks = <&clks 196>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x08000000>; + + nor@0,0 { + compatible = "cfi-flash"; + reg = <0 0 0x02000000>; + #address-cells = <1>; + #size-cells = <1>; + bank-width = <2>; + fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000 + 0x0000c000 0x1404a38e 0x00000000>; + }; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/clock/imx6q-clock.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/clock/imx6q-clock.txt --- linux-3.10.30/Documentation/devicetree/bindings/clock/imx6q-clock.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/clock/imx6q-clock.txt 2014-03-08 20:32:51.000000000 +0100 @@ -89,8 +89,6 @@ gpu3d_shader 74 ipu1_podf 75 ipu2_podf 76 - ldb_di0_podf 77 - ldb_di1_podf 78 ipu1_di0_pre 79 ipu1_di1_pre 80 ipu2_di0_pre 81 @@ -208,6 +206,29 @@ pll4_post_div 193 pll5_post_div 194 pll5_video_div 195 + eim_slow 196 + spdif 197 + cko2_sel 198 + cko2_podf 199 + cko2 200 + cko 201 + vdoa 202 + gpt_3m 203 + video_27m 204 + ldb_di0_div_7 205 + ldb_di1_div_7 206 + ldb_di0_div_sel 207 + ldb_di1_div_sel 208 + pll4_audio_div 209 + lvds1_sel 210 + lvds1_in 211 + lvds1_out 212 + caam_mem 213 + caam_aclk 214 + caam_ipg 215 + epit1 216 + epit2 217 + tzasc2 218 Examples: diff -Nur linux-3.10.30/Documentation/devicetree/bindings/clock/imx6sl-clock.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/clock/imx6sl-clock.txt --- linux-3.10.30/Documentation/devicetree/bindings/clock/imx6sl-clock.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/clock/imx6sl-clock.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,10 @@ +* Clock bindings for Freescale i.MX6 SoloLite + +Required properties: +- compatible: Should be "fsl,imx6sl-ccm" +- reg: Address and length of the register set +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx6sl-clock.h +for the full list of i.MX6 SoloLite clock IDs. diff -Nur linux-3.10.30/Documentation/devicetree/bindings/clock/vf610-clock.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/clock/vf610-clock.txt --- linux-3.10.30/Documentation/devicetree/bindings/clock/vf610-clock.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/clock/vf610-clock.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,26 @@ +* Clock bindings for Freescale Vybrid VF610 SOC + +Required properties: +- compatible: Should be "fsl,vf610-ccm" +- reg: Address and length of the register set +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. See include/dt-bindings/clock/vf610-clock.h +for the full list of VF610 clock IDs. + +Examples: + +clks: ccm@4006b000 { + compatible = "fsl,vf610-ccm"; + reg = <0x4006b000 0x1000>; + #clock-cells = <1>; +}; + +uart1: serial@40028000 { + compatible = "fsl,vf610-uart"; + reg = <0x40028000 0x1000>; + interrupts = <0 62 0x04>; + clocks = <&clks VF610_CLK_UART1>; + clock-names = "ipg"; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/cpufreq/cpufreq-imx6q.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/cpufreq/cpufreq-imx6q.txt --- linux-3.10.30/Documentation/devicetree/bindings/cpufreq/cpufreq-imx6q.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/cpufreq/cpufreq-imx6q.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,59 @@ +iMX6q/iMX6dl/iMX6sl specific CPUFREQ settings + +iMX6q/iMX6dl/iMX6sl has limitation that a couple of voltage rails (VDDSOC_CAP and VDDPU_CAP) +must track VDDARM_CAP within 50mV: +VDDARM_CAP - VDDSOC_CAP/VDDPU_CAP <= 50mV + +The VDDSOC_CAP and VDDPU_CAP operating points for various VDDARM_CAP settings are listed below. + +Required properties: +- fsl,soc-operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt + for details. It is a voltage frequency tuple. + +- For other entries in the example below please refer to Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt + +Examples: + +cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 1200000 1275000 + 996000 1250000 + 792000 1175000 + 396000 1075000 + >; + fsl,soc-operating-points = < + /* ARM kHz SOC-PU uV */ + 1200000 1275000 + 996000 1250000 + 792000 1175000 + 396000 1175000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + reg = <2>; + next-level-cache = <&L2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + reg = <3>; + next-level-cache = <&L2>; + }; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt --- linux-3.10.30/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt 2014-03-08 20:32:51.000000000 +0100 @@ -4,14 +4,71 @@ - compatible : Should be "fsl,-sdma" - reg : Should contain SDMA registers location and length - interrupts : Should contain SDMA interrupt +- #dma-cells : Must be <3>. + The first cell specifies the DMA request/event ID. See details below + about the second and third cell. - fsl,sdma-ram-script-name : Should contain the full path of SDMA RAM scripts firmware +The second cell of dma phandle specifies the peripheral type of DMA transfer. +The full ID of peripheral types can be found below. + + ID transfer type + --------------------- + 0 MCU domain SSI + 1 Shared SSI + 2 MMC + 3 SDHC + 4 MCU domain UART + 5 Shared UART + 6 FIRI + 7 MCU domain CSPI + 8 Shared CSPI + 9 SIM + 10 ATA + 11 CCM + 12 External peripheral + 13 Memory Stick Host Controller + 14 Shared Memory Stick Host Controller + 15 DSP + 16 Memory + 17 FIFO type Memory + 18 SPDIF + 19 IPU Memory + 20 ASRC + 21 ESAI + 22 HDMI Audio + +The third cell specifies the transfer priority as below. + + ID transfer priority + ------------------------- + 0 High + 1 Medium + 2 Low + Examples: sdma@83fb0000 { compatible = "fsl,imx51-sdma", "fsl,imx35-sdma"; reg = <0x83fb0000 0x4000>; interrupts = <6>; + #dma-cells = <3>; fsl,sdma-ram-script-name = "sdma-imx51.bin"; }; + +DMA clients connected to the i.MX SDMA controller must use the format +described in the dma.txt file. + +Examples: + +ssi2: ssi@70014000 { + compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; + reg = <0x70014000 0x4000>; + interrupts = <30>; + clocks = <&clks 49>; + dmas = <&sdma 24 1 0>, + <&sdma 25 1 0>; + dma-names = "rx", "tx"; + fsl,fifo-depth = <15>; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/fb/fsl_epdc_fb.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/fb/fsl_epdc_fb.txt --- linux-3.10.30/Documentation/devicetree/bindings/fb/fsl_epdc_fb.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/fb/fsl_epdc_fb.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,30 @@ +* Freescale MXC Electrophoretic Display Controller (EPDC) + +Required properties: +- compatible: Should be "fsl,-epdc". Supported chips include + imx6dl and imx6sl +- reg: Address and length of the register set for EPDC +- interrupts: Should contain EPDC interrupts +- clocks: the clocks for EPDC +- pinctrl-names: should be "default" +- pinctrl-0: should be pinctrl_ipu1_1 or pinctrl_ipu2_1, which depends on the + IPU connected. +- V3P3_supply: power supply for EPDC_PWRCTRL0 from pmic +- VCOM_supply: power supply for EPDC_VCOM0 from pmic +- DISPLAY_supply: power supply enable for pmic + +Examples: + +imx6_epdc@0x020f8000 { + compatible = "fsl,imx6dl-epdc"; + reg = <0x020f8000 4000>; + interrupts = <0 97 0x04>; + clocks = <&clks 133>, <&clks 137>; /* ipu2, ipu2_di1 */ + clock-names = "epdc-axi", "epdc-pix"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_epdc_0>; + V3P3_supply = <&V3P3_reg>; + VCOM_supply = <&VCOM_reg>; + DISPLAY_supply = <&DISPLAY_reg>; + status = "disabled"; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt --- linux-3.10.30/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,146 @@ +* FSL IPUv3 Display/FB + +The FSL IPUv3 is Image Processing Unit version 3, a part of video and graphics +subsystem in an application processor. The goal of the IPU is to provide +comprehensive support for the flow of data from an image sensor or/and to a +display device. + +Two IPU units are on the imx6q SOC while only one IPU unit on the imx6dl SOC. +Each IPU unit has two display interfaces. + +For LDB/LVDS panel, there are two LVDS channels(LVDS0 and LVDS1) which can +transfer video data, these two channels can be used as +split/dual/single/separate mode. +-split mode means display data from DI0 or DI1 will send to both channels + LVDS0+LVDS1. +-dual mode means display data from DI0 or DI1 will be duplicated on LVDS0 + and LVDS1, it said, LVDS0 and LVDS1 has the same content. +-single mode means only work for DI0/DI1->LVDS0 or DI0/DI1->LVDS1. +-separate mode means you can make DI0/DI1->LVDS0 and DI0/DI1->LVDS1 work + at the same time. + "ldb=spl0/1" -- split mode on DI0/1 + "ldb=dul0/1" -- dual mode on DI0/1 + "ldb=sin0/1" -- single mode on LVDS0/1 + "ldb=sep0/1" -- separate mode begin from LVDS0/1 + +Required properties for IPU: +- bypass_reset :Bypass reset to avoid display channel being. + stopped by probe since it may start to work in bootloader: 0 or 1. +- compatible : should be "fsl,imx6q-ipu". +- reg : the register address range. +- interrupts : the error and sync interrupts request. +- clocks : the clock sources that it depends on. +- clock-names: the related clock names. +- resets : IPU reset specifier. See reset.txt and fsl,imx-src.txt in + Documentation/devicetree/bindings/reset/ for details. + +Required properties for fb: +- compatible : should be "fsl,mxc_sdc_fb". +- disp_dev : display device: "ldb", "lcd", "hdmi", "mipi_dsi". +- mode_str : video mode string: "LDB-XGA" or "LDB-1080P60" for ldb, + "CLAA-WVGA" for lcd, "TRULY-WVGA" for TRULY mipi_dsi lcd panel, + "1920x1080M@60" for hdmi. +- default_bpp : default bits per pixel: 8/16/24/32 +- int_clk : use internal clock as pixel clock: 0 or 1 +- late_init : to avoid display channel being re-initialized + as we've probably setup the channel in bootloader: 0 or 1 +- interface_pix_fmt : display interface pixel format as below: + RGB666 IPU_PIX_FMT_RGB666 + RGB565 IPU_PIX_FMT_RGB565 + RGB24 IPU_PIX_FMT_RGB24 + BGR24 IPU_PIX_FMT_BGR24 + GBR24 IPU_PIX_FMT_GBR24 + YUV444 IPU_PIX_FMT_YUV444 + LVDS666 IPU_PIX_FMT_LVDS666 + YUYV IPU_PIX_FMT_YUYV + UYVY IPU_PIX_FMT_UYVY + YVYV IPU_PIX_FMT_YVYU + VYUY IPU_PIX_FMT_VYUY + +Required properties for display: +- compatible : should be "fsl,lcd" for lcd panel, "fsl,imx6q-ldb" for ldb +- reg : the register address range if necessary to have. +- interrupts : the error and sync interrupts if necessary to have. +- clocks : the clock sources that it depends on if necessary to have. +- clock-names: the related clock names if necessary to have. +- ipu_id : ipu id for the first display device: 0 or 1 +- disp_id : display interface id for the first display interface: 0 or 1 +- default_ifmt : save as above display interface pixel format for lcd +- pinctrl-names : should be "default" +- pinctrl-0 : should be pinctrl_ipu1_1 or pinctrl_ipu2_1, which depends on the + IPU connected. +- sec_ipu_id : secondary ipu id for the second display device(ldb only): 0 or 1 +- sec_disp_id : secondary display interface id for the second display + device(ldb only): 0 or 1 +- ext_ref : reference resistor select for ldb only: 0 or 1 +- mode : ldb mode as below: + spl0 LDB_SPL_DI0 + spl1 LDB_SPL_DI1 + dul0 LDB_DUL_DI0 + dul1 LDB_DUL_DI1 + sin0 LDB_SIN0 + sin1 LDB_SIN1 + sep0 LDB_SEP0 + sep1 LDB_SEP1 +- gpr : the mux controller for the display engine's display interfaces and the display encoder + (only valid for mipi dsi now). +- disp-power-on-supply : the regulator to control display panel's power. + (only valid for mipi dsi now). +- resets : the gpio pin to reset the display device(only valid for mipi display panel now). +- lcd_panel : the video mode name for the display device(only valid for mipi display panel now). +- dev_id : the display engine's identity within the system, which intends to replace ipu_id + (only valid for mipi dsi now). + +Example for IPU: + ipu1: ipu@02400000 { + compatible = "fsl,imx6q-ipu"; + reg = <0x02400000 0x400000>; + interrupts = <0 6 0x4 0 5 0x4>; + clocks = <&clks 130>, <&clks 131>, <&clks 132>, + <&clks 39>, <&clks 40>, + <&clks 135>, <&clks 136>; + clock-names = "bus", "di0", "di1", + "di0_sel", "di1_sel", + "ldb_di0", "ldb_di1"; + resets = <&src 2>; + bypass_reset = <0>; + }; + +Example for fb: + fb0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="LDB-XGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; + +Example for ldb display: + ldb@020e0000 { + ipu_id = <1>; + disp_id = <0>; + ext_ref = <1>; + mode = "sep0"; + sec_ipu_id = <1>; + sec_disp_id = <1>; + status = "okay"; + }; + +Example for mipi dsi display: + mipi_dsi: mipi@021e0000 { + compatible = "fsl,imx6q-mipi-dsi"; + reg = <0x021e0000 0x4000>; + interrupts = <0 102 0x04>; + gpr = <&gpr>; + clocks = <&clks 138>, <&clks 204>; + clock-names = "mipi_pllref_clk", "mipi_cfg_clk"; + dev_id = <0>; + disp_id = <0>; + lcd_panel = "TRULY-WVGA"; + disp-power-on-supply = <®_mipi_dsi_pwr_on> + resets = <&mipi_dsi_reset>; + status = "okay"; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/fb/mxsfb.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/fb/mxsfb.txt --- linux-3.10.30/Documentation/devicetree/bindings/fb/mxsfb.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/fb/mxsfb.txt 2014-03-08 20:32:51.000000000 +0100 @@ -3,6 +3,9 @@ Required properties: - compatible: Should be "fsl,-lcdif". Supported chips include imx23 and imx28. +- pinctrl-names: Should be "default" +- pinctrl-0: pinctrl setting for lcd +- lcd-supply: lcd power supply, usually via GPIO - reg: Address and length of the register set for lcdif - interrupts: Should contain lcdif interrupts - display : phandle to display node (see below for details) @@ -22,6 +25,10 @@ compatible = "fsl,imx28-lcdif"; reg = <0x80030000 2000>; interrupts = <38 86>; + pinctrl-names = "default"; + pinctrl-0 = <&lcdif_24bit_pins_a + &lcdif_pins_evk>; + lcd-supply = <®_lcd_3v3>; display: display { bits-per-pixel = <32>; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/input/touchscreen/elan-ts.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/input/touchscreen/elan-ts.txt --- linux-3.10.30/Documentation/devicetree/bindings/input/touchscreen/elan-ts.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/input/touchscreen/elan-ts.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,23 @@ +* ELAN Resistive Touch Controller + +Required properties: +- compatible: must be "elan,elan-touch" +- reg: i2c slave address +- interrupt-parent: the phandle for the interrupt controller +- interrupts: touch controller interrupt +- gpio_elan_cs: the gpio pin for chip select +- gpio_elan_rst: the gpio pin for chip reset +- gpio_intr: the gpio pin to be used for interrupt pin + +Example: + + elan@10 { + compatible = "elan,elan-touch"; + reg = <0x10>; + interrupt-parent = <&gpio3>; + interrupts = <28 3>; + gpio_elan_cs = <&gpio2 18 0>; + gpio_elan_rst = <&gpio3 8 0>; + gpio_intr = <&gpio3 28 0>; + status = "okay"; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/leds/leds-pwm.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/leds/leds-pwm.txt --- linux-3.10.30/Documentation/devicetree/bindings/leds/leds-pwm.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/leds/leds-pwm.txt 2014-03-08 20:32:51.000000000 +0100 @@ -13,6 +13,9 @@ For the pwms and pwm-names property please refer to: Documentation/devicetree/bindings/pwm/pwm.txt - max-brightness : Maximum brightness possible for the LED +- default-brightness : (optional) Default brightness for the LED +- active-low : (optional) For PWMs where the LED is wired to supply + rather than ground. - label : (optional) see Documentation/devicetree/bindings/leds/common.txt - linux,default-trigger : (optional) diff -Nur linux-3.10.30/Documentation/devicetree/bindings/mfd/vexpress-spc.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mfd/vexpress-spc.txt --- linux-3.10.30/Documentation/devicetree/bindings/mfd/vexpress-spc.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mfd/vexpress-spc.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,35 @@ +* ARM Versatile Express Serial Power Controller device tree bindings + +Latest ARM development boards implement a power management interface (serial +power controller - SPC) that is capable of managing power/voltage and +operating point transitions, through memory mapped registers interface. + +On testchips like TC2 it also provides a configuration interface that can +be used to read/write values which cannot be read/written through simple +memory mapped reads/writes. + +- spc node + + - compatible: + Usage: required + Value type: + Definition: must be + "arm,vexpress-spc,v2p-ca15_a7","arm,vexpress-spc" + - reg: + Usage: required + Value type: + Definition: A standard property that specifies the base address + and the size of the SPC address space + - interrupts: + Usage: required + Value type: + Definition: SPC interrupt configuration. A standard property + that follows ePAPR interrupts specifications + +Example: + +spc: spc@7fff0000 { + compatible = "arm,vexpress-spc,v2p-ca15_a7","arm,vexpress-spc"; + reg = <0 0x7FFF0000 0 0x1000>; + interrupts = <0 95 4>; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/mlb/mlb150.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mlb/mlb150.txt --- linux-3.10.30/Documentation/devicetree/bindings/mlb/mlb150.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mlb/mlb150.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,22 @@ +* Freescale Media Local Bus Host Controller (MLB) for i.MX6Q/DL + +The Media Local Bus Host Controller on Freescale i.MX family +provides an interface for MOST network. + +Required properties: +- compatible : Should be "fsl,-mlb150" +- reg : Should contain mlb registers location and length +- interrupts : Should contain mlb interrupt +- clocks: Should contain the mlb clock sources +- clock-names: Should be the names of mlb clock sources +- iram : phandle pointing to the SRAM device node + +Examples: +mlb@0218c000 { + compatible = "fsl,imx6q-mlb150"; + reg = <0x0218c000 0x4000>; + interrupts = <0 53 0x04 0 117 0x04 0 126 0x04>; + clocks = <&clks 139>, <&clks 175>; + clock-names = "mlb", "pll8_mlb"; + iram = <&ocram>; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt --- linux-3.10.30/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt 2014-03-08 20:32:51.000000000 +0100 @@ -12,6 +12,11 @@ Optional properties: - fsl,cd-controller : Indicate to use controller internal card detection - fsl,wp-controller : Indicate to use controller internal write protection +- fsl,delay-line : Specify the number of delay cells for override mode. + This is used to set the clock delay for DLL(Delay Line) on override mode + to select a proper data sampling window in case the clock quality is not good + due to signal path is too long on the board. Please refer to eSDHC/uSDHC + chapter, DLL (Delay Line) section in RM for details. Examples: diff -Nur linux-3.10.30/Documentation/devicetree/bindings/mmc/mmc.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mmc/mmc.txt --- linux-3.10.30/Documentation/devicetree/bindings/mmc/mmc.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mmc/mmc.txt 2014-03-08 20:32:51.000000000 +0100 @@ -5,6 +5,8 @@ Interpreted by the OF core: - reg: Registers location and length. - interrupts: Interrupts used by the MMC controller. +- clocks: Clocks needed for the host controller, if any. +- clock-names: Goes with clocks above. Card detection: If no property below is supplied, host native card detect is used. @@ -29,6 +31,15 @@ - cap-power-off-card: powering off the card is safe - cap-sdio-irq: enable SDIO IRQ signalling on this interface +Card power and reset control: +The following properties can be specified for cases where the MMC +peripheral needs additional reset, regulator and clock lines. It is for +example common for WiFi/BT adapters to have these separate from the main +MMC bus: + - card-reset-gpios: Specify GPIOs for card reset (reset active low) + - card-external-vcc-supply: Regulator to drive (independent) card VCC + - clock with name "card_ext_clock": External clock provided to the card + *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line polarity properties, we have to fix the meaning of the "normal" and "inverted" line levels. We choose to follow the SDHCI standard, which specifies both those diff -Nur linux-3.10.30/Documentation/devicetree/bindings/mtd/gpmi-nand.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mtd/gpmi-nand.txt --- linux-3.10.30/Documentation/devicetree/bindings/mtd/gpmi-nand.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/mtd/gpmi-nand.txt 2014-03-08 20:32:51.000000000 +0100 @@ -17,6 +17,14 @@ Optional properties: - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false + - fsl,use-minimum-ecc: Protect this NAND flash with the minimum ECC + strength required. The required ECC strength is + automatically discoverable for some flash + (e.g., according to the ONFI standard). + However, note that if this strength is not + discoverable or this property is not enabled, + the software may chooses an implementation-defined + ECC scheme. The device tree may optionally contain sub-nodes describing partitions of the address space. See partition.txt for more detail. diff -Nur linux-3.10.30/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt --- linux-3.10.30/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt 2014-03-08 20:32:51.000000000 +0100 @@ -15,6 +15,13 @@ Optional properties: - clock-frequency : The oscillator frequency driving the flexcan device +- gpr: phandle to general purpose register node. The remote wakeup control + bits is stored here. + +Below are gpios for tranceiver: +- trx_en_gpio : enable gpio +- trx_stby_gpio : standby gpio +- trx_nerr_gpio : NERR gpio Example: diff -Nur linux-3.10.30/Documentation/devicetree/bindings/pci/designware-pcie.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/pci/designware-pcie.txt --- linux-3.10.30/Documentation/devicetree/bindings/pci/designware-pcie.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/pci/designware-pcie.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,81 @@ +* Synopsis Designware PCIe interface + +Required properties: +- compatible: should contain "snps,dw-pcie" to identify the + core, plus an identifier for the specific instance, such + as "samsung,exynos5440-pcie" or "fsl,imx6q-pcie". +- reg: base addresses and lengths of the pcie controller, + the phy controller, additional register for the phy controller. +- interrupts: interrupt values for level interrupt, + pulse interrupt, special interrupt. +- clocks: from common clock binding: handle to pci clock. +- clock-names: from common clock binding: should be "pcie" and "pcie_bus". +- #address-cells: set to <3> +- #size-cells: set to <2> +- device_type: set to "pci" +- ranges: ranges for the PCI memory and I/O regions +- #interrupt-cells: set to <1> +- interrupt-map-mask and interrupt-map: standard PCI properties + to define the mapping of the PCIe interface to interrupt + numbers. +- num-lanes: number of lanes to use +- reset-gpio: gpio pin number of power good signal + +Optional properties for fsl,imx6q-pcie +- power-on-gpio: gpio pin number of power-enable signal +- wake-up-gpio: gpio pin number of incoming wakeup signal +- disable-gpio: gpio pin number of outgoing rfkill/endpoint disable signal + +Example: + +SoC specific DT Entry: + + pcie@290000 { + compatible = "samsung,exynos5440-pcie", "snps,dw-pcie"; + reg = <0x290000 0x1000 + 0x270000 0x1000 + 0x271000 0x40>; + interrupts = <0 20 0>, <0 21 0>, <0 22 0>; + clocks = <&clock 28>, <&clock 27>; + clock-names = "pcie", "pcie_bus"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x00000800 0 0x40000000 0x40000000 0 0x00001000 /* configuration space */ + 0x81000000 0 0 0x40001000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0x0 0 &gic 53>; + num-lanes = <4>; + }; + + pcie@2a0000 { + compatible = "samsung,exynos5440-pcie", "snps,dw-pcie"; + reg = <0x2a0000 0x1000 + 0x272000 0x1000 + 0x271040 0x40>; + interrupts = <0 23 0>, <0 24 0>, <0 25 0>; + clocks = <&clock 29>, <&clock 27>; + clock-names = "pcie", "pcie_bus"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x00000800 0 0x60000000 0x60000000 0 0x00001000 /* configuration space */ + 0x81000000 0 0 0x60001000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0x0 0 &gic 56>; + num-lanes = <4>; + }; + +Board specific DT Entry: + + pcie@290000 { + reset-gpio = <&pin_ctrl 5 0>; + }; + + pcie@2a0000 { + reset-gpio = <&pin_ctrl 22 0>; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/pci/mvebu-pci.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/pci/mvebu-pci.txt --- linux-3.10.30/Documentation/devicetree/bindings/pci/mvebu-pci.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/pci/mvebu-pci.txt 2014-03-08 20:32:51.000000000 +0100 @@ -0,0 +1,221 @@ +* Marvell EBU PCIe interfaces + +Mandatory properties: +- compatible: one of the following values: + marvell,armada-370-pcie + marvell,armada-xp-pcie + marvell,kirkwood-pcie +- #address-cells, set to <3> +- #size-cells, set to <2> +- #interrupt-cells, set to <1> +- bus-range: PCI bus numbers covered +- device_type, set to "pci" +- ranges: ranges for the PCI memory and I/O regions, as well as the + MMIO registers to control the PCIe interfaces. + +In addition, the Device Tree node must have sub-nodes describing each +PCIe interface, having the following mandatory properties: +- reg: used only for interrupt mapping, so only the first four bytes + are used to refer to the correct bus number and device number. +- assigned-addresses: reference to the MMIO registers used to control + this PCIe interface. +- clocks: the clock associated to this PCIe interface +- marvell,pcie-port: the physical PCIe port number +- status: either "disabled" or "okay" +- device_type, set to "pci" +- #address-cells, set to <3> +- #size-cells, set to <2> +- #interrupt-cells, set to <1> +- ranges, empty property. +- interrupt-map-mask and interrupt-map, standard PCI properties to + define the mapping of the PCIe interface to interrupt numbers. + +and the following optional properties: +- marvell,pcie-lane: the physical PCIe lane number, for ports having + multiple lanes. If this property is not found, we assume that the + value is 0. + +Example: + +pcie-controller { + compatible = "marvell,armada-xp-pcie"; + status = "disabled"; + device_type = "pci"; + + #address-cells = <3>; + #size-cells = <2>; + + bus-range = <0x00 0xff>; + + ranges = <0x82000000 0 0xd0040000 0xd0040000 0 0x00002000 /* Port 0.0 registers */ + 0x82000000 0 0xd0042000 0xd0042000 0 0x00002000 /* Port 2.0 registers */ + 0x82000000 0 0xd0044000 0xd0044000 0 0x00002000 /* Port 0.1 registers */ + 0x82000000 0 0xd0048000 0xd0048000 0 0x00002000 /* Port 0.2 registers */ + 0x82000000 0 0xd004c000 0xd004c000 0 0x00002000 /* Port 0.3 registers */ + 0x82000000 0 0xd0080000 0xd0080000 0 0x00002000 /* Port 1.0 registers */ + 0x82000000 0 0xd0082000 0xd0082000 0 0x00002000 /* Port 3.0 registers */ + 0x82000000 0 0xd0084000 0xd0084000 0 0x00002000 /* Port 1.1 registers */ + 0x82000000 0 0xd0088000 0xd0088000 0 0x00002000 /* Port 1.2 registers */ + 0x82000000 0 0xd008c000 0xd008c000 0 0x00002000 /* Port 1.3 registers */ + 0x82000000 0 0xe0000000 0xe0000000 0 0x08000000 /* non-prefetchable memory */ + 0x81000000 0 0 0xe8000000 0 0x00100000>; /* downstream I/O */ + + pcie@1,0 { + device_type = "pci"; + assigned-addresses = <0x82000800 0 0xd0040000 0 0x2000>; + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 58>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; + }; + + pcie@2,0 { + device_type = "pci"; + assigned-addresses = <0x82001000 0 0xd0044000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 59>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <1>; + clocks = <&gateclk 6>; + status = "disabled"; + }; + + pcie@3,0 { + device_type = "pci"; + assigned-addresses = <0x82001800 0 0xd0048000 0 0x2000>; + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 60>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <2>; + clocks = <&gateclk 7>; + status = "disabled"; + }; + + pcie@4,0 { + device_type = "pci"; + assigned-addresses = <0x82002000 0 0xd004c000 0 0x2000>; + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 61>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <3>; + clocks = <&gateclk 8>; + status = "disabled"; + }; + + pcie@5,0 { + device_type = "pci"; + assigned-addresses = <0x82002800 0 0xd0080000 0 0x2000>; + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 62>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 9>; + status = "disabled"; + }; + + pcie@6,0 { + device_type = "pci"; + assigned-addresses = <0x82003000 0 0xd0084000 0 0x2000>; + reg = <0x3000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 63>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <1>; + clocks = <&gateclk 10>; + status = "disabled"; + }; + + pcie@7,0 { + device_type = "pci"; + assigned-addresses = <0x82003800 0 0xd0088000 0 0x2000>; + reg = <0x3800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 64>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <2>; + clocks = <&gateclk 11>; + status = "disabled"; + }; + + pcie@8,0 { + device_type = "pci"; + assigned-addresses = <0x82004000 0 0xd008c000 0 0x2000>; + reg = <0x4000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 65>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <3>; + clocks = <&gateclk 12>; + status = "disabled"; + }; + pcie@9,0 { + device_type = "pci"; + assigned-addresses = <0x82004800 0 0xd0042000 0 0x2000>; + reg = <0x4800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 99>; + marvell,pcie-port = <2>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 26>; + status = "disabled"; + }; + + pcie@10,0 { + device_type = "pci"; + assigned-addresses = <0x82005000 0 0xd0082000 0 0x2000>; + reg = <0x5000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + ranges; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &mpic 103>; + marvell,pcie-port = <3>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 27>; + status = "disabled"; + }; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/pinctrl/fsl,vf610-pinctrl.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/pinctrl/fsl,vf610-pinctrl.txt --- linux-3.10.30/Documentation/devicetree/bindings/pinctrl/fsl,vf610-pinctrl.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/pinctrl/fsl,vf610-pinctrl.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,41 @@ +Freescale Vybrid VF610 IOMUX Controller + +Please refer to fsl,imx-pinctrl.txt in this directory for common binding part +and usage. + +Required properties: +- compatible: "fsl,vf610-iomuxc" +- fsl,pins: two integers array, represents a group of pins mux and config + setting. The format is fsl,pins = , PIN_FUNC_ID is + a pin working on a specific function, CONFIG is the pad setting value + such as pull-up, speed, ode for this pin. Please refer to Vybrid VF610 + datasheet for the valid pad config settings. + +CONFIG bits definition: +PAD_CTL_SPEED_LOW (1 << 12) +PAD_CTL_SPEED_MED (2 << 12) +PAD_CTL_SPEED_HIGH (3 << 12) +PAD_CTL_SRE_FAST (1 << 11) +PAD_CTL_SRE_SLOW (0 << 11) +PAD_CTL_ODE (1 << 10) +PAD_CTL_HYS (1 << 9) +PAD_CTL_DSE_DISABLE (0 << 6) +PAD_CTL_DSE_150ohm (1 << 6) +PAD_CTL_DSE_75ohm (2 << 6) +PAD_CTL_DSE_50ohm (3 << 6) +PAD_CTL_DSE_37ohm (4 << 6) +PAD_CTL_DSE_30ohm (5 << 6) +PAD_CTL_DSE_25ohm (6 << 6) +PAD_CTL_DSE_20ohm (7 << 6) +PAD_CTL_PUS_100K_DOWN (0 << 4) +PAD_CTL_PUS_47K_UP (1 << 4) +PAD_CTL_PUS_100K_UP (2 << 4) +PAD_CTL_PUS_22K_UP (3 << 4) +PAD_CTL_PKE (1 << 3) +PAD_CTL_PUE (1 << 2) +PAD_CTL_OBE_ENABLE (1 << 1) +PAD_CTL_IBE_ENABLE (1 << 0) +PAD_CTL_OBE_IBE_ENABLE (3 << 0) + +Please refer to vf610-pinfunc.h in device tree source folder +for all available PIN_FUNC_ID for Vybrid VF610. diff -Nur linux-3.10.30/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt --- linux-3.10.30/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt 2014-03-08 20:32:52.000000000 +0100 @@ -71,6 +71,13 @@ name for integer state ID 0, list entry 1 for state ID 1, and so on. +pinctrl-assert-gpios: + List of phandles, each pointing at a GPIO which is used by some + board design to steer pins between two peripherals on the board. + It plays like a board level pin multiplexer to choose different + functions for given pins by pulling up/down the GPIOs. See + bindings/gpio/gpio.txt for details of how to specify GPIO. + For example: /* For a client device requiring named states */ diff -Nur linux-3.10.30/Documentation/devicetree/bindings/regulator/max17135-regulator.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/regulator/max17135-regulator.txt --- linux-3.10.30/Documentation/devicetree/bindings/regulator/max17135-regulator.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/regulator/max17135-regulator.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,112 @@ +Maxim MAX17135 Voltage Regulator + +The MAX17135 is a complete power-management IC for E-paper displays that +provides source- and gate-driver power supplies, a high-speed VCOM amplifier, +and a temperature sensor. It is interfaced to the host controller using an +i2c interface. + +Required properties : +- compatible : "maxim,max17135" +- reg: Specifies the i2c slave address of the pmic block. +- vneg_pwrup: the timing for VNEG power up +- gvee_pwrup: the timing for GVEE power up +- vpos_pwrup: the timing for VPOS power up +- gvdd_pwrup: the timing for GVDD power up +- gvdd_pwrdn: the timing for GVDD power down +- vpos_pwrdn: the timing for VPOS power down +- gvee_pwrdn: the timing for GVEE power down +- vneg_pwrdn: the timing for VNEG power down +- gpio_pmic_pwrgood: gpio setting for EPDC_PWRSTAT +- gpio_pmic_vcom_ctrl: gpio setting for EPDC_VCOM +- gpio_pmic_wakeup: gpio setting for EPDC_PWRWAKEUP +- gpio_pmic_v3p3: gpio setting for EPDC_PWRCTRL0 +- gpio_pmic_intr: gpio setting for EPDC_PWRINT + +Optional properties : +- SENSOR-supply: the gpio regulator to control the supply for this chip + + +Regulators: The regulators of max17135 that have to be instantiated should be +included in a sub-node named 'regulators'. Regulator nodes included in this +sub-node should be of the format as listed below. + + regulator_name { + standard regulator bindings here + }; + +Example: + max17135@48 { + compatible = "maxim,max17135"; + reg = <0x48>; + vneg_pwrup = <1>; + gvee_pwrup = <1>; + vpos_pwrup = <2>; + gvdd_pwrup = <1>; + gvdd_pwrdn = <1>; + vpos_pwrdn = <2>; + gvee_pwrdn = <1>; + vneg_pwrdn = <1>; + SENSOR-supply = <®_sensor>; + gpio_pmic_pwrgood = <&gpio2 21 0>; + gpio_pmic_vcom_ctrl = <&gpio3 17 0>; + gpio_pmic_wakeup = <&gpio3 20 0>; + gpio_pmic_v3p3 = <&gpio2 20 0>; + gpio_pmic_intr = <&gpio2 25 0>; + + regulators { + DISPLAY_reg: DISPLAY { + regulator-name = "DISPLAY"; + }; + + GVDD_reg: GVDD { + regulator-name = "GVDD"; + regulator-min-microvolt = <20000000>; + regulator-max-microvolt = <20000000>; + }; + + GVEE_reg: GVEE { + regulator-name = "GVEE"; + /* 2's-compliment, -22000000 */ + regulator-min-microvolt = <0xfeb04e80>; + regulator-max-microvolt = <0xfeb04e80>; + }; + + HVINN_reg: HVINN { + regulator-name = "HVINN"; + /* 2's-compliment, -22000000 */ + regulator-min-microvolt = <0xfeb04e80>; + regulator-max-microvolt = <0xfeb04e80>; + }; + + HVINP_reg: HVINP { + regulator-name = "HVINP"; + regulator-min-microvolt = <20000000>; + regulator-max-microvolt = <20000000>; + }; + + VCOM_reg: VCOM { + regulator-name = "VCOM"; + /* 2's-compliment, -4325000 */ + regulator-min-microvolt = <0xffbe0178>; + /* 2's-compliment, -500000 */ + regulator-max-microvolt = <0xfff85ee0>; + }; + + VNEG_reg: VNEG { + regulator-name = "VNEG"; + /* 2's-compliment, -15000000 */ + regulator-min-microvolt = <0xff1b1e40>; + regulator-max-microvolt = <0xff1b1e40>; + }; + + VPOS_reg: VPOS { + regulator-name = "VPOS"; + regulator-min-microvolt = <15000000>; + regulator-max-microvolt = <15000000>; + }; + + V3P3_reg: V3P3 { + regulator-name = "V3P3"; + }; + }; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/regulator/pfuze100.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/regulator/pfuze100.txt --- linux-3.10.30/Documentation/devicetree/bindings/regulator/pfuze100.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/regulator/pfuze100.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,115 @@ +PFUZE100 family of regulators + +Required properties: +- compatible: "fsl,pfuze100" +- reg: I2C slave address + +Required child node: +- regulators: This is the list of child nodes that specify the regulator + initialization data for defined regulators. Please refer to below doc + Documentation/devicetree/bindings/regulator/regulator.txt. + + The valid names for regulators are: + sw1ab,sw1c,sw2,sw3a,sw3b,sw4,swbst,vsnvs,vrefddr,vgen1~vgen6 + +Each regulator is defined using the standard binding for regulators. + +Example: + + pmic: pfuze100@08 { + compatible = "fsl,pfuze100"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1ab { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw1c_reg: sw1c { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3a { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3b_reg: sw3b { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + vgen4_reg: vgen4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen5_reg: vgen5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vgen6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/reset/gpio-reset.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/reset/gpio-reset.txt --- linux-3.10.30/Documentation/devicetree/bindings/reset/gpio-reset.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/reset/gpio-reset.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,35 @@ +GPIO reset controller +===================== + +A GPIO reset controller controls a single GPIO that is connected to the reset +pin of a peripheral IC. Please also refer to reset.txt in this directory for +common reset controller binding usage. + +Required properties: +- compatible: Should be "gpio-reset" +- reset-gpios: A gpio used as reset line. The gpio specifier for this property + depends on the gpio controller that provides the gpio. +- #reset-cells: 0, see below + +Optional properties: +- reset-delay-us: delay in microseconds. The gpio reset line will be asserted for + this duration to reset. +- initially-in-reset: boolean. If not set, the initial state should be a + deasserted reset line. If this property exists, the + reset line should be kept in reset. + +example: + +sii902x_reset: gpio-reset { + compatible = "gpio-reset"; + reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>; + reset-delay-us = <10000>; + initially-in-reset; + #reset-cells = <0>; +}; + +/* Device with nRESET pin connected to GPIO5_0 */ +sii902x@39 { + /* ... */ + resets = <&sii902x_reset>; /* active-low GPIO5_0, 10 ms delay */ +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/cs42888.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/cs42888.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/cs42888.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/cs42888.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,29 @@ +CS42888 audio CODEC + +This device supports I2C only. + +Required properties: + + - compatible: "cirrus,cs42888" + - reg: the I2C address of the device. + - clocks: Phandle to the clock node. + - clock-names: Contains name for each entry in clocks. + "codec_osc" : the external oscillator. + "esai" : the hckt clock from esai. + - -supply: Phandle to the regulator . + +Note: cs42888 needs a regulators node and a clocks node. + +Example: +In this case, the clock is external oscillator. + +codec: cs42888@48 { + compatible = "cirrus,cs42888"; + reg = <0x048>; + clocks = <&codec_osc 0>; + clock-names = "codec_osc"; + VA-supply = <®_audio>; + VD-supply = <®_audio>; + VLS-supply = <®_audio>; + VLC-supply = <®_audio>; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/fsl,spdif.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/fsl,spdif.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/fsl,spdif.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/fsl,spdif.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,54 @@ +Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller + +The Freescale S/PDIF audio block is a stereo transceiver that allows the +processor to receive and transmit digital audio via an coaxial cable or +a fibre cable. + +Required properties: + + - compatible : Compatible list, must contain "fsl,imx35-spdif". + + - reg : Offset and length of the register set for the device. + + - interrupts : Contains the spdif interrupt. + + - dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. + + - dma-names : Two dmas have to be defined, "tx" and "rx". + + - clocks : Contains an entry for each entry in clock-names. + + - clock-names : Includes the following entries: + "core" The core clock of spdif controller + "rxtx<0-7>" Clock source list for tx and rx clock. + This clock list should be identical to + the source list connecting to the spdif + clock mux in "SPDIF Transceiver Clock + Diagram" of SoC reference manual. It + can also be referred to TxClk_Source + bit of register SPDIF_STC. + +Example: + +spdif: spdif@02004000 { + compatible = "fsl,imx35-spdif"; + reg = <0x02004000 0x4000>; + interrupts = <0 52 0x04>; + dmas = <&sdma 14 18 0>, + <&sdma 15 18 0>; + dma-names = "rx", "tx"; + + clocks = <&clks 197>, <&clks 3>, + <&clks 197>, <&clks 107>, + <&clks 0>, <&clks 118>, + <&clks 62>, <&clks 139>, + <&clks 0>; + clock-names = "core", "rxtx0", + "rxtx1", "rxtx2", + "rxtx3", "rxtx4", + "rxtx5", "rxtx6", + "rxtx7"; + + status = "okay"; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/fsl-asrc-p2p.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/fsl-asrc-p2p.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/fsl-asrc-p2p.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/fsl-asrc-p2p.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,23 @@ +* Freescale Asynchronous Sample Rate Converter (ASRC) + +This document is for asrc p2p node. p2p is one of asrc mode. asrc p2p depend on +MXC_ASRC. + +Required properties: + - compatible: Should be "fsl,-asrc-p2p". + - fsl,output-rate: the output rate of asrc p2p. which can be <32000> to <192000>, + - fsl,output-width: the output width of asrc p2p. which can be <16>, <24>. + - fsl,asrc-dma-rx-events: The rx dma event of the asrc, corresponding + to 3 pair of asrc. + - fsl,asrc-dma-tx-events: The tx dma event of the esai, corresponding + to 3 pair of asrc. + +Example: +asrc_p2p: asrc_p2p { + compatible = "fsl,imx6q-asrc-p2p"; + fsl,output-rate = <48000>; + fsl,output-width = <16>; + fsl,asrc-dma-rx-events = <17 18 19>; + fsl,asrc-dma-tx-events = <20 21 22>; + status = "okay"; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/fsl-easi.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/fsl-easi.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/fsl-easi.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/fsl-easi.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,26 @@ +* Freescale Enhanced Serial Audio Interface (ESAI) + +Required properties: + - compatible: Should be "fsl,-esai". + - reg: Offset and length of the register set for the device. + - interrupts: Contains ESAI interrupt. + - clocks: Contains an entry for each entry in clock-names. + - fsl,fifo-depth: The number of elements in the transmit and receive FIFOs. + This number is the maximum allowed value for TFCR[TFWM]. + - fsl,esai-dma-events: The dma event of the esai, , a is the tx dma. + b is the rx dma. + - fsl,flags: <1> is for ESAI network mode, <2> is for ESAI SYNC mode. + <3> is for network and SYNC mode. + +Example: + +esai: esai@02024000 { + compatible = "fsl,imx6q-esai"; + reg = <0x02024000 0x4000>; + interrupts = <0 51 0x04>; + clocks = <&clks 118>; + fsl,fifo-depth = <128>; + fsl,esai-dma-events = <24 23>; + fsl,flags = <1>; + status = "disabled"; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/imx-audio-cs42888.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/imx-audio-cs42888.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/imx-audio-cs42888.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/imx-audio-cs42888.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,25 @@ +Freescale i.MX audio complex with CS42888 codec + +Required properties: +- compatible : "fsl,imx-audio-cs42888" +- model : The user-visible name of this sound complex +- esai-controller : The phandle of the i.MX SSI controller +- audio-codec : The phandle of the CS42888 audio codec + +Optional properties: +- asrc-controller : The phandle of the i.MX ASRC controller +- audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names could be power + supplies, CS42888 pins, and the jacks on the board: + +Example: + +sound { + compatible = "fsl,imx6q-sabresd-wm8962", + "fsl,imx-audio-wm8962"; + model = "cs42888-audio"; + esai-controller = <&esai>; + asrc-controller = <&asrc_p2p>; + audio-codec = <&codec>; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/imx-audio-si476x.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/imx-audio-si476x.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/imx-audio-si476x.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/imx-audio-si476x.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,24 @@ +Freescale i.MX audio complex with si476x codec + +Required properties: +- compatible : "fsl,imx-audio-si476x" +- model : The user-visible name of this sound complex +- ssi-controller : The phandle of the i.MX SSI controller + +- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) +- mux-ext-port : The external port of the i.MX audio muxer + +Note: The AUDMUX port numbering should start at 1, which is consistent with +hardware manual. + +Example: + +sound { + compatible = "fsl,imx-audio-si476x", + "fsl,imx-tuner-si476x"; + model = "imx-radio-si476x"; + + ssi-controller = <&ssi1>; + mux-int-port = <2>; + mux-ext-port = <5>; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,34 @@ +Freescale i.MX audio complex with S/PDIF transceiver + +Required properties: + + - compatible : "fsl,imx-audio-spdif" + + - model : The user-visible name of this sound complex + + - spdif-controller : The phandle of the i.MX S/PDIF controller + + +Optional properties: + + - spdif-out : This is a boolean property. If present, the transmitting + function of S/PDIF will be enabled, indicating there's a physical + S/PDIF out connector/jack on the board or it's connecting to some + other IP block, such as an HDMI encoder/display-controller. + + - spdif-in : This is a boolean property. If present, the receiving + function of S/PDIF will be enabled, indicating there's a physical + S/PDIF in connector/jack on the board. + +* Note: At least one of these two properties should be set in the DT binding. + + +Example: + +sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + spdif-in; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,54 @@ +Freescale i.MX audio complex with WM8962 codec + +Required properties: +- compatible : "fsl,imx-audio-wm8962" +- model : The user-visible name of this sound complex +- ssi-controller : The phandle of the i.MX SSI controller +- audio-codec : The phandle of the WM8962 audio codec +- audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names could be power + supplies, WM8962 pins, and the jacks on the board: + + Power supplies: + * Mic Bias + + Board connectors: + * Mic Jack + * Headphone Jack + * Ext Spk + +- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) +- mux-ext-port : The external port of the i.MX audio muxer + +Note: The AUDMUX port numbering should start at 1, which is consistent with +hardware manual. + +Optional properties: +- hp-det-gpios : The gpio pin to detect plug in/out event that happens to + Headphone jack. +- mic-det-gpios: The gpio pin to detect plug in/out event that happens to + Microphone jack. + +Example: + +sound { + compatible = "fsl,imx6q-sabresd-wm8962", + "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + ssi-controller = <&ssi2>; + audio-codec = <&codec>; + audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "MICBIAS", "AMIC", + "IN3R", "MICBIAS", + "DMIC", "MICBIAS", + "DMICDAT", "DMIC"; + mux-int-port = <2>; + mux-ext-port = <3>; + hp-det-gpios = <&gpio7 8 1>; + mic-det-gpios = <&gpio1 9 1>; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/sound/wm8962.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/wm8962.txt --- linux-3.10.30/Documentation/devicetree/bindings/sound/wm8962.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/sound/wm8962.txt 2014-03-08 20:32:52.000000000 +0100 @@ -8,9 +8,40 @@ - reg : the I2C address of the device. +Optional properties: + - spk-mono: This is a boolean property. If present, the SPK_MONO bit + of R51 (Class D Control 2) gets set, indicating that the speaker is + in mono mode. + + - amic-mono: This is a boolean property. If present, indicating that the + analog micphone is hardware mono input, the driver would enable monomix + for it. + + - dmic-mono: This is a boolean property. If present, indicating that the + digital micphone is hardware mono input, the driver would enable monomix + for it. + + - mic-cfg : Default register value for R48 (Additional Control 4). + If absent, the default should be the register default. + + - gpio-cfg : A list of GPIO configuration register values. The list must + be 6 entries long. If absent, no configuration of these registers is + performed. And note that only the value within [0x0, 0xffff] is valid. + Any other value is regarded as setting the GPIO register by its reset + value 0x0. + Example: codec: wm8962@1a { compatible = "wlf,wm8962"; reg = <0x1a>; + + gpio-cfg = < + 0x0000 /* 0:Default */ + 0x0000 /* 1:Default */ + 0x0013 /* 2:FN_DMICCLK */ + 0x0000 /* 3:Default */ + 0x8014 /* 4:FN_DMICCDAT */ + 0x0000 /* 5:Default */ + >; }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/spi/spi-bus.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/spi/spi-bus.txt --- linux-3.10.30/Documentation/devicetree/bindings/spi/spi-bus.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/spi/spi-bus.txt 2014-03-08 20:32:52.000000000 +0100 @@ -55,6 +55,16 @@ chip select active high - spi-3wire - (optional) Empty property indicating device requires 3-wire mode. +- spi-tx-bus-width - (optional) The bus width(number of data wires) that + used for MOSI. Defaults to 1 if not present. +- spi-rx-bus-width - (optional) The bus width(number of data wires) that + used for MISO. Defaults to 1 if not present. + +Some SPI controllers and devices support Dual and Quad SPI transfer mode. +It allows data in SPI system transfered in 2 wires(DUAL) or 4 wires(QUAD). +Now the value that spi-tx-bus-width and spi-rx-bus-width can receive is +only 1(SINGLE), 2(DUAL) and 4(QUAD). +Dual/Quad mode is not allowed when 3-wire mode is used. If a gpio chipselect is used for the SPI slave the gpio number will be passed via the cs_gpio diff -Nur linux-3.10.30/Documentation/devicetree/bindings/thermal/imx-thermal.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/thermal/imx-thermal.txt --- linux-3.10.30/Documentation/devicetree/bindings/thermal/imx-thermal.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/thermal/imx-thermal.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,17 @@ +* Temperature Monitor (TEMPMON) on Freescale i.MX SoCs + +Required properties: +- compatible : "fsl,imx6q-thermal" +- fsl,tempmon : phandle pointer to system controller that contains TEMPMON + control registers, e.g. ANATOP on imx6q. +- fsl,tempmon-data : phandle pointer to fuse controller that contains TEMPMON + calibration data, e.g. OCOTP on imx6q. The details about calibration data + can be found in SoC Reference Manual. + +Example: + +tempmon { + compatible = "fsl,imx6q-tempmon"; + fsl,tempmon = <&anatop>; + fsl,tempmon-data = <&ocotp>; +}; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt --- linux-3.10.30/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt 2014-03-08 20:32:52.000000000 +0100 @@ -8,6 +8,8 @@ Optional properties: - fsl,uart-has-rtscts : Indicate the uart has rts and cts - fsl,irda-mode : Indicate the uart supports irda mode +- fsl,dte-mode : Indicate the uart works in DTE mode. The uart works + is DCE mode by default. Example: @@ -16,4 +18,5 @@ reg = <0x73fbc000 0x4000>; interrupts = <31>; fsl,uart-has-rtscts; + fsl,dte-mode; }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt --- linux-3.10.30/Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/tty/serial/fsl-lpuart.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,14 @@ +* Freescale low power universal asynchronous receiver/transmitter (lpuart) + +Required properties: +- compatible : Should be "fsl,-lpuart" +- reg : Address and length of the register set for the device +- interrupts : Should contain uart interrupt + +Example: + +uart0: serial@40027000 { + compatible = "fsl,vf610-lpuart"; + reg = <0x40027000 0x1000>; + interrupts = <0 61 0x00>; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt --- linux-3.10.30/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt 2014-03-08 20:32:52.000000000 +0100 @@ -5,6 +5,12 @@ - reg: Should contain registers location and length - interrupts: Should contain controller interrupt +Recommended properies: +- phy_type: the type of the phy connected to the core. Should be one + of "utmi", "utmi_wide", "ulpi", "serial" or "hsic". Without this + property the PORTSC register won't be touched +- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" + Optional properties: - fsl,usbphy: phandler of usb phy that connects to the only one port - fsl,usbmisc: phandler of non-core register device, with one argument @@ -12,6 +18,12 @@ - vbus-supply: regulator for vbus - disable-over-current: disable over current detect - external-vbus-divider: enables off-chip resistor divider for Vbus +- imx6-usb-charger-detection: enable imx6 usb charger detect function, +only set it when the user wants SoC usb charger detection capabilities. +If the user wants to use charger IC's usb charger detection capabilities, +please do not set it. +- fsl,anatop: phandle for anatop module, anatop module is only existed +at imx6 SoC series Examples: usb@02184000 { /* USB OTG */ @@ -22,4 +34,6 @@ fsl,usbmisc = <&usbmisc 0>; disable-over-current; external-vbus-divider; + imx6-usb-charger-detection; + fsl,anatop = <&anatop>; }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/usb/mxs-phy.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/usb/mxs-phy.txt --- linux-3.10.30/Documentation/devicetree/bindings/usb/mxs-phy.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/usb/mxs-phy.txt 2014-03-08 20:32:52.000000000 +0100 @@ -1,13 +1,16 @@ * Freescale MXS USB Phy Device Required properties: -- compatible: Should be "fsl,imx23-usbphy" +- compatible: "fsl,imx23-usbphy" for imx23 and imx28, "fsl,imx6q-usbphy" +for imx6dq and imx6dl, "fsl,imx6sl-usbphy" for imx6sl - reg: Should contain registers location and length - interrupts: Should contain phy interrupt +- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series Example: usbphy1: usbphy@020c9000 { compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; reg = <0x020c9000 0x1000>; interrupts = <0 44 0x04>; + fsl,anatop = <&anatop>; }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/video/fsl,csi-v4l2-capture.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/fsl,csi-v4l2-capture.txt --- linux-3.10.30/Documentation/devicetree/bindings/video/fsl,csi-v4l2-capture.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/fsl,csi-v4l2-capture.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,61 @@ +* Freescale CMOS Sensor Interface (CSI) V4L2 Capture + +Required properties for CSI +- compatible: "fsl,-csi". Supported chip includes imx6sl +- reg: Address and length of the register set for CSI +- interrupts: Should contain CSI interrupts + +Required properties for v4l2_capture +- compatible: should be "fsl,-csi-v4l2", supported socs include imx6sl + +Required properties for sensor +- compatible: "," + please check the supported sensor in the Supported Sensor fields. +- reg: sensor I2C slave address +- pinctrl-names: should be "default" for parallel sensor +- pinctrl-0: should depend on the connection between sensor and i.MX + connection between sensor and i.MX could be only legacy parallel on i.MX6SL +- clocks: should be the clock source provided to sensor. +- clock-names: should be "csi_mclk" +- AVDD-supply: set according to the board. +- DVDD-supply: set according to the board. +- pwn-gpios: set according to the board. +- rst-gpios: set according to the board. +- csi_id: csi id for v4l2 capture device + should be 0 for i.MX6SL +- mclk: should the value of mclk clock send out the sensor. unit is Hz. +- mclk_source: should be 0 for i.MX6SL + +Supported Sensor +- ovti, ov5640 + +Example for CSI: + csi: csi@020e4000 { + compatible = "fsl,imx6sl-csi"; + reg = <0x020e4000 0x4000>; + interrupts = <0 7 0x04>; + status = "disabled"; + }; + +Examples for v4l2_capture: + csi_v4l2_cap { + compatible = "fsl,imx6q-v4l2-capture"; + status = "okay"; + }; + +Examples for sensors: + ov564x: ov564x@3c { + compatible = "ovti,ov564x"; + reg = <0x3c>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_csi_0>; + clocks = <&clks IMX6SL_CLK_CSI>; + clock-names = "csi_mclk"; + AVDD-supply = <&vgen6_reg>; /* 2.8v */ + DVDD-supply = <&vgen2_reg>; /* 1.5v*/ + pwn-gpios = <&gpio1 25 1>; + rst-gpios = <&gpio1 26 0>; + csi_id = <0>; + mclk = <24000000>; + mclk_source = <0>; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/video/fsl,mipi-csi2.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/fsl,mipi-csi2.txt --- linux-3.10.30/Documentation/devicetree/bindings/video/fsl,mipi-csi2.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/fsl,mipi-csi2.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,42 @@ +* Freescale MIPI CSI2 Controller for i.MX6DQ/i.MX6SDL + +Required properties for mipi csi2 controller: +- compatible: should be "fsl,imx6q-mipi-csi2" +- reg: contains mipi csi2 register base address and range +- interrupts: where type is a interrupt type, num is the + interrupt number and flag is a field that level/trigger information for + the interrupt. +- clocks: the clock sources that mipi csi2 depends on. +- clock-names: the name is related to the clock source one by one. +- status: should be set to "disable". + +Required properties for mipi csi2 on specified board: +- ipu_id: ipu id which mipi csi2 connected to. + should be 0 or 1 for i.MX6DQ; should be 0 for i.MX6SDL +- csi_id: csi id which mipi csi2 connected to. + should be 0 or 1 for i.MX6DQ/i.MX6SDL +- v_channel: virtual channel which send to MIPI CSI2 controller + should keep consistent with the input MIPI signal. +- lanes: data lanes of input MIPI signal. The maximum data lanes is 4. + should keep consistent with the input MIPI signal. +- status: should be set to "okay". + +Examples: +for SOC imx6qdl.dtsi: + mipi_csi@021dc000 { + compatible = "fsl,imx6q-mipi-csi2"; + reg = <0x021dc000 0x4000>; + interrupts = <0 100 0x04>, <0 101 0x04>; + clocks = <&clks 138>, <&clks 53>; + clock-names = "dphy_clk", "pixel_clk"; + status = "disabled"; + }; + +for board imx6qdl-sabresd.dtsi: + mipi_csi@021dc000 { + status = "okay"; + ipu_id = <0>; + csi_id = <1>; + v_channel = <0>; + lanes = <2>; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/video/fsl,pxp.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/fsl,pxp.txt --- linux-3.10.30/Documentation/devicetree/bindings/video/fsl,pxp.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/fsl,pxp.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,30 @@ +* Freescale PxP Controller for i.MX6DL, i.MX6SL + +Required properties for PxP controller: +- compatible: should be "fsl,-pxp-dma" +- reg: contains pxp register base address and range +- interrupts: where type is an interrupt type, num is the + interrupt number and flag is a field that level/trigger information for + the interrupt. +- clocks: the clock sources that pxp depends on. +- clock-names: the name is related to the clock source + +Required properties for pxp on specified board: +- status: should be set to "okay" if want to use PxP + +Examples: +for SOC imx6dl.dtsi: + pxp@020f0000 { + compatible = "fsl,imx6dl-pxp-dma"; + reg = <0x020f0000 0x4000>; + interrupts = <0 98 0x04>; + clocks = <&clks 133>; + clock-names = "pxp-axi"; + status = "disabled"; + }; + + +for board imx6dl-sabresd.dts: + &pxp { + status = "okay"; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/video/fsl,v4l2-capture.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/fsl,v4l2-capture.txt --- linux-3.10.30/Documentation/devicetree/bindings/video/fsl,v4l2-capture.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/fsl,v4l2-capture.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,102 @@ +* Freescale V4L2 Capture for i.MX6DQ/i.MX6SDL + +Required board properties for IPUv3 capture: +- clocks: should include the clock provided by i.MX6 to sensor +- clock-names: sensor clock's name should be "ipux_csiy" + x should be 1 or 2 for i.MX6DQ; should be 1 for i.MX6SDL + y is 0 or 1 for i.MX6DQ/i.MX6SDL +Note: other detailed information for IPUv3, please refer to +Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt + +Required properties for v4l2_capture +- compatible: should be "fsl,imx6q-v4l2-capture" +- ipu_id: ipu id for v4l2 capture device + should be 0 or 1 for i.MX6DQ; should be 0 for i.MX6SDL +- csi_id: csi id for v4l2 capture device + should be 0 or 1 for i.MX6DQ/i.MX6SDL +- mclk_source: should be 0 or 1. two mclk sources at most now +- status: should be set to "okay" to enable this device + +Required properties for sensor +- compatible: "," + please check the supported sensor in the Supported Sensor fields. +- reg: sensor I2C slave address +- pinctrl-names: should be "default" for parallel sensor +- pinctrl-0: should depend on the connection between sensor and i.MX + connection between sensor and i.MX could be MIPI-CSI2 or legacy parallel +- clocks: should be the clock source provided to sensor. +- clock-names: should be "csi_mclk" +- DOVDD-supply: set according to the board. +- AVDD-supply: set according to the board. +- DVDD-supply: set according to the board. +- pwn-gpios: set according to the board. +- rst-gpios: set according to the board. +- csi_id: csi id for v4l2 capture device + should be 0 or 1 for i.MX6DQ/i.MX6SDL. +- mclk: should the value of mclk clock send out the sensor. unit is Hz. +- mclk_source: should be 0 or 1 and should be the same as the setting in + v4l2_capture. +- cvbs: 1 for CVBS input, 0 YPbPr input. This property is only needed for + adv7180 tv decoder. + +Supported Sensor +- ov5640 +- ov5642 +- ov5640_mipi +- adv7180 + + +Example for IPUv3 including capture settings on imx6q-sabresd.dts: + ipu1: ipu@02400000 { /* IPU1 */ + compatible = "fsl,imx6q-ipuv3"; + reg = <0x02400000 0x400000>; + interrupts = <0 5 0x04>, < 0 6 0x04>; + clocks = <&clks 130>, <&clks 131>, <&clks 132>, <&clks 39>, <&clks 40>, <&clks 169>; + clock-names = "ipu1", "ipu1_di0", "ipu1_di1", "ipu1_di0_sel", "ipu1_di1_sel", "ipu1_csi0"; + status = "disabled"; + }; + +Examples for v4l2_capture: + v4l2_cap { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <0>; + mclk_source = <0>; + status = "okay"; + }; + +Examples for sensors: + ov5642: ov5642@3c { + compatible = "ovti,ov5642"; + reg = <0x3c>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_2>; + clocks = <&clks 201>; + clock-names = "csi_mclk"; + DOVDD-supply = <&vgen4_reg>; /* 1.8v */ + AVDD-supply = <&vgen3_reg>; /* 2.8v, on rev C board is VGEN3 */ + DVDD-supply = <&vgen2_reg>; /* 1.5v*/ + pwn-gpios = <&gpio1 16 1>; /* active low: SD1_DAT0 */ + rst-gpios = <&gpio1 17 0>; /* active high: SD1_DAT1 */ + csi_id = <0>; + mclk = <24000000>; + mclk_source = <0>; + }; + + adv7180: adv7180@21 { + compatible = "adv,adv7180"; + reg = <0x21>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_3>; + clocks = <&clks 201>; + clock-names = "csi_mclk"; + DOVDD-supply = <®_3p3v>; /* 3.3v, enabled via 2.8 VGEN6 */ + AVDD-supply = <®_3p3v>; /* 1.8v */ + DVDD-supply = <®_3p3v>; /* 1.8v */ + PVDD-supply = <®_3p3v>; /* 1.8v */ + pwn-gpios = <&max7310_b 2 0>; + csi_id = <0>; + mclk = <24000000>; + mclk_source = <0>; + cvbs = <1>; + }; diff -Nur linux-3.10.30/Documentation/devicetree/bindings/video/mxc_hdmi_video.txt linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/mxc_hdmi_video.txt --- linux-3.10.30/Documentation/devicetree/bindings/video/mxc_hdmi_video.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/devicetree/bindings/video/mxc_hdmi_video.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,20 @@ +Device-Tree bindings for hdmi video driver + +Required properties: +- compatible: value should be "fsl,imx6q-hdmi-video". +- fsl,hdcp: define the property in dts, hdmi driver will initalize for hdcp, + otherwise hdcp function will not supported. +- fsl,phy_reg_vlev: hdmi phy register,Voltage Level Control Register offset 0x0e, + adjust hdmi phy signal voltage level. +- fsl,phy_reg_cksymtx: hdmi phy register, clock symbol and transmitter control + register offset 0x09, adjust hdmi signal pre-emphasis. + +Example: + + hdmi_video { + compatible = "fsl,imx6q-hdmi-video"; + fsl,hdcp; + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + }; + diff -Nur linux-3.10.30/Documentation/driver-model/devres.txt linux-3.10.30-cubox-i/Documentation/driver-model/devres.txt --- linux-3.10.30/Documentation/driver-model/devres.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/driver-model/devres.txt 2014-03-08 20:32:52.000000000 +0100 @@ -293,3 +293,6 @@ PHY devm_usb_get_phy() devm_usb_put_phy() + +SPI + devm_spi_register_master() diff -Nur linux-3.10.30/Documentation/kernel-parameters.txt linux-3.10.30-cubox-i/Documentation/kernel-parameters.txt --- linux-3.10.30/Documentation/kernel-parameters.txt 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/kernel-parameters.txt 2014-03-08 20:32:52.000000000 +0100 @@ -1240,6 +1240,15 @@ See comment before ip2_setup() in drivers/char/ip2/ip2base.c. + irqaffinity= [SMP] Set the default irq affinity mask + Format: + ,..., + or + - + (must be a positive range in ascending order) + or a mixture + ,...,- + irqfixup [HW] When an interrupt is not handled search all handlers for it. Intended to get systems with badly broken @@ -3345,6 +3354,21 @@ that this also can be controlled per-workqueue for workqueues visible under /sys/bus/workqueue/. + workqueue.power_efficient + Per-cpu workqueues are generally preferred because + they show better performance thanks to cache + locality; unfortunately, per-cpu workqueues tend to + be more power hungry than unbound workqueues. + + Enabling this makes the per-cpu workqueues which + were observed to contribute significantly to power + consumption unbound, leading to measurably lower + power usage at the cost of small performance + overhead. + + The default value of this parameter is determined by + the config option CONFIG_WQ_POWER_EFFICIENT_DEFAULT. + x2apic_phys [X86-64,APIC] Use x2apic physical mode instead of default x2apic cluster mode on platforms supporting x2apic. diff -Nur linux-3.10.30/Documentation/vm/zswap.txt linux-3.10.30-cubox-i/Documentation/vm/zswap.txt --- linux-3.10.30/Documentation/vm/zswap.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/Documentation/vm/zswap.txt 2014-03-08 20:32:52.000000000 +0100 @@ -0,0 +1,72 @@ +Overview: + +Zswap is a lightweight compressed cache for swap pages. It takes pages that are +in the process of being swapped out and attempts to compress them into a +dynamically allocated RAM-based memory pool. If this process is successful, +the writeback to the swap device is deferred and, in many cases, avoided +completely. This results in a significant I/O reduction and performance gains +for systems that are swapping. + +Zswap provides compressed swap caching that basically trades CPU cycles for +reduced swap I/O. This trade-off can result in a significant performance +improvement as reads to/writes from to the compressed cache almost always +faster that reading from a swap device which incurs the latency of an +asynchronous block I/O read. + +Some potential benefits: +* Desktop/laptop users with limited RAM capacities can mitigate the + performance impact of swapping. +* Overcommitted guests that share a common I/O resource can + dramatically reduce their swap I/O pressure, avoiding heavy handed I/O + throttling by the hypervisor. This allows more work to get done with less + impact to the guest workload and guests sharing the I/O subsystem +* Users with SSDs as swap devices can extend the life of the device by + drastically reducing life-shortening writes. + +Zswap evicts pages from compressed cache on an LRU basis to the backing swap +device when the compressed pool reaches it size limit. This requirement had +been identified in prior community discussions. + +To enabled zswap, the "enabled" attribute must be set to 1 at boot time. e.g. +zswap.enabled=1 + +Design: + +Zswap receives pages for compression through the Frontswap API and is able to +evict pages from its own compressed pool on an LRU basis and write them back to +the backing swap device in the case that the compressed pool is full. + +Zswap makes use of zbud for the managing the compressed memory pool. Each +allocation in zbud is not directly accessible by address. Rather, a handle is +return by the allocation routine and that handle must be mapped before being +accessed. The compressed memory pool grows on demand and shrinks as compressed +pages are freed. The pool is not preallocated. + +When a swap page is passed from frontswap to zswap, zswap maintains a mapping +of the swap entry, a combination of the swap type and swap offset, to the zbud +handle that references that compressed swap page. This mapping is achieved +with a red-black tree per swap type. The swap offset is the search key for the +tree nodes. + +During a page fault on a PTE that is a swap entry, frontswap calls the zswap +load function to decompress the page into the page allocated by the page fault +handler. + +Once there are no PTEs referencing a swap page stored in zswap (i.e. the count +in the swap_map goes to 0) the swap code calls the zswap invalidate function, +via frontswap, to free the compressed entry. + +Zswap seeks to be simple in its policies. Sysfs attributes allow for two user +controlled policies: +* max_compression_ratio - Maximum compression ratio, as as percentage, + for an acceptable compressed page. Any page that does not compress by at + least this ratio will be rejected. +* max_pool_percent - The maximum percentage of memory that the compressed + pool can occupy. + +Zswap allows the compressor to be selected at kernel boot time by setting the +“compressor” attribute. The default compressor is lzo. e.g. +zswap.compressor=deflate + +A debugfs interface is provided for various statistic about pool size, number +of pages stored, and various counters for the reasons pages are rejected. diff -Nur linux-3.10.30/arch/arm/Kconfig linux-3.10.30-cubox-i/arch/arm/Kconfig --- linux-3.10.30/arch/arm/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/Kconfig 2014-03-08 21:32:22.000000000 +0100 @@ -1494,6 +1494,109 @@ MultiThreading at a cost of slightly increased overhead in some places. If unsure say N here. +config DISABLE_CPU_SCHED_DOMAIN_BALANCE + bool "(EXPERIMENTAL) Disable CPU level scheduler load-balancing" + help + Disables scheduler load-balancing at CPU sched domain level. + +config SCHED_HMP + bool "(EXPERIMENTAL) Heterogenous multiprocessor scheduling" + depends on DISABLE_CPU_SCHED_DOMAIN_BALANCE && SCHED_MC && FAIR_GROUP_SCHED && !SCHED_AUTOGROUP + help + Experimental scheduler optimizations for heterogeneous platforms. + Attempts to introspectively select task affinity to optimize power + and performance. Basic support for multiple (>2) cpu types is in place, + but it has only been tested with two types of cpus. + There is currently no support for migration of task groups, hence + !SCHED_AUTOGROUP. Furthermore, normal load-balancing must be disabled + between cpus of different type (DISABLE_CPU_SCHED_DOMAIN_BALANCE). + When turned on, this option adds sys/kernel/hmp directory which + contains the following files: + up_threshold - the load average threshold used for up migration + (0 - 1023) + down_threshold - the load average threshold used for down migration + (0 - 1023) + hmp_domains - a list of cpumasks for the present HMP domains, + starting with the 'biggest' and ending with the + 'smallest'. + Note that both the threshold files can be written at runtime to + control scheduler behaviour. + +config SCHED_HMP_PRIO_FILTER + bool "(EXPERIMENTAL) Filter HMP migrations by task priority" + depends on SCHED_HMP + help + Enables task priority based HMP migration filter. Any task with + a NICE value above the threshold will always be on low-power cpus + with less compute capacity. + +config SCHED_HMP_PRIO_FILTER_VAL + int "NICE priority threshold" + default 5 + depends on SCHED_HMP_PRIO_FILTER + +config HMP_FAST_CPU_MASK + string "HMP scheduler fast CPU mask" + depends on SCHED_HMP + help + Leave empty to use device tree information. + Specify the cpuids of the fast CPUs in the system as a list string, + e.g. cpuid 0+1 should be specified as 0-1. + +config HMP_SLOW_CPU_MASK + string "HMP scheduler slow CPU mask" + depends on SCHED_HMP + help + Leave empty to use device tree information. + Specify the cpuids of the slow CPUs in the system as a list string, + e.g. cpuid 0+1 should be specified as 0-1. + +config HMP_VARIABLE_SCALE + bool "Allows changing the load tracking scale through sysfs" + depends on SCHED_HMP + help + When turned on, this option exports the load average period value + for the load tracking patches through sysfs. + The values can be modified to change the rate of load accumulation + used for HMP migration. 'load_avg_period_ms' is the time in ms to + reach a load average of 0.5 for an idle task of 0 load average + ratio which becomes 100% busy. + For example, with load_avg_period_ms = 128 and up_threshold = 512, + a running task with a load of 0 will be migrated to a bigger CPU after + 128ms, because after 128ms its load_avg_ratio is 0.5 and the real + up_threshold is 0.5. + This patch has the same behavior as changing the Y of the load + average computation to + (1002/1024)^(LOAD_AVG_PERIOD/load_avg_period_ms) + but removes intermediate overflows in computation. + +config HMP_FREQUENCY_INVARIANT_SCALE + bool "(EXPERIMENTAL) Frequency-Invariant Tracked Load for HMP" + depends on SCHED_HMP && CPU_FREQ + help + Scales the current load contribution in line with the frequency + of the CPU that the task was executed on. + In this version, we use a simple linear scale derived from the + maximum frequency reported by CPUFreq. + Restricting tracked load to be scaled by the CPU's frequency + represents the consumption of possible compute capacity + (rather than consumption of actual instantaneous capacity as + normal) and allows the HMP migration's simple threshold + migration strategy to interact more predictably with CPUFreq's + asynchronous compute capacity changes. + +config SCHED_HMP_LITTLE_PACKING + bool "Small task packing for HMP" + depends on SCHED_HMP + default n + help + Allows the HMP Scheduler to pack small tasks into CPUs in the + smallest HMP domain. + Controlled by two sysfs files in sys/kernel/hmp. + packing_enable: 1 to enable, 0 to disable packing. Default 1. + packing_limit: runqueue load ratio where a RQ is considered + to be full. Default is NICE_0_LOAD * 9/8. + config HAVE_ARM_SCU bool help @@ -1521,6 +1624,31 @@ for (multi-)cluster based systems, such as big.LITTLE based systems. +config BIG_LITTLE + bool "big.LITTLE support (Experimental)" + depends on CPU_V7 && SMP + select MCPM + help + This option enables support for the big.LITTLE architecture. + +config BL_SWITCHER + bool "big.LITTLE switcher support" + depends on BIG_LITTLE && MCPM && HOTPLUG_CPU + select CPU_PM + select ARM_CPU_SUSPEND + help + The big.LITTLE "switcher" provides the core functionality to + transparently handle transition between a cluster of A15's + and a cluster of A7's in a big.LITTLE system. + +config BL_SWITCHER_DUMMY_IF + tristate "Simple big.LITTLE switcher user interface" + depends on BL_SWITCHER && DEBUG_KERNEL + help + This is a simple and dummy char dev interface to control + the big.LITTLE switcher core code. It is meant for + debugging purposes only. + choice prompt "Memory split" default VMSPLIT_3G @@ -1738,6 +1866,7 @@ range 11 64 if ARCH_SHMOBILE default "12" if SOC_AM33XX default "9" if SA1111 + default "14" if ARCH_MXC default "11" help The kernel memory allocator divides physically contiguous memory diff -Nur linux-3.10.30/arch/arm/Kconfig.debug linux-3.10.30-cubox-i/arch/arm/Kconfig.debug --- linux-3.10.30/arch/arm/Kconfig.debug 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/Kconfig.debug 2014-03-08 20:32:53.000000000 +0100 @@ -251,6 +251,13 @@ Say Y here if you want kernel low-level debugging support on i.MX6Q/DL. + config DEBUG_IMX6SL_UART + bool "i.MX6SL Debug UART" + depends on SOC_IMX6SL + help + Say Y here if you want kernel low-level debugging support + on i.MX6SL. + config DEBUG_MMP_UART2 bool "Kernel low-level debugging message via MMP UART2" depends on ARCH_MMP @@ -309,6 +316,13 @@ Say Y here if you want kernel low-level debugging support on MVEBU based platforms. + config DEBUG_VF_UART + bool "Vybrid UART" + depends on SOC_VF610 + help + Say Y here if you want kernel low-level debugging support + on Vybrid based platforms. + config DEBUG_NOMADIK_UART bool "Kernel low-level debugging messages via NOMADIK UART" depends on ARCH_NOMADIK @@ -532,7 +546,8 @@ DEBUG_IMX35_UART || \ DEBUG_IMX51_UART || \ DEBUG_IMX53_UART || \ - DEBUG_IMX6Q_UART + DEBUG_IMX6Q_UART || \ + DEBUG_IMX6SL_UART default 1 depends on ARCH_MXC help @@ -631,7 +646,8 @@ DEBUG_IMX35_UART || \ DEBUG_IMX51_UART || \ DEBUG_IMX53_UART ||\ - DEBUG_IMX6Q_UART + DEBUG_IMX6Q_UART || \ + DEBUG_IMX6SL_UART default "debug/mvebu.S" if DEBUG_MVEBU_UART default "debug/mxs.S" if DEBUG_IMX23_UART || DEBUG_IMX28_UART default "debug/nomadik.S" if DEBUG_NOMADIK_UART @@ -646,6 +662,7 @@ default "debug/ux500.S" if DEBUG_UX500_UART default "debug/vexpress.S" if DEBUG_VEXPRESS_UART0_DETECT || \ DEBUG_VEXPRESS_UART0_CA9 || DEBUG_VEXPRESS_UART0_RS1 + default "debug/vf.S" if DEBUG_VF_UART default "debug/vt8500.S" if DEBUG_VT8500_UART0 default "debug/zynq.S" if DEBUG_ZYNQ_UART0 || DEBUG_ZYNQ_UART1 default "mach/debug-macro.S" diff -Nur linux-3.10.30/arch/arm/boot/dts/Makefile linux-3.10.30-cubox-i/arch/arm/boot/dts/Makefile --- linux-3.10.30/arch/arm/boot/dts/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/Makefile 2014-03-08 20:32:53.000000000 +0100 @@ -113,14 +113,31 @@ imx53-mba53.dtb \ imx53-qsb.dtb \ imx53-smd.dtb \ + imx6dl-cubox-i.dtb \ + imx6dl-hummingboard.dtb \ imx6dl-sabreauto.dtb \ + imx6dl-sabreauto-ecspi.dtb \ + imx6dl-sabreauto-flexcan1.dtb \ + imx6dl-sabreauto-gpmi-weim.dtb \ imx6dl-sabresd.dtb \ + imx6dl-sabresd-hdcp.dtb \ + imx6dl-sabresd-ldo.dtb \ imx6dl-wandboard.dtb \ imx6q-arm2.dtb \ + imx6q-cubox-i.dtb \ imx6q-sabreauto.dtb \ + imx6q-sabreauto-ecspi.dtb \ + imx6q-sabreauto-flexcan1.dtb \ + imx6q-sabreauto-gpmi-weim.dtb \ imx6q-sabrelite.dtb \ imx6q-sabresd.dtb \ - imx6q-sbc6x.dtb + imx6q-sabresd-hdcp.dtb \ + imx6q-sabresd-ldo.dtb \ + imx6q-sbc6x.dtb \ + imx6sl-evk.dtb \ + imx6sl-evk-csi.dtb \ + imx6sl-evk-ldo.dtb \ + vf610-twr.dtb dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \ imx23-olinuxino.dtb \ imx23-stmp378x_devb.dtb \ @@ -202,7 +219,14 @@ dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \ vexpress-v2p-ca9.dtb \ vexpress-v2p-ca15-tc1.dtb \ - vexpress-v2p-ca15_a7.dtb + vexpress-v2p-ca15_a7.dtb \ + rtsm_ve-cortex_a9x2.dtb \ + rtsm_ve-cortex_a9x4.dtb \ + rtsm_ve-cortex_a15x1.dtb \ + rtsm_ve-cortex_a15x2.dtb \ + rtsm_ve-cortex_a15x4.dtb \ + rtsm_ve-v2p-ca15x1-ca7x1.dtb \ + rtsm_ve-v2p-ca15x4-ca7x4.dtb dtb-$(CONFIG_ARCH_VIRT) += xenvm-4.2.dtb dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \ wm8505-ref.dtb \ diff -Nur linux-3.10.30/arch/arm/boot/dts/clcd-panels.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/clcd-panels.dtsi --- linux-3.10.30/arch/arm/boot/dts/clcd-panels.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/clcd-panels.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,52 @@ +/* + * ARM Ltd. Versatile Express + * + */ + +/ { + panels { + panel@0 { + compatible = "panel"; + mode = "VGA"; + refresh = <60>; + xres = <640>; + yres = <480>; + pixclock = <39721>; + left_margin = <40>; + right_margin = <24>; + upper_margin = <32>; + lower_margin = <11>; + hsync_len = <96>; + vsync_len = <2>; + sync = <0>; + vmode = "FB_VMODE_NONINTERLACED"; + + tim2 = "TIM2_BCD", "TIM2_IPC"; + cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)"; + caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888"; + bpp = <16>; + }; + + panel@1 { + compatible = "panel"; + mode = "XVGA"; + refresh = <60>; + xres = <1024>; + yres = <768>; + pixclock = <15748>; + left_margin = <152>; + right_margin = <48>; + upper_margin = <23>; + lower_margin = <3>; + hsync_len = <104>; + vsync_len = <4>; + sync = <0>; + vmode = "FB_VMODE_NONINTERLACED"; + + tim2 = "TIM2_BCD", "TIM2_IPC"; + cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)"; + caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888"; + bpp = <16>; + }; + }; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/exynos5440-ssdk5440.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/exynos5440-ssdk5440.dts --- linux-3.10.30/arch/arm/boot/dts/exynos5440-ssdk5440.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/exynos5440-ssdk5440.dts 2014-03-08 20:32:53.000000000 +0100 @@ -30,4 +30,12 @@ clock-frequency = <50000000>; }; }; + + pcie@290000 { + reset-gpio = <&pin_ctrl 5 0>; + }; + + pcie@2a0000 { + reset-gpio = <&pin_ctrl 22 0>; + }; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/exynos5440.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/exynos5440.dtsi --- linux-3.10.30/arch/arm/boot/dts/exynos5440.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/exynos5440.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -113,7 +113,7 @@ clock-names = "spi", "spi_busclk0"; }; - pinctrl { + pin_ctrl: pinctrl { compatible = "samsung,exynos5440-pinctrl"; reg = <0xE0000 0x1000>; interrupts = <0 37 0>, <0 38 0>, <0 39 0>, <0 40 0>, @@ -216,4 +216,44 @@ clock-names = "rtc"; status = "disabled"; }; + + pcie@290000 { + compatible = "samsung,exynos5440-pcie", "snps,dw-pcie"; + reg = <0x290000 0x1000 + 0x270000 0x1000 + 0x271000 0x40>; + interrupts = <0 20 0>, <0 21 0>, <0 22 0>; + clocks = <&clock 28>, <&clock 27>; + clock-names = "pcie", "pcie_bus"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x00000800 0 0x40000000 0x40000000 0 0x00001000 /* configuration space */ + 0x81000000 0 0 0x40001000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0x0 0 &gic 53>; + num-lanes = <4>; + }; + + pcie@2a0000 { + compatible = "samsung,exynos5440-pcie", "snps,dw-pcie"; + reg = <0x2a0000 0x1000 + 0x272000 0x1000 + 0x271040 0x40>; + interrupts = <0 23 0>, <0 24 0>, <0 25 0>; + clocks = <&clock 29>, <&clock 27>; + clock-names = "pcie", "pcie_bus"; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x00000800 0 0x60000000 0x60000000 0 0x00001000 /* configuration space */ + 0x81000000 0 0 0x60001000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0x0 0 &gic 56>; + num-lanes = <4>; + }; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx23.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx23.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx23.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx23.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -20,6 +20,7 @@ gpio2 = &gpio2; serial0 = &auart0; serial1 = &auart1; + usbphy0 = &usbphy0; }; cpus { @@ -360,7 +361,8 @@ compatible = "fsl,imx23-lcdif"; reg = <0x80030000 2000>; interrupts = <46 45>; - clocks = <&clks 38>; + clocks = <&clks 38>, <&clks 38>; + clock-names = "pix", "axi"; status = "disabled"; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx28.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx28.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx28.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx28.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -29,6 +29,8 @@ serial4 = &auart4; ethernet0 = &mac0; ethernet1 = &mac1; + usbphy0 = &usbphy0; + usbphy1 = &usbphy1; }; cpus { @@ -727,7 +729,8 @@ compatible = "fsl,imx28-lcdif"; reg = <0x80030000 0x2000>; interrupts = <38 86>; - clocks = <&clks 55>; + clocks = <&clks 55>, <&clks 55>; + clock-names = "pix", "axi"; dmas = <&dma_apbh 13>; dma-names = "rx"; status = "disabled"; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx53.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx53.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx53.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx53.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -782,5 +782,10 @@ status = "disabled"; }; }; + + ocram: sram@f8000000 { + compatible = "mmio-sram"; + reg = <0xf8000000 0x20000>; + }; }; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-cubox-i.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-cubox-i.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-cubox-i.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-cubox-i.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2014 Russell King + */ +/dts-v1/; + +#include "imx6dl.dtsi" +#include "imx6qdl-cubox-i.dtsi" + +/ { + model = "SolidRun Cubox-i Solo/DualLite"; + compatible = "solidrun,cubox-i/dl", "fsl,imx6dl"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-hummingboard.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-hummingboard.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-hummingboard.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-hummingboard.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2013 Russell King + * + * The code contained herein is licensed under the GNU General Public + * License version 2. + */ +/dts-v1/; + +#include "imx6dl.dtsi" +#include "imx6qdl-microsom.dtsi" +#include "imx6qdl-microsom-ar8035.dtsi" + +/ { + model = "SolidRun HummingBoard DL/Solo"; + compatible = "solidrun,hummingboard", "fsl,imx6dl"; + + aliases { + mxcfb0 = &mxcfb1; + }; + + ir_recv: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio1 2 1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_gpio1_2>; + }; + + regulators { + compatible = "simple-bus"; + + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + reg_usbh1_vbus: usb-h1-vbus { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 0 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_usbh1_vbus>; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_usbotg_vbus: usb-otg-vbus { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio3 22 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_usbotg_vbus>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + }; + + codec: spdif-transmitter { + compatible = "linux,spdif-dit"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_spdif>; + }; + + imx-drm { + compatible = "fsl,imx-drm"; + crtcs = <&ipu1 0>, <&ipu1 1>; + connectors = <&hdmi>; + }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "imx-spdif"; + /* IMX6 doesn't implement this yet */ + spdif-controller = <&spdif>; + spdif-out; + }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + mxcfb1: mxc_sdc_fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1280x720@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; + + v4l2_cap_0 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <0>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_cap_1 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <1>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; +}; + +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_flexcan1>; + status = "okay"; +}; + +&hdmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_hdmi>; + ddc = <&i2c2>; + status = "okay"; + crtcs = <&ipu1 0>; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_cec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_hdmi>; + status = "okay"; +}; + +&hdmi_core { + ipu_id = <1>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + fsl,hdcp; + status = "okay"; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1_1>; + + /* + * Not fitted on Carrier-1 board... yet + status = "okay"; + + rtc: pcf8523@68 { + compatible = "nxp,pcf8523"; + reg = <0x68>; + }; + */ +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_2>; + status = "okay"; +}; + +&iomuxc { + hummingboard { + pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 { + fsl,pins = < + MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x80000000 + MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x80000000 + >; + }; + + pinctrl_hummingboard_gpio1_2: hummingboard-gpio1_2 { + fsl,pins = < + MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 + >; + }; + + pinctrl_hummingboard_hdmi: hummingboard-hdmi { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + + pinctrl_hummingboard_spdif: hummingboard-spdif { + fsl,pins = ; + }; + + pinctrl_hummingboard_usbh1_vbus: hummingboard-usbh1-vbus { + fsl,pins = ; + }; + + pinctrl_hummingboard_usbotg_vbus: hummingboard-usbotg-vbus { + fsl,pins = ; + }; + + pinctrl_hummingboard_usdhc2_aux: hummingboard-usdhc2-aux { + fsl,pins = < + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1f071 + >; + }; + + pinctrl_hummingboard_usdhc2: hummingboard-usdhc2 { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 + >; + }; + }; +}; + +&spdif { + status = "okay"; +}; + +&usbh1 { + vbus-supply = <®_usbh1_vbus>; + status = "okay"; +}; + +&usbotg { + vbus-supply = <®_usbotg_vbus>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default"; + pinctrl-0 = < + &pinctrl_hummingboard_usdhc2_aux + &pinctrl_hummingboard_usdhc2 + >; + vmmc-supply = <®_3p3v>; + cd-gpios = <&gpio1 4 0>; + status = "okay"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-pinfunc.h linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-pinfunc.h --- linux-3.10.30/arch/arm/boot/dts/imx6dl-pinfunc.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-pinfunc.h 2014-03-08 20:32:53.000000000 +0100 @@ -14,1072 +14,1076 @@ * The pin function ID is a tuple of * */ -#define MX6DL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10 0x04c 0x360 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT10__AUD3_RXC 0x04c 0x360 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT10__ECSPI2_MISO 0x04c 0x360 0x7f8 0x2 0x0 -#define MX6DL_PAD_CSI0_DAT10__UART1_TX_DATA 0x04c 0x360 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT10__UART1_RX_DATA 0x04c 0x360 0x8fc 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT10__GPIO5_IO28 0x04c 0x360 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT10__ARM_TRACE07 0x04c 0x360 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11 0x050 0x364 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT11__AUD3_RXFS 0x050 0x364 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT11__ECSPI2_SS0 0x050 0x364 0x800 0x2 0x0 -#define MX6DL_PAD_CSI0_DAT11__UART1_RX_DATA 0x050 0x364 0x8fc 0x3 0x1 -#define MX6DL_PAD_CSI0_DAT11__UART1_TX_DATA 0x050 0x364 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT11__GPIO5_IO29 0x050 0x364 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT11__ARM_TRACE08 0x050 0x364 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x054 0x368 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT12__EIM_DATA08 0x054 0x368 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT12__UART4_TX_DATA 0x054 0x368 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT12__UART4_RX_DATA 0x054 0x368 0x914 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT12__GPIO5_IO30 0x054 0x368 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT12__ARM_TRACE09 0x054 0x368 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x058 0x36c 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT13__EIM_DATA09 0x058 0x36c 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT13__UART4_RX_DATA 0x058 0x36c 0x914 0x3 0x1 -#define MX6DL_PAD_CSI0_DAT13__UART4_TX_DATA 0x058 0x36c 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT13__GPIO5_IO31 0x058 0x36c 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT13__ARM_TRACE10 0x058 0x36c 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x05c 0x370 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT14__EIM_DATA10 0x05c 0x370 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT14__UART5_TX_DATA 0x05c 0x370 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT14__UART5_RX_DATA 0x05c 0x370 0x91c 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT14__GPIO6_IO00 0x05c 0x370 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT14__ARM_TRACE11 0x05c 0x370 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x060 0x374 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT15__EIM_DATA11 0x060 0x374 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT15__UART5_RX_DATA 0x060 0x374 0x91c 0x3 0x1 -#define MX6DL_PAD_CSI0_DAT15__UART5_TX_DATA 0x060 0x374 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT15__GPIO6_IO01 0x060 0x374 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT15__ARM_TRACE12 0x060 0x374 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x064 0x378 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT16__EIM_DATA12 0x064 0x378 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT16__UART4_RTS_B 0x064 0x378 0x910 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT16__UART4_CTS_B 0x064 0x378 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT16__GPIO6_IO02 0x064 0x378 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT16__ARM_TRACE13 0x064 0x378 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x068 0x37c 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT17__EIM_DATA13 0x068 0x37c 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT17__UART4_CTS_B 0x068 0x37c 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT17__UART4_RTS_B 0x068 0x37c 0x910 0x3 0x1 -#define MX6DL_PAD_CSI0_DAT17__GPIO6_IO03 0x068 0x37c 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT17__ARM_TRACE14 0x068 0x37c 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x06c 0x380 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT18__EIM_DATA14 0x06c 0x380 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT18__UART5_RTS_B 0x06c 0x380 0x918 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT18__UART5_CTS_B 0x06c 0x380 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT18__GPIO6_IO04 0x06c 0x380 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT18__ARM_TRACE15 0x06c 0x380 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x070 0x384 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT19__EIM_DATA15 0x070 0x384 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT19__UART5_CTS_B 0x070 0x384 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT19__UART5_RTS_B 0x070 0x384 0x918 0x3 0x1 -#define MX6DL_PAD_CSI0_DAT19__GPIO6_IO05 0x070 0x384 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04 0x074 0x388 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT4__EIM_DATA02 0x074 0x388 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT4__ECSPI1_SCLK 0x074 0x388 0x7d8 0x2 0x0 -#define MX6DL_PAD_CSI0_DAT4__KEY_COL5 0x074 0x388 0x8c0 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT4__AUD3_TXC 0x074 0x388 0x000 0x4 0x0 -#define MX6DL_PAD_CSI0_DAT4__GPIO5_IO22 0x074 0x388 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT4__ARM_TRACE01 0x074 0x388 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05 0x078 0x38c 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT5__EIM_DATA03 0x078 0x38c 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT5__ECSPI1_MOSI 0x078 0x38c 0x7e0 0x2 0x0 -#define MX6DL_PAD_CSI0_DAT5__KEY_ROW5 0x078 0x38c 0x8cc 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT5__AUD3_TXD 0x078 0x38c 0x000 0x4 0x0 -#define MX6DL_PAD_CSI0_DAT5__GPIO5_IO23 0x078 0x38c 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT5__ARM_TRACE02 0x078 0x38c 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06 0x07c 0x390 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT6__EIM_DATA04 0x07c 0x390 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT6__ECSPI1_MISO 0x07c 0x390 0x7dc 0x2 0x0 -#define MX6DL_PAD_CSI0_DAT6__KEY_COL6 0x07c 0x390 0x8c4 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT6__AUD3_TXFS 0x07c 0x390 0x000 0x4 0x0 -#define MX6DL_PAD_CSI0_DAT6__GPIO5_IO24 0x07c 0x390 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT6__ARM_TRACE03 0x07c 0x390 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07 0x080 0x394 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT7__EIM_DATA05 0x080 0x394 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT7__ECSPI1_SS0 0x080 0x394 0x7e4 0x2 0x0 -#define MX6DL_PAD_CSI0_DAT7__KEY_ROW6 0x080 0x394 0x8d0 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT7__AUD3_RXD 0x080 0x394 0x000 0x4 0x0 -#define MX6DL_PAD_CSI0_DAT7__GPIO5_IO25 0x080 0x394 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT7__ARM_TRACE04 0x080 0x394 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08 0x084 0x398 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT8__EIM_DATA06 0x084 0x398 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT8__ECSPI2_SCLK 0x084 0x398 0x7f4 0x2 0x0 -#define MX6DL_PAD_CSI0_DAT8__KEY_COL7 0x084 0x398 0x8c8 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT8__I2C1_SDA 0x084 0x398 0x86c 0x4 0x0 -#define MX6DL_PAD_CSI0_DAT8__GPIO5_IO26 0x084 0x398 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT8__ARM_TRACE05 0x084 0x398 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09 0x088 0x39c 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DAT9__EIM_DATA07 0x088 0x39c 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DAT9__ECSPI2_MOSI 0x088 0x39c 0x7fc 0x2 0x0 -#define MX6DL_PAD_CSI0_DAT9__KEY_ROW7 0x088 0x39c 0x8d4 0x3 0x0 -#define MX6DL_PAD_CSI0_DAT9__I2C1_SCL 0x088 0x39c 0x868 0x4 0x0 -#define MX6DL_PAD_CSI0_DAT9__GPIO5_IO27 0x088 0x39c 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DAT9__ARM_TRACE06 0x088 0x39c 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x08c 0x3a0 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_DATA_EN__EIM_DATA00 0x08c 0x3a0 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x08c 0x3a0 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_DATA_EN__ARM_TRACE_CLK 0x08c 0x3a0 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x090 0x3a4 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_MCLK__CCM_CLKO1 0x090 0x3a4 0x000 0x3 0x0 -#define MX6DL_PAD_CSI0_MCLK__GPIO5_IO19 0x090 0x3a4 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_MCLK__ARM_TRACE_CTL 0x090 0x3a4 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x094 0x3a8 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_PIXCLK__GPIO5_IO18 0x094 0x3a8 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_PIXCLK__ARM_EVENTO 0x094 0x3a8 0x000 0x7 0x0 -#define MX6DL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x098 0x3ac 0x000 0x0 0x0 -#define MX6DL_PAD_CSI0_VSYNC__EIM_DATA01 0x098 0x3ac 0x000 0x1 0x0 -#define MX6DL_PAD_CSI0_VSYNC__GPIO5_IO21 0x098 0x3ac 0x000 0x5 0x0 -#define MX6DL_PAD_CSI0_VSYNC__ARM_TRACE00 0x098 0x3ac 0x000 0x7 0x0 -#define MX6DL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x09c 0x3b0 0x000 0x0 0x0 -#define MX6DL_PAD_DI0_DISP_CLK__LCD_CLK 0x09c 0x3b0 0x000 0x1 0x0 -#define MX6DL_PAD_DI0_DISP_CLK__GPIO4_IO16 0x09c 0x3b0 0x000 0x5 0x0 -#define MX6DL_PAD_DI0_DISP_CLK__LCD_WR_RWN 0x09c 0x3b0 0x000 0x8 0x0 -#define MX6DL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x0a0 0x3b4 0x000 0x0 0x0 -#define MX6DL_PAD_DI0_PIN15__LCD_ENABLE 0x0a0 0x3b4 0x000 0x1 0x0 -#define MX6DL_PAD_DI0_PIN15__AUD6_TXC 0x0a0 0x3b4 0x000 0x2 0x0 -#define MX6DL_PAD_DI0_PIN15__GPIO4_IO17 0x0a0 0x3b4 0x000 0x5 0x0 -#define MX6DL_PAD_DI0_PIN15__LCD_RD_E 0x0a0 0x3b4 0x000 0x8 0x0 -#define MX6DL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x0a4 0x3b8 0x000 0x0 0x0 -#define MX6DL_PAD_DI0_PIN2__LCD_HSYNC 0x0a4 0x3b8 0x8d8 0x1 0x0 -#define MX6DL_PAD_DI0_PIN2__AUD6_TXD 0x0a4 0x3b8 0x000 0x2 0x0 -#define MX6DL_PAD_DI0_PIN2__GPIO4_IO18 0x0a4 0x3b8 0x000 0x5 0x0 -#define MX6DL_PAD_DI0_PIN2__LCD_RS 0x0a4 0x3b8 0x000 0x8 0x0 -#define MX6DL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x0a8 0x3bc 0x000 0x0 0x0 -#define MX6DL_PAD_DI0_PIN3__LCD_VSYNC 0x0a8 0x3bc 0x000 0x1 0x0 -#define MX6DL_PAD_DI0_PIN3__AUD6_TXFS 0x0a8 0x3bc 0x000 0x2 0x0 -#define MX6DL_PAD_DI0_PIN3__GPIO4_IO19 0x0a8 0x3bc 0x000 0x5 0x0 -#define MX6DL_PAD_DI0_PIN3__LCD_CS 0x0a8 0x3bc 0x000 0x8 0x0 -#define MX6DL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x0ac 0x3c0 0x000 0x0 0x0 -#define MX6DL_PAD_DI0_PIN4__LCD_BUSY 0x0ac 0x3c0 0x8d8 0x1 0x1 -#define MX6DL_PAD_DI0_PIN4__AUD6_RXD 0x0ac 0x3c0 0x000 0x2 0x0 -#define MX6DL_PAD_DI0_PIN4__SD1_WP 0x0ac 0x3c0 0x92c 0x3 0x0 -#define MX6DL_PAD_DI0_PIN4__GPIO4_IO20 0x0ac 0x3c0 0x000 0x5 0x0 -#define MX6DL_PAD_DI0_PIN4__LCD_RESET 0x0ac 0x3c0 0x000 0x8 0x0 -#define MX6DL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x0b0 0x3c4 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT0__LCD_DATA00 0x0b0 0x3c4 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x0b0 0x3c4 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT0__GPIO4_IO21 0x0b0 0x3c4 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x0b4 0x3c8 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT1__LCD_DATA01 0x0b4 0x3c8 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x0b4 0x3c8 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT1__GPIO4_IO22 0x0b4 0x3c8 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x0b8 0x3cc 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT10__LCD_DATA10 0x0b8 0x3cc 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT10__GPIO4_IO31 0x0b8 0x3cc 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x0bc 0x3d0 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT11__LCD_DATA11 0x0bc 0x3d0 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT11__GPIO5_IO05 0x0bc 0x3d0 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x0c0 0x3d4 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT12__LCD_DATA12 0x0c0 0x3d4 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT12__GPIO5_IO06 0x0c0 0x3d4 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x0c4 0x3d8 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT13__LCD_DATA13 0x0c4 0x3d8 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT13__AUD5_RXFS 0x0c4 0x3d8 0x7bc 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT13__GPIO5_IO07 0x0c4 0x3d8 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x0c8 0x3dc 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT14__LCD_DATA14 0x0c8 0x3dc 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT14__AUD5_RXC 0x0c8 0x3dc 0x7b8 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT14__GPIO5_IO08 0x0c8 0x3dc 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x0cc 0x3e0 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT15__LCD_DATA15 0x0cc 0x3e0 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT15__ECSPI1_SS1 0x0cc 0x3e0 0x7e8 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT15__ECSPI2_SS1 0x0cc 0x3e0 0x804 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT15__GPIO5_IO09 0x0cc 0x3e0 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x0d0 0x3e4 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT16__LCD_DATA16 0x0d0 0x3e4 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT16__ECSPI2_MOSI 0x0d0 0x3e4 0x7fc 0x2 0x1 -#define MX6DL_PAD_DISP0_DAT16__AUD5_TXC 0x0d0 0x3e4 0x7c0 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT16__SDMA_EXT_EVENT0 0x0d0 0x3e4 0x8e8 0x4 0x0 -#define MX6DL_PAD_DISP0_DAT16__GPIO5_IO10 0x0d0 0x3e4 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x0d4 0x3e8 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT17__LCD_DATA17 0x0d4 0x3e8 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT17__ECSPI2_MISO 0x0d4 0x3e8 0x7f8 0x2 0x1 -#define MX6DL_PAD_DISP0_DAT17__AUD5_TXD 0x0d4 0x3e8 0x7b4 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT17__SDMA_EXT_EVENT1 0x0d4 0x3e8 0x8ec 0x4 0x0 -#define MX6DL_PAD_DISP0_DAT17__GPIO5_IO11 0x0d4 0x3e8 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x0d8 0x3ec 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT18__LCD_DATA18 0x0d8 0x3ec 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT18__ECSPI2_SS0 0x0d8 0x3ec 0x800 0x2 0x1 -#define MX6DL_PAD_DISP0_DAT18__AUD5_TXFS 0x0d8 0x3ec 0x7c4 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT18__AUD4_RXFS 0x0d8 0x3ec 0x7a4 0x4 0x0 -#define MX6DL_PAD_DISP0_DAT18__GPIO5_IO12 0x0d8 0x3ec 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT18__EIM_CS2_B 0x0d8 0x3ec 0x000 0x7 0x0 -#define MX6DL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x0dc 0x3f0 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT19__LCD_DATA19 0x0dc 0x3f0 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT19__ECSPI2_SCLK 0x0dc 0x3f0 0x7f4 0x2 0x1 -#define MX6DL_PAD_DISP0_DAT19__AUD5_RXD 0x0dc 0x3f0 0x7b0 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT19__AUD4_RXC 0x0dc 0x3f0 0x7a0 0x4 0x0 -#define MX6DL_PAD_DISP0_DAT19__GPIO5_IO13 0x0dc 0x3f0 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT19__EIM_CS3_B 0x0dc 0x3f0 0x000 0x7 0x0 -#define MX6DL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x0e0 0x3f4 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT2__LCD_DATA02 0x0e0 0x3f4 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT2__ECSPI3_MISO 0x0e0 0x3f4 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT2__GPIO4_IO23 0x0e0 0x3f4 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x0e4 0x3f8 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT20__LCD_DATA20 0x0e4 0x3f8 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT20__ECSPI1_SCLK 0x0e4 0x3f8 0x7d8 0x2 0x1 -#define MX6DL_PAD_DISP0_DAT20__AUD4_TXC 0x0e4 0x3f8 0x7a8 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT20__GPIO5_IO14 0x0e4 0x3f8 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x0e8 0x3fc 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT21__LCD_DATA21 0x0e8 0x3fc 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT21__ECSPI1_MOSI 0x0e8 0x3fc 0x7e0 0x2 0x1 -#define MX6DL_PAD_DISP0_DAT21__AUD4_TXD 0x0e8 0x3fc 0x79c 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT21__GPIO5_IO15 0x0e8 0x3fc 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x0ec 0x400 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT22__LCD_DATA22 0x0ec 0x400 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT22__ECSPI1_MISO 0x0ec 0x400 0x7dc 0x2 0x1 -#define MX6DL_PAD_DISP0_DAT22__AUD4_TXFS 0x0ec 0x400 0x7ac 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT22__GPIO5_IO16 0x0ec 0x400 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x0f0 0x404 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT23__LCD_DATA23 0x0f0 0x404 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT23__ECSPI1_SS0 0x0f0 0x404 0x7e4 0x2 0x1 -#define MX6DL_PAD_DISP0_DAT23__AUD4_RXD 0x0f0 0x404 0x798 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT23__GPIO5_IO17 0x0f0 0x404 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x0f4 0x408 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT3__LCD_DATA03 0x0f4 0x408 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT3__ECSPI3_SS0 0x0f4 0x408 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT3__GPIO4_IO24 0x0f4 0x408 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x0f8 0x40c 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT4__LCD_DATA04 0x0f8 0x40c 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT4__ECSPI3_SS1 0x0f8 0x40c 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT4__GPIO4_IO25 0x0f8 0x40c 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x0fc 0x410 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT5__LCD_DATA05 0x0fc 0x410 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT5__ECSPI3_SS2 0x0fc 0x410 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT5__AUD6_RXFS 0x0fc 0x410 0x000 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT5__GPIO4_IO26 0x0fc 0x410 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x100 0x414 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT6__LCD_DATA06 0x100 0x414 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT6__ECSPI3_SS3 0x100 0x414 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT6__AUD6_RXC 0x100 0x414 0x000 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT6__GPIO4_IO27 0x100 0x414 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x104 0x418 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT7__LCD_DATA07 0x104 0x418 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT7__ECSPI3_RDY 0x104 0x418 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT7__GPIO4_IO28 0x104 0x418 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x108 0x41c 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT8__LCD_DATA08 0x108 0x41c 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT8__PWM1_OUT 0x108 0x41c 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT8__WDOG1_B 0x108 0x41c 0x000 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT8__GPIO4_IO29 0x108 0x41c 0x000 0x5 0x0 -#define MX6DL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x10c 0x420 0x000 0x0 0x0 -#define MX6DL_PAD_DISP0_DAT9__LCD_DATA09 0x10c 0x420 0x000 0x1 0x0 -#define MX6DL_PAD_DISP0_DAT9__PWM2_OUT 0x10c 0x420 0x000 0x2 0x0 -#define MX6DL_PAD_DISP0_DAT9__WDOG2_B 0x10c 0x420 0x000 0x3 0x0 -#define MX6DL_PAD_DISP0_DAT9__GPIO4_IO30 0x10c 0x420 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A16__EIM_ADDR16 0x110 0x4e0 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A16__IPU1_DI1_DISP_CLK 0x110 0x4e0 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A16__IPU1_CSI1_PIXCLK 0x110 0x4e0 0x8b8 0x2 0x0 -#define MX6DL_PAD_EIM_A16__GPIO2_IO22 0x110 0x4e0 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A16__SRC_BOOT_CFG16 0x110 0x4e0 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A16__EPDC_DATA00 0x110 0x4e0 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A17__EIM_ADDR17 0x114 0x4e4 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A17__IPU1_DISP1_DATA12 0x114 0x4e4 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A17__IPU1_CSI1_DATA12 0x114 0x4e4 0x890 0x2 0x0 -#define MX6DL_PAD_EIM_A17__GPIO2_IO21 0x114 0x4e4 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A17__SRC_BOOT_CFG17 0x114 0x4e4 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A17__EPDC_PWR_STAT 0x114 0x4e4 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A18__EIM_ADDR18 0x118 0x4e8 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A18__IPU1_DISP1_DATA13 0x118 0x4e8 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A18__IPU1_CSI1_DATA13 0x118 0x4e8 0x894 0x2 0x0 -#define MX6DL_PAD_EIM_A18__GPIO2_IO20 0x118 0x4e8 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A18__SRC_BOOT_CFG18 0x118 0x4e8 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A18__EPDC_PWR_CTRL0 0x118 0x4e8 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A19__EIM_ADDR19 0x11c 0x4ec 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A19__IPU1_DISP1_DATA14 0x11c 0x4ec 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A19__IPU1_CSI1_DATA14 0x11c 0x4ec 0x898 0x2 0x0 -#define MX6DL_PAD_EIM_A19__GPIO2_IO19 0x11c 0x4ec 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A19__SRC_BOOT_CFG19 0x11c 0x4ec 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A19__EPDC_PWR_CTRL1 0x11c 0x4ec 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A20__EIM_ADDR20 0x120 0x4f0 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A20__IPU1_DISP1_DATA15 0x120 0x4f0 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A20__IPU1_CSI1_DATA15 0x120 0x4f0 0x89c 0x2 0x0 -#define MX6DL_PAD_EIM_A20__GPIO2_IO18 0x120 0x4f0 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A20__SRC_BOOT_CFG20 0x120 0x4f0 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A20__EPDC_PWR_CTRL2 0x120 0x4f0 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A21__EIM_ADDR21 0x124 0x4f4 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A21__IPU1_DISP1_DATA16 0x124 0x4f4 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A21__IPU1_CSI1_DATA16 0x124 0x4f4 0x8a0 0x2 0x0 -#define MX6DL_PAD_EIM_A21__GPIO2_IO17 0x124 0x4f4 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A21__SRC_BOOT_CFG21 0x124 0x4f4 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A21__EPDC_GDCLK 0x124 0x4f4 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A22__EIM_ADDR22 0x128 0x4f8 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A22__IPU1_DISP1_DATA17 0x128 0x4f8 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A22__IPU1_CSI1_DATA17 0x128 0x4f8 0x8a4 0x2 0x0 -#define MX6DL_PAD_EIM_A22__GPIO2_IO16 0x128 0x4f8 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A22__SRC_BOOT_CFG22 0x128 0x4f8 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A22__EPDC_GDSP 0x128 0x4f8 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A23__EIM_ADDR23 0x12c 0x4fc 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A23__IPU1_DISP1_DATA18 0x12c 0x4fc 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A23__IPU1_CSI1_DATA18 0x12c 0x4fc 0x8a8 0x2 0x0 -#define MX6DL_PAD_EIM_A23__IPU1_SISG3 0x12c 0x4fc 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_A23__GPIO6_IO06 0x12c 0x4fc 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A23__SRC_BOOT_CFG23 0x12c 0x4fc 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A23__EPDC_GDOE 0x12c 0x4fc 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A24__EIM_ADDR24 0x130 0x500 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A24__IPU1_DISP1_DATA19 0x130 0x500 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A24__IPU1_CSI1_DATA19 0x130 0x500 0x8ac 0x2 0x0 -#define MX6DL_PAD_EIM_A24__IPU1_SISG2 0x130 0x500 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_A24__GPIO5_IO04 0x130 0x500 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A24__SRC_BOOT_CFG24 0x130 0x500 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_A24__EPDC_GDRL 0x130 0x500 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A25__EIM_ADDR25 0x134 0x504 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_A25__ECSPI4_SS1 0x134 0x504 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_A25__ECSPI2_RDY 0x134 0x504 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_A25__IPU1_DI1_PIN12 0x134 0x504 0x000 0x3 0x0 -#define MX6DL_PAD_EIM_A25__IPU1_DI0_D1_CS 0x134 0x504 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_A25__GPIO5_IO02 0x134 0x504 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_A25__HDMI_TX_CEC_LINE 0x134 0x504 0x85c 0x6 0x0 -#define MX6DL_PAD_EIM_A25__EPDC_DATA15 0x134 0x504 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_A25__EIM_ACLK_FREERUN 0x134 0x504 0x000 0x9 0x0 -#define MX6DL_PAD_EIM_BCLK__EIM_BCLK 0x138 0x508 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_BCLK__IPU1_DI1_PIN16 0x138 0x508 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_BCLK__GPIO6_IO31 0x138 0x508 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_BCLK__EPDC_SDCE9 0x138 0x508 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_CS0__EIM_CS0_B 0x13c 0x50c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_CS0__IPU1_DI1_PIN05 0x13c 0x50c 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_CS0__ECSPI2_SCLK 0x13c 0x50c 0x7f4 0x2 0x2 -#define MX6DL_PAD_EIM_CS0__GPIO2_IO23 0x13c 0x50c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_CS0__EPDC_DATA06 0x13c 0x50c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_CS1__EIM_CS1_B 0x140 0x510 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_CS1__IPU1_DI1_PIN06 0x140 0x510 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_CS1__ECSPI2_MOSI 0x140 0x510 0x7fc 0x2 0x2 -#define MX6DL_PAD_EIM_CS1__GPIO2_IO24 0x140 0x510 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_CS1__EPDC_DATA08 0x140 0x510 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D16__EIM_DATA16 0x144 0x514 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D16__ECSPI1_SCLK 0x144 0x514 0x7d8 0x1 0x2 -#define MX6DL_PAD_EIM_D16__IPU1_DI0_PIN05 0x144 0x514 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D16__IPU1_CSI1_DATA18 0x144 0x514 0x8a8 0x3 0x1 -#define MX6DL_PAD_EIM_D16__HDMI_TX_DDC_SDA 0x144 0x514 0x864 0x4 0x0 -#define MX6DL_PAD_EIM_D16__GPIO3_IO16 0x144 0x514 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D16__I2C2_SDA 0x144 0x514 0x874 0x6 0x0 -#define MX6DL_PAD_EIM_D16__EPDC_DATA10 0x144 0x514 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D17__EIM_DATA17 0x148 0x518 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D17__ECSPI1_MISO 0x148 0x518 0x7dc 0x1 0x2 -#define MX6DL_PAD_EIM_D17__IPU1_DI0_PIN06 0x148 0x518 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D17__IPU1_CSI1_PIXCLK 0x148 0x518 0x8b8 0x3 0x1 -#define MX6DL_PAD_EIM_D17__DCIC1_OUT 0x148 0x518 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D17__GPIO3_IO17 0x148 0x518 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D17__I2C3_SCL 0x148 0x518 0x878 0x6 0x0 -#define MX6DL_PAD_EIM_D17__EPDC_VCOM0 0x148 0x518 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D18__EIM_DATA18 0x14c 0x51c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D18__ECSPI1_MOSI 0x14c 0x51c 0x7e0 0x1 0x2 -#define MX6DL_PAD_EIM_D18__IPU1_DI0_PIN07 0x14c 0x51c 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D18__IPU1_CSI1_DATA17 0x14c 0x51c 0x8a4 0x3 0x1 -#define MX6DL_PAD_EIM_D18__IPU1_DI1_D0_CS 0x14c 0x51c 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D18__GPIO3_IO18 0x14c 0x51c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D18__I2C3_SDA 0x14c 0x51c 0x87c 0x6 0x0 -#define MX6DL_PAD_EIM_D18__EPDC_VCOM1 0x14c 0x51c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D19__EIM_DATA19 0x150 0x520 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D19__ECSPI1_SS1 0x150 0x520 0x7e8 0x1 0x1 -#define MX6DL_PAD_EIM_D19__IPU1_DI0_PIN08 0x150 0x520 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D19__IPU1_CSI1_DATA16 0x150 0x520 0x8a0 0x3 0x1 -#define MX6DL_PAD_EIM_D19__UART1_CTS_B 0x150 0x520 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D19__UART1_RTS_B 0x150 0x520 0x8f8 0x4 0x0 -#define MX6DL_PAD_EIM_D19__GPIO3_IO19 0x150 0x520 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D19__EPIT1_OUT 0x150 0x520 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_D19__EPDC_DATA12 0x150 0x520 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D20__EIM_DATA20 0x154 0x524 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D20__ECSPI4_SS0 0x154 0x524 0x808 0x1 0x0 -#define MX6DL_PAD_EIM_D20__IPU1_DI0_PIN16 0x154 0x524 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D20__IPU1_CSI1_DATA15 0x154 0x524 0x89c 0x3 0x1 -#define MX6DL_PAD_EIM_D20__UART1_RTS_B 0x154 0x524 0x8f8 0x4 0x1 -#define MX6DL_PAD_EIM_D20__UART1_CTS_B 0x154 0x524 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D20__GPIO3_IO20 0x154 0x524 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D20__EPIT2_OUT 0x154 0x524 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_D21__EIM_DATA21 0x158 0x528 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D21__ECSPI4_SCLK 0x158 0x528 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D21__IPU1_DI0_PIN17 0x158 0x528 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D21__IPU1_CSI1_DATA11 0x158 0x528 0x88c 0x3 0x0 -#define MX6DL_PAD_EIM_D21__USB_OTG_OC 0x158 0x528 0x920 0x4 0x0 -#define MX6DL_PAD_EIM_D21__GPIO3_IO21 0x158 0x528 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D21__I2C1_SCL 0x158 0x528 0x868 0x6 0x1 -#define MX6DL_PAD_EIM_D21__SPDIF_IN 0x158 0x528 0x8f0 0x7 0x0 -#define MX6DL_PAD_EIM_D22__EIM_DATA22 0x15c 0x52c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D22__ECSPI4_MISO 0x15c 0x52c 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D22__IPU1_DI0_PIN01 0x15c 0x52c 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D22__IPU1_CSI1_DATA10 0x15c 0x52c 0x888 0x3 0x0 -#define MX6DL_PAD_EIM_D22__USB_OTG_PWR 0x15c 0x52c 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D22__GPIO3_IO22 0x15c 0x52c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D22__SPDIF_OUT 0x15c 0x52c 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_D22__EPDC_SDCE6 0x15c 0x52c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D23__EIM_DATA23 0x160 0x530 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D23__IPU1_DI0_D0_CS 0x160 0x530 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D23__UART3_CTS_B 0x160 0x530 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D23__UART3_RTS_B 0x160 0x530 0x908 0x2 0x0 -#define MX6DL_PAD_EIM_D23__UART1_DCD_B 0x160 0x530 0x000 0x3 0x0 -#define MX6DL_PAD_EIM_D23__IPU1_CSI1_DATA_EN 0x160 0x530 0x8b0 0x4 0x0 -#define MX6DL_PAD_EIM_D23__GPIO3_IO23 0x160 0x530 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D23__IPU1_DI1_PIN02 0x160 0x530 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_D23__IPU1_DI1_PIN14 0x160 0x530 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_D23__EPDC_DATA11 0x160 0x530 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D24__EIM_DATA24 0x164 0x534 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D24__ECSPI4_SS2 0x164 0x534 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D24__UART3_TX_DATA 0x164 0x534 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D24__UART3_RX_DATA 0x164 0x534 0x90c 0x2 0x0 -#define MX6DL_PAD_EIM_D24__ECSPI1_SS2 0x164 0x534 0x7ec 0x3 0x0 -#define MX6DL_PAD_EIM_D24__ECSPI2_SS2 0x164 0x534 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D24__GPIO3_IO24 0x164 0x534 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D24__AUD5_RXFS 0x164 0x534 0x7bc 0x6 0x1 -#define MX6DL_PAD_EIM_D24__UART1_DTR_B 0x164 0x534 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_D24__EPDC_SDCE7 0x164 0x534 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D25__EIM_DATA25 0x168 0x538 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D25__ECSPI4_SS3 0x168 0x538 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D25__UART3_RX_DATA 0x168 0x538 0x90c 0x2 0x1 -#define MX6DL_PAD_EIM_D25__UART3_TX_DATA 0x168 0x538 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D25__ECSPI1_SS3 0x168 0x538 0x7f0 0x3 0x0 -#define MX6DL_PAD_EIM_D25__ECSPI2_SS3 0x168 0x538 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D25__GPIO3_IO25 0x168 0x538 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D25__AUD5_RXC 0x168 0x538 0x7b8 0x6 0x1 -#define MX6DL_PAD_EIM_D25__UART1_DSR_B 0x168 0x538 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_D25__EPDC_SDCE8 0x168 0x538 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D26__EIM_DATA26 0x16c 0x53c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D26__IPU1_DI1_PIN11 0x16c 0x53c 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D26__IPU1_CSI0_DATA01 0x16c 0x53c 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D26__IPU1_CSI1_DATA14 0x16c 0x53c 0x898 0x3 0x1 -#define MX6DL_PAD_EIM_D26__UART2_TX_DATA 0x16c 0x53c 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D26__UART2_RX_DATA 0x16c 0x53c 0x904 0x4 0x0 -#define MX6DL_PAD_EIM_D26__GPIO3_IO26 0x16c 0x53c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D26__IPU1_SISG2 0x16c 0x53c 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_D26__IPU1_DISP1_DATA22 0x16c 0x53c 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_D26__EPDC_SDOED 0x16c 0x53c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D27__EIM_DATA27 0x170 0x540 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D27__IPU1_DI1_PIN13 0x170 0x540 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D27__IPU1_CSI0_DATA00 0x170 0x540 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D27__IPU1_CSI1_DATA13 0x170 0x540 0x894 0x3 0x1 -#define MX6DL_PAD_EIM_D27__UART2_RX_DATA 0x170 0x540 0x904 0x4 0x1 -#define MX6DL_PAD_EIM_D27__UART2_TX_DATA 0x170 0x540 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D27__GPIO3_IO27 0x170 0x540 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D27__IPU1_SISG3 0x170 0x540 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_D27__IPU1_DISP1_DATA23 0x170 0x540 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_D27__EPDC_SDOE 0x170 0x540 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D28__EIM_DATA28 0x174 0x544 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D28__I2C1_SDA 0x174 0x544 0x86c 0x1 0x1 -#define MX6DL_PAD_EIM_D28__ECSPI4_MOSI 0x174 0x544 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D28__IPU1_CSI1_DATA12 0x174 0x544 0x890 0x3 0x1 -#define MX6DL_PAD_EIM_D28__UART2_CTS_B 0x174 0x544 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D28__UART2_RTS_B 0x174 0x544 0x900 0x4 0x0 -#define MX6DL_PAD_EIM_D28__GPIO3_IO28 0x174 0x544 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D28__IPU1_EXT_TRIG 0x174 0x544 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_D28__IPU1_DI0_PIN13 0x174 0x544 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_D28__EPDC_PWR_CTRL3 0x174 0x544 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D29__EIM_DATA29 0x178 0x548 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D29__IPU1_DI1_PIN15 0x178 0x548 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D29__ECSPI4_SS0 0x178 0x548 0x808 0x2 0x1 -#define MX6DL_PAD_EIM_D29__UART2_RTS_B 0x178 0x548 0x900 0x4 0x1 -#define MX6DL_PAD_EIM_D29__UART2_CTS_B 0x178 0x548 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D29__GPIO3_IO29 0x178 0x548 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D29__IPU1_CSI1_VSYNC 0x178 0x548 0x8bc 0x6 0x0 -#define MX6DL_PAD_EIM_D29__IPU1_DI0_PIN14 0x178 0x548 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_D29__EPDC_PWR_WAKE 0x178 0x548 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D30__EIM_DATA30 0x17c 0x54c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D30__IPU1_DISP1_DATA21 0x17c 0x54c 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D30__IPU1_DI0_PIN11 0x17c 0x54c 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D30__IPU1_CSI0_DATA03 0x17c 0x54c 0x000 0x3 0x0 -#define MX6DL_PAD_EIM_D30__UART3_CTS_B 0x17c 0x54c 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D30__UART3_RTS_B 0x17c 0x54c 0x908 0x4 0x1 -#define MX6DL_PAD_EIM_D30__GPIO3_IO30 0x17c 0x54c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D30__USB_H1_OC 0x17c 0x54c 0x924 0x6 0x0 -#define MX6DL_PAD_EIM_D30__EPDC_SDOEZ 0x17c 0x54c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D31__EIM_DATA31 0x180 0x550 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_D31__IPU1_DISP1_DATA20 0x180 0x550 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_D31__IPU1_DI0_PIN12 0x180 0x550 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_D31__IPU1_CSI0_DATA02 0x180 0x550 0x000 0x3 0x0 -#define MX6DL_PAD_EIM_D31__UART3_RTS_B 0x180 0x550 0x908 0x4 0x2 -#define MX6DL_PAD_EIM_D31__UART3_CTS_B 0x180 0x550 0x000 0x4 0x0 -#define MX6DL_PAD_EIM_D31__GPIO3_IO31 0x180 0x550 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_D31__USB_H1_PWR 0x180 0x550 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_D31__EPDC_SDCLK_P 0x180 0x550 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_D31__EIM_ACLK_FREERUN 0x180 0x550 0x000 0x9 0x0 -#define MX6DL_PAD_EIM_DA0__EIM_AD00 0x184 0x554 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA0__IPU1_DISP1_DATA09 0x184 0x554 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA0__IPU1_CSI1_DATA09 0x184 0x554 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA0__GPIO3_IO00 0x184 0x554 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA0__SRC_BOOT_CFG00 0x184 0x554 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA0__EPDC_SDCLK_N 0x184 0x554 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA1__EIM_AD01 0x188 0x558 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA1__IPU1_DISP1_DATA08 0x188 0x558 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA1__IPU1_CSI1_DATA08 0x188 0x558 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA1__GPIO3_IO01 0x188 0x558 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA1__SRC_BOOT_CFG01 0x188 0x558 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA1__EPDC_SDLE 0x188 0x558 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA10__EIM_AD10 0x18c 0x55c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA10__IPU1_DI1_PIN15 0x18c 0x55c 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA10__IPU1_CSI1_DATA_EN 0x18c 0x55c 0x8b0 0x2 0x1 -#define MX6DL_PAD_EIM_DA10__GPIO3_IO10 0x18c 0x55c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA10__SRC_BOOT_CFG10 0x18c 0x55c 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA10__EPDC_DATA01 0x18c 0x55c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA11__EIM_AD11 0x190 0x560 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA11__IPU1_DI1_PIN02 0x190 0x560 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA11__IPU1_CSI1_HSYNC 0x190 0x560 0x8b4 0x2 0x0 -#define MX6DL_PAD_EIM_DA11__GPIO3_IO11 0x190 0x560 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA11__SRC_BOOT_CFG11 0x190 0x560 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA11__EPDC_DATA03 0x190 0x560 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA12__EIM_AD12 0x194 0x564 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA12__IPU1_DI1_PIN03 0x194 0x564 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA12__IPU1_CSI1_VSYNC 0x194 0x564 0x8bc 0x2 0x1 -#define MX6DL_PAD_EIM_DA12__GPIO3_IO12 0x194 0x564 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA12__SRC_BOOT_CFG12 0x194 0x564 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA12__EPDC_DATA02 0x194 0x564 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA13__EIM_AD13 0x198 0x568 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA13__IPU1_DI1_D0_CS 0x198 0x568 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA13__GPIO3_IO13 0x198 0x568 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA13__SRC_BOOT_CFG13 0x198 0x568 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA13__EPDC_DATA13 0x198 0x568 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA14__EIM_AD14 0x19c 0x56c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA14__IPU1_DI1_D1_CS 0x19c 0x56c 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA14__GPIO3_IO14 0x19c 0x56c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA14__SRC_BOOT_CFG14 0x19c 0x56c 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA14__EPDC_DATA14 0x19c 0x56c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA15__EIM_AD15 0x1a0 0x570 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA15__IPU1_DI1_PIN01 0x1a0 0x570 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA15__IPU1_DI1_PIN04 0x1a0 0x570 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA15__GPIO3_IO15 0x1a0 0x570 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA15__SRC_BOOT_CFG15 0x1a0 0x570 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA15__EPDC_DATA09 0x1a0 0x570 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA2__EIM_AD02 0x1a4 0x574 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA2__IPU1_DISP1_DATA07 0x1a4 0x574 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA2__IPU1_CSI1_DATA07 0x1a4 0x574 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA2__GPIO3_IO02 0x1a4 0x574 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA2__SRC_BOOT_CFG02 0x1a4 0x574 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA2__EPDC_BDR0 0x1a4 0x574 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA3__EIM_AD03 0x1a8 0x578 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA3__IPU1_DISP1_DATA06 0x1a8 0x578 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA3__IPU1_CSI1_DATA06 0x1a8 0x578 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA3__GPIO3_IO03 0x1a8 0x578 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA3__SRC_BOOT_CFG03 0x1a8 0x578 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA3__EPDC_BDR1 0x1a8 0x578 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA4__EIM_AD04 0x1ac 0x57c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA4__IPU1_DISP1_DATA05 0x1ac 0x57c 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA4__IPU1_CSI1_DATA05 0x1ac 0x57c 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA4__GPIO3_IO04 0x1ac 0x57c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA4__SRC_BOOT_CFG04 0x1ac 0x57c 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA4__EPDC_SDCE0 0x1ac 0x57c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA5__EIM_AD05 0x1b0 0x580 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA5__IPU1_DISP1_DATA04 0x1b0 0x580 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA5__IPU1_CSI1_DATA04 0x1b0 0x580 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA5__GPIO3_IO05 0x1b0 0x580 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA5__SRC_BOOT_CFG05 0x1b0 0x580 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA5__EPDC_SDCE1 0x1b0 0x580 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA6__EIM_AD06 0x1b4 0x584 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA6__IPU1_DISP1_DATA03 0x1b4 0x584 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA6__IPU1_CSI1_DATA03 0x1b4 0x584 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA6__GPIO3_IO06 0x1b4 0x584 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA6__SRC_BOOT_CFG06 0x1b4 0x584 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA6__EPDC_SDCE2 0x1b4 0x584 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA7__EIM_AD07 0x1b8 0x588 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA7__IPU1_DISP1_DATA02 0x1b8 0x588 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA7__IPU1_CSI1_DATA02 0x1b8 0x588 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA7__GPIO3_IO07 0x1b8 0x588 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA7__SRC_BOOT_CFG07 0x1b8 0x588 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA7__EPDC_SDCE3 0x1b8 0x588 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA8__EIM_AD08 0x1bc 0x58c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA8__IPU1_DISP1_DATA01 0x1bc 0x58c 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA8__IPU1_CSI1_DATA01 0x1bc 0x58c 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA8__GPIO3_IO08 0x1bc 0x58c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA8__SRC_BOOT_CFG08 0x1bc 0x58c 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA8__EPDC_SDCE4 0x1bc 0x58c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_DA9__EIM_AD09 0x1c0 0x590 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_DA9__IPU1_DISP1_DATA00 0x1c0 0x590 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_DA9__IPU1_CSI1_DATA00 0x1c0 0x590 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_DA9__GPIO3_IO09 0x1c0 0x590 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_DA9__SRC_BOOT_CFG09 0x1c0 0x590 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_DA9__EPDC_SDCE5 0x1c0 0x590 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_EB0__EIM_EB0_B 0x1c4 0x594 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_EB0__IPU1_DISP1_DATA11 0x1c4 0x594 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_EB0__IPU1_CSI1_DATA11 0x1c4 0x594 0x88c 0x2 0x1 -#define MX6DL_PAD_EIM_EB0__CCM_PMIC_READY 0x1c4 0x594 0x7d4 0x4 0x0 -#define MX6DL_PAD_EIM_EB0__GPIO2_IO28 0x1c4 0x594 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_EB0__SRC_BOOT_CFG27 0x1c4 0x594 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_EB0__EPDC_PWR_COM 0x1c4 0x594 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_EB1__EIM_EB1_B 0x1c8 0x598 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_EB1__IPU1_DISP1_DATA10 0x1c8 0x598 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_EB1__IPU1_CSI1_DATA10 0x1c8 0x598 0x888 0x2 0x1 -#define MX6DL_PAD_EIM_EB1__GPIO2_IO29 0x1c8 0x598 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_EB1__SRC_BOOT_CFG28 0x1c8 0x598 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_EB1__EPDC_SDSHR 0x1c8 0x598 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_EB2__EIM_EB2_B 0x1cc 0x59c 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_EB2__ECSPI1_SS0 0x1cc 0x59c 0x7e4 0x1 0x2 -#define MX6DL_PAD_EIM_EB2__IPU1_CSI1_DATA19 0x1cc 0x59c 0x8ac 0x3 0x1 -#define MX6DL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x1cc 0x59c 0x860 0x4 0x0 -#define MX6DL_PAD_EIM_EB2__GPIO2_IO30 0x1cc 0x59c 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_EB2__I2C2_SCL 0x1cc 0x59c 0x870 0x6 0x0 -#define MX6DL_PAD_EIM_EB2__SRC_BOOT_CFG30 0x1cc 0x59c 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_EB2__EPDC_DATA05 0x1cc 0x59c 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_EB3__EIM_EB3_B 0x1d0 0x5a0 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_EB3__ECSPI4_RDY 0x1d0 0x5a0 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_EB3__UART3_RTS_B 0x1d0 0x5a0 0x908 0x2 0x3 -#define MX6DL_PAD_EIM_EB3__UART3_CTS_B 0x1d0 0x5a0 0x000 0x2 0x0 -#define MX6DL_PAD_EIM_EB3__UART1_RI_B 0x1d0 0x5a0 0x000 0x3 0x0 -#define MX6DL_PAD_EIM_EB3__IPU1_CSI1_HSYNC 0x1d0 0x5a0 0x8b4 0x4 0x1 -#define MX6DL_PAD_EIM_EB3__GPIO2_IO31 0x1d0 0x5a0 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_EB3__IPU1_DI1_PIN03 0x1d0 0x5a0 0x000 0x6 0x0 -#define MX6DL_PAD_EIM_EB3__SRC_BOOT_CFG31 0x1d0 0x5a0 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_EB3__EPDC_SDCE0 0x1d0 0x5a0 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_EB3__EIM_ACLK_FREERUN 0x1d0 0x5a0 0x000 0x9 0x0 -#define MX6DL_PAD_EIM_LBA__EIM_LBA_B 0x1d4 0x5a4 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_LBA__IPU1_DI1_PIN17 0x1d4 0x5a4 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_LBA__ECSPI2_SS1 0x1d4 0x5a4 0x804 0x2 0x1 -#define MX6DL_PAD_EIM_LBA__GPIO2_IO27 0x1d4 0x5a4 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_LBA__SRC_BOOT_CFG26 0x1d4 0x5a4 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_LBA__EPDC_DATA04 0x1d4 0x5a4 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_OE__EIM_OE_B 0x1d8 0x5a8 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_OE__IPU1_DI1_PIN07 0x1d8 0x5a8 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_OE__ECSPI2_MISO 0x1d8 0x5a8 0x7f8 0x2 0x2 -#define MX6DL_PAD_EIM_OE__GPIO2_IO25 0x1d8 0x5a8 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_OE__EPDC_PWR_IRQ 0x1d8 0x5a8 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_RW__EIM_RW 0x1dc 0x5ac 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_RW__IPU1_DI1_PIN08 0x1dc 0x5ac 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_RW__ECSPI2_SS0 0x1dc 0x5ac 0x800 0x2 0x2 -#define MX6DL_PAD_EIM_RW__GPIO2_IO26 0x1dc 0x5ac 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_RW__SRC_BOOT_CFG29 0x1dc 0x5ac 0x000 0x7 0x0 -#define MX6DL_PAD_EIM_RW__EPDC_DATA07 0x1dc 0x5ac 0x000 0x8 0x0 -#define MX6DL_PAD_EIM_WAIT__EIM_WAIT_B 0x1e0 0x5b0 0x000 0x0 0x0 -#define MX6DL_PAD_EIM_WAIT__EIM_DTACK_B 0x1e0 0x5b0 0x000 0x1 0x0 -#define MX6DL_PAD_EIM_WAIT__GPIO5_IO00 0x1e0 0x5b0 0x000 0x5 0x0 -#define MX6DL_PAD_EIM_WAIT__SRC_BOOT_CFG25 0x1e0 0x5b0 0x000 0x7 0x0 -#define MX6DL_PAD_ENET_CRS_DV__ENET_RX_EN 0x1e4 0x5b4 0x828 0x1 0x0 -#define MX6DL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1e4 0x5b4 0x840 0x2 0x0 -#define MX6DL_PAD_ENET_CRS_DV__SPDIF_EXT_CLK 0x1e4 0x5b4 0x8f4 0x3 0x0 -#define MX6DL_PAD_ENET_CRS_DV__GPIO1_IO25 0x1e4 0x5b4 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_MDC__MLB_DATA 0x1e8 0x5b8 0x8e0 0x0 0x0 -#define MX6DL_PAD_ENET_MDC__ENET_MDC 0x1e8 0x5b8 0x000 0x1 0x0 -#define MX6DL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1e8 0x5b8 0x858 0x2 0x0 -#define MX6DL_PAD_ENET_MDC__ENET_1588_EVENT1_IN 0x1e8 0x5b8 0x000 0x4 0x0 -#define MX6DL_PAD_ENET_MDC__GPIO1_IO31 0x1e8 0x5b8 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_MDIO__ENET_MDIO 0x1ec 0x5bc 0x810 0x1 0x0 -#define MX6DL_PAD_ENET_MDIO__ESAI_RX_CLK 0x1ec 0x5bc 0x83c 0x2 0x0 -#define MX6DL_PAD_ENET_MDIO__ENET_1588_EVENT1_OUT 0x1ec 0x5bc 0x000 0x4 0x0 -#define MX6DL_PAD_ENET_MDIO__GPIO1_IO22 0x1ec 0x5bc 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_MDIO__SPDIF_LOCK 0x1ec 0x5bc 0x000 0x6 0x0 -#define MX6DL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1f0 0x5c0 0x000 0x1 0x0 -#define MX6DL_PAD_ENET_REF_CLK__ESAI_RX_FS 0x1f0 0x5c0 0x82c 0x2 0x0 -#define MX6DL_PAD_ENET_REF_CLK__GPIO1_IO23 0x1f0 0x5c0 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_REF_CLK__SPDIF_SR_CLK 0x1f0 0x5c0 0x000 0x6 0x0 -#define MX6DL_PAD_ENET_RX_ER__USB_OTG_ID 0x1f4 0x5c4 0x790 0x0 0x0 -#define MX6DL_PAD_ENET_RX_ER__ENET_RX_ER 0x1f4 0x5c4 0x000 0x1 0x0 -#define MX6DL_PAD_ENET_RX_ER__ESAI_RX_HF_CLK 0x1f4 0x5c4 0x834 0x2 0x0 -#define MX6DL_PAD_ENET_RX_ER__SPDIF_IN 0x1f4 0x5c4 0x8f0 0x3 0x1 -#define MX6DL_PAD_ENET_RX_ER__ENET_1588_EVENT2_OUT 0x1f4 0x5c4 0x000 0x4 0x0 -#define MX6DL_PAD_ENET_RX_ER__GPIO1_IO24 0x1f4 0x5c4 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_RXD0__ENET_RX_DATA0 0x1f8 0x5c8 0x818 0x1 0x0 -#define MX6DL_PAD_ENET_RXD0__ESAI_TX_HF_CLK 0x1f8 0x5c8 0x838 0x2 0x0 -#define MX6DL_PAD_ENET_RXD0__SPDIF_OUT 0x1f8 0x5c8 0x000 0x3 0x0 -#define MX6DL_PAD_ENET_RXD0__GPIO1_IO27 0x1f8 0x5c8 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_RXD1__MLB_SIG 0x1fc 0x5cc 0x8e4 0x0 0x0 -#define MX6DL_PAD_ENET_RXD1__ENET_RX_DATA1 0x1fc 0x5cc 0x81c 0x1 0x0 -#define MX6DL_PAD_ENET_RXD1__ESAI_TX_FS 0x1fc 0x5cc 0x830 0x2 0x0 -#define MX6DL_PAD_ENET_RXD1__ENET_1588_EVENT3_OUT 0x1fc 0x5cc 0x000 0x4 0x0 -#define MX6DL_PAD_ENET_RXD1__GPIO1_IO26 0x1fc 0x5cc 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_TX_EN__ENET_TX_EN 0x200 0x5d0 0x000 0x1 0x0 -#define MX6DL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x200 0x5d0 0x850 0x2 0x0 -#define MX6DL_PAD_ENET_TX_EN__GPIO1_IO28 0x200 0x5d0 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_TX_EN__I2C4_SCL 0x200 0x5d0 0x880 0x9 0x0 -#define MX6DL_PAD_ENET_TXD0__ENET_TX_DATA0 0x204 0x5d4 0x000 0x1 0x0 -#define MX6DL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x204 0x5d4 0x854 0x2 0x0 -#define MX6DL_PAD_ENET_TXD0__GPIO1_IO30 0x204 0x5d4 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_TXD1__MLB_CLK 0x208 0x5d8 0x8dc 0x0 0x0 -#define MX6DL_PAD_ENET_TXD1__ENET_TX_DATA1 0x208 0x5d8 0x000 0x1 0x0 -#define MX6DL_PAD_ENET_TXD1__ESAI_TX2_RX3 0x208 0x5d8 0x84c 0x2 0x0 -#define MX6DL_PAD_ENET_TXD1__ENET_1588_EVENT0_IN 0x208 0x5d8 0x000 0x4 0x0 -#define MX6DL_PAD_ENET_TXD1__GPIO1_IO29 0x208 0x5d8 0x000 0x5 0x0 -#define MX6DL_PAD_ENET_TXD1__I2C4_SDA 0x208 0x5d8 0x884 0x9 0x0 -#define MX6DL_PAD_GPIO_0__CCM_CLKO1 0x20c 0x5dc 0x000 0x0 0x0 -#define MX6DL_PAD_GPIO_0__KEY_COL5 0x20c 0x5dc 0x8c0 0x2 0x1 -#define MX6DL_PAD_GPIO_0__ASRC_EXT_CLK 0x20c 0x5dc 0x794 0x3 0x0 -#define MX6DL_PAD_GPIO_0__EPIT1_OUT 0x20c 0x5dc 0x000 0x4 0x0 -#define MX6DL_PAD_GPIO_0__GPIO1_IO00 0x20c 0x5dc 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_0__USB_H1_PWR 0x20c 0x5dc 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_0__SNVS_VIO_5 0x20c 0x5dc 0x000 0x7 0x0 -#define MX6DL_PAD_GPIO_1__ESAI_RX_CLK 0x210 0x5e0 0x83c 0x0 0x1 -#define MX6DL_PAD_GPIO_1__WDOG2_B 0x210 0x5e0 0x000 0x1 0x0 -#define MX6DL_PAD_GPIO_1__KEY_ROW5 0x210 0x5e0 0x8cc 0x2 0x1 -#define MX6DL_PAD_GPIO_1__USB_OTG_ID 0x210 0x5e0 0x790 0x3 0x1 -#define MX6DL_PAD_GPIO_1__PWM2_OUT 0x210 0x5e0 0x000 0x4 0x0 -#define MX6DL_PAD_GPIO_1__GPIO1_IO01 0x210 0x5e0 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_1__SD1_CD_B 0x210 0x5e0 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_16__ESAI_TX3_RX2 0x214 0x5e4 0x850 0x0 0x1 -#define MX6DL_PAD_GPIO_16__ENET_1588_EVENT2_IN 0x214 0x5e4 0x000 0x1 0x0 -#define MX6DL_PAD_GPIO_16__ENET_REF_CLK 0x214 0x5e4 0x80c 0x2 0x0 -#define MX6DL_PAD_GPIO_16__SD1_LCTL 0x214 0x5e4 0x000 0x3 0x0 -#define MX6DL_PAD_GPIO_16__SPDIF_IN 0x214 0x5e4 0x8f0 0x4 0x2 -#define MX6DL_PAD_GPIO_16__GPIO7_IO11 0x214 0x5e4 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_16__I2C3_SDA 0x214 0x5e4 0x87c 0x6 0x1 -#define MX6DL_PAD_GPIO_16__JTAG_DE_B 0x214 0x5e4 0x000 0x7 0x0 -#define MX6DL_PAD_GPIO_17__ESAI_TX0 0x218 0x5e8 0x844 0x0 0x0 -#define MX6DL_PAD_GPIO_17__ENET_1588_EVENT3_IN 0x218 0x5e8 0x000 0x1 0x0 -#define MX6DL_PAD_GPIO_17__CCM_PMIC_READY 0x218 0x5e8 0x7d4 0x2 0x1 -#define MX6DL_PAD_GPIO_17__SDMA_EXT_EVENT0 0x218 0x5e8 0x8e8 0x3 0x1 -#define MX6DL_PAD_GPIO_17__SPDIF_OUT 0x218 0x5e8 0x000 0x4 0x0 -#define MX6DL_PAD_GPIO_17__GPIO7_IO12 0x218 0x5e8 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_18__ESAI_TX1 0x21c 0x5ec 0x848 0x0 0x0 -#define MX6DL_PAD_GPIO_18__ENET_RX_CLK 0x21c 0x5ec 0x814 0x1 0x0 -#define MX6DL_PAD_GPIO_18__SD3_VSELECT 0x21c 0x5ec 0x000 0x2 0x0 -#define MX6DL_PAD_GPIO_18__SDMA_EXT_EVENT1 0x21c 0x5ec 0x8ec 0x3 0x1 -#define MX6DL_PAD_GPIO_18__ASRC_EXT_CLK 0x21c 0x5ec 0x794 0x4 0x1 -#define MX6DL_PAD_GPIO_18__GPIO7_IO13 0x21c 0x5ec 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_18__SNVS_VIO_5_CTL 0x21c 0x5ec 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_19__KEY_COL5 0x220 0x5f0 0x8c0 0x0 0x2 -#define MX6DL_PAD_GPIO_19__ENET_1588_EVENT0_OUT 0x220 0x5f0 0x000 0x1 0x0 -#define MX6DL_PAD_GPIO_19__SPDIF_OUT 0x220 0x5f0 0x000 0x2 0x0 -#define MX6DL_PAD_GPIO_19__CCM_CLKO1 0x220 0x5f0 0x000 0x3 0x0 -#define MX6DL_PAD_GPIO_19__ECSPI1_RDY 0x220 0x5f0 0x000 0x4 0x0 -#define MX6DL_PAD_GPIO_19__GPIO4_IO05 0x220 0x5f0 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_19__ENET_TX_ER 0x220 0x5f0 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_2__ESAI_TX_FS 0x224 0x5f4 0x830 0x0 0x1 -#define MX6DL_PAD_GPIO_2__KEY_ROW6 0x224 0x5f4 0x8d0 0x2 0x1 -#define MX6DL_PAD_GPIO_2__GPIO1_IO02 0x224 0x5f4 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_2__SD2_WP 0x224 0x5f4 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_2__MLB_DATA 0x224 0x5f4 0x8e0 0x7 0x1 -#define MX6DL_PAD_GPIO_3__ESAI_RX_HF_CLK 0x228 0x5f8 0x834 0x0 0x1 -#define MX6DL_PAD_GPIO_3__I2C3_SCL 0x228 0x5f8 0x878 0x2 0x1 -#define MX6DL_PAD_GPIO_3__XTALOSC_REF_CLK_24M 0x228 0x5f8 0x000 0x3 0x0 -#define MX6DL_PAD_GPIO_3__CCM_CLKO2 0x228 0x5f8 0x000 0x4 0x0 -#define MX6DL_PAD_GPIO_3__GPIO1_IO03 0x228 0x5f8 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_3__USB_H1_OC 0x228 0x5f8 0x924 0x6 0x1 -#define MX6DL_PAD_GPIO_3__MLB_CLK 0x228 0x5f8 0x8dc 0x7 0x1 -#define MX6DL_PAD_GPIO_4__ESAI_TX_HF_CLK 0x22c 0x5fc 0x838 0x0 0x1 -#define MX6DL_PAD_GPIO_4__KEY_COL7 0x22c 0x5fc 0x8c8 0x2 0x1 -#define MX6DL_PAD_GPIO_4__GPIO1_IO04 0x22c 0x5fc 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_4__SD2_CD_B 0x22c 0x5fc 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_5__ESAI_TX2_RX3 0x230 0x600 0x84c 0x0 0x1 -#define MX6DL_PAD_GPIO_5__KEY_ROW7 0x230 0x600 0x8d4 0x2 0x1 -#define MX6DL_PAD_GPIO_5__CCM_CLKO1 0x230 0x600 0x000 0x3 0x0 -#define MX6DL_PAD_GPIO_5__GPIO1_IO05 0x230 0x600 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_5__I2C3_SCL 0x230 0x600 0x878 0x6 0x2 -#define MX6DL_PAD_GPIO_5__ARM_EVENTI 0x230 0x600 0x000 0x7 0x0 -#define MX6DL_PAD_GPIO_6__ESAI_TX_CLK 0x234 0x604 0x840 0x0 0x1 -#define MX6DL_PAD_GPIO_6__I2C3_SDA 0x234 0x604 0x87c 0x2 0x2 -#define MX6DL_PAD_GPIO_6__GPIO1_IO06 0x234 0x604 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_6__SD2_LCTL 0x234 0x604 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_6__MLB_SIG 0x234 0x604 0x8e4 0x7 0x1 -#define MX6DL_PAD_GPIO_7__ESAI_TX4_RX1 0x238 0x608 0x854 0x0 0x1 -#define MX6DL_PAD_GPIO_7__EPIT1_OUT 0x238 0x608 0x000 0x2 0x0 -#define MX6DL_PAD_GPIO_7__FLEXCAN1_TX 0x238 0x608 0x000 0x3 0x0 -#define MX6DL_PAD_GPIO_7__UART2_TX_DATA 0x238 0x608 0x000 0x4 0x0 -#define MX6DL_PAD_GPIO_7__UART2_RX_DATA 0x238 0x608 0x904 0x4 0x2 -#define MX6DL_PAD_GPIO_7__GPIO1_IO07 0x238 0x608 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_7__SPDIF_LOCK 0x238 0x608 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_7__USB_OTG_HOST_MODE 0x238 0x608 0x000 0x7 0x0 -#define MX6DL_PAD_GPIO_7__I2C4_SCL 0x238 0x608 0x880 0x8 0x1 -#define MX6DL_PAD_GPIO_8__ESAI_TX5_RX0 0x23c 0x60c 0x858 0x0 0x1 -#define MX6DL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x23c 0x60c 0x000 0x1 0x0 -#define MX6DL_PAD_GPIO_8__EPIT2_OUT 0x23c 0x60c 0x000 0x2 0x0 -#define MX6DL_PAD_GPIO_8__FLEXCAN1_RX 0x23c 0x60c 0x7c8 0x3 0x0 -#define MX6DL_PAD_GPIO_8__UART2_RX_DATA 0x23c 0x60c 0x904 0x4 0x3 -#define MX6DL_PAD_GPIO_8__UART2_TX_DATA 0x23c 0x60c 0x000 0x4 0x0 -#define MX6DL_PAD_GPIO_8__GPIO1_IO08 0x23c 0x60c 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_8__SPDIF_SR_CLK 0x23c 0x60c 0x000 0x6 0x0 -#define MX6DL_PAD_GPIO_8__USB_OTG_PWR_CTL_WAKE 0x23c 0x60c 0x000 0x7 0x0 -#define MX6DL_PAD_GPIO_8__I2C4_SDA 0x23c 0x60c 0x884 0x8 0x1 -#define MX6DL_PAD_GPIO_9__ESAI_RX_FS 0x240 0x610 0x82c 0x0 0x1 -#define MX6DL_PAD_GPIO_9__WDOG1_B 0x240 0x610 0x000 0x1 0x0 -#define MX6DL_PAD_GPIO_9__KEY_COL6 0x240 0x610 0x8c4 0x2 0x1 -#define MX6DL_PAD_GPIO_9__CCM_REF_EN_B 0x240 0x610 0x000 0x3 0x0 -#define MX6DL_PAD_GPIO_9__PWM1_OUT 0x240 0x610 0x000 0x4 0x0 -#define MX6DL_PAD_GPIO_9__GPIO1_IO09 0x240 0x610 0x000 0x5 0x0 -#define MX6DL_PAD_GPIO_9__SD1_WP 0x240 0x610 0x92c 0x6 0x1 -#define MX6DL_PAD_KEY_COL0__ECSPI1_SCLK 0x244 0x62c 0x7d8 0x0 0x3 -#define MX6DL_PAD_KEY_COL0__ENET_RX_DATA3 0x244 0x62c 0x824 0x1 0x0 -#define MX6DL_PAD_KEY_COL0__AUD5_TXC 0x244 0x62c 0x7c0 0x2 0x1 -#define MX6DL_PAD_KEY_COL0__KEY_COL0 0x244 0x62c 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_COL0__UART4_TX_DATA 0x244 0x62c 0x000 0x4 0x0 -#define MX6DL_PAD_KEY_COL0__UART4_RX_DATA 0x244 0x62c 0x914 0x4 0x2 -#define MX6DL_PAD_KEY_COL0__GPIO4_IO06 0x244 0x62c 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_COL0__DCIC1_OUT 0x244 0x62c 0x000 0x6 0x0 -#define MX6DL_PAD_KEY_COL1__ECSPI1_MISO 0x248 0x630 0x7dc 0x0 0x3 -#define MX6DL_PAD_KEY_COL1__ENET_MDIO 0x248 0x630 0x810 0x1 0x1 -#define MX6DL_PAD_KEY_COL1__AUD5_TXFS 0x248 0x630 0x7c4 0x2 0x1 -#define MX6DL_PAD_KEY_COL1__KEY_COL1 0x248 0x630 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_COL1__UART5_TX_DATA 0x248 0x630 0x000 0x4 0x0 -#define MX6DL_PAD_KEY_COL1__UART5_RX_DATA 0x248 0x630 0x91c 0x4 0x2 -#define MX6DL_PAD_KEY_COL1__GPIO4_IO08 0x248 0x630 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_COL1__SD1_VSELECT 0x248 0x630 0x000 0x6 0x0 -#define MX6DL_PAD_KEY_COL2__ECSPI1_SS1 0x24c 0x634 0x7e8 0x0 0x2 -#define MX6DL_PAD_KEY_COL2__ENET_RX_DATA2 0x24c 0x634 0x820 0x1 0x0 -#define MX6DL_PAD_KEY_COL2__FLEXCAN1_TX 0x24c 0x634 0x000 0x2 0x0 -#define MX6DL_PAD_KEY_COL2__KEY_COL2 0x24c 0x634 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_COL2__ENET_MDC 0x24c 0x634 0x000 0x4 0x0 -#define MX6DL_PAD_KEY_COL2__GPIO4_IO10 0x24c 0x634 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_COL2__USB_H1_PWR_CTL_WAKE 0x24c 0x634 0x000 0x6 0x0 -#define MX6DL_PAD_KEY_COL3__ECSPI1_SS3 0x250 0x638 0x7f0 0x0 0x1 -#define MX6DL_PAD_KEY_COL3__ENET_CRS 0x250 0x638 0x000 0x1 0x0 -#define MX6DL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x250 0x638 0x860 0x2 0x1 -#define MX6DL_PAD_KEY_COL3__KEY_COL3 0x250 0x638 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_COL3__I2C2_SCL 0x250 0x638 0x870 0x4 0x1 -#define MX6DL_PAD_KEY_COL3__GPIO4_IO12 0x250 0x638 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_COL3__SPDIF_IN 0x250 0x638 0x8f0 0x6 0x3 -#define MX6DL_PAD_KEY_COL4__FLEXCAN2_TX 0x254 0x63c 0x000 0x0 0x0 -#define MX6DL_PAD_KEY_COL4__IPU1_SISG4 0x254 0x63c 0x000 0x1 0x0 -#define MX6DL_PAD_KEY_COL4__USB_OTG_OC 0x254 0x63c 0x920 0x2 0x1 -#define MX6DL_PAD_KEY_COL4__KEY_COL4 0x254 0x63c 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_COL4__UART5_RTS_B 0x254 0x63c 0x918 0x4 0x2 -#define MX6DL_PAD_KEY_COL4__UART5_CTS_B 0x254 0x63c 0x000 0x4 0x0 -#define MX6DL_PAD_KEY_COL4__GPIO4_IO14 0x254 0x63c 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_ROW0__ECSPI1_MOSI 0x258 0x640 0x7e0 0x0 0x3 -#define MX6DL_PAD_KEY_ROW0__ENET_TX_DATA3 0x258 0x640 0x000 0x1 0x0 -#define MX6DL_PAD_KEY_ROW0__AUD5_TXD 0x258 0x640 0x7b4 0x2 0x1 -#define MX6DL_PAD_KEY_ROW0__KEY_ROW0 0x258 0x640 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_ROW0__UART4_RX_DATA 0x258 0x640 0x914 0x4 0x3 -#define MX6DL_PAD_KEY_ROW0__UART4_TX_DATA 0x258 0x640 0x000 0x4 0x0 -#define MX6DL_PAD_KEY_ROW0__GPIO4_IO07 0x258 0x640 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_ROW0__DCIC2_OUT 0x258 0x640 0x000 0x6 0x0 -#define MX6DL_PAD_KEY_ROW1__ECSPI1_SS0 0x25c 0x644 0x7e4 0x0 0x3 -#define MX6DL_PAD_KEY_ROW1__ENET_COL 0x25c 0x644 0x000 0x1 0x0 -#define MX6DL_PAD_KEY_ROW1__AUD5_RXD 0x25c 0x644 0x7b0 0x2 0x1 -#define MX6DL_PAD_KEY_ROW1__KEY_ROW1 0x25c 0x644 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_ROW1__UART5_RX_DATA 0x25c 0x644 0x91c 0x4 0x3 -#define MX6DL_PAD_KEY_ROW1__UART5_TX_DATA 0x25c 0x644 0x000 0x4 0x0 -#define MX6DL_PAD_KEY_ROW1__GPIO4_IO09 0x25c 0x644 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_ROW1__SD2_VSELECT 0x25c 0x644 0x000 0x6 0x0 -#define MX6DL_PAD_KEY_ROW2__ECSPI1_SS2 0x260 0x648 0x7ec 0x0 0x1 -#define MX6DL_PAD_KEY_ROW2__ENET_TX_DATA2 0x260 0x648 0x000 0x1 0x0 -#define MX6DL_PAD_KEY_ROW2__FLEXCAN1_RX 0x260 0x648 0x7c8 0x2 0x1 -#define MX6DL_PAD_KEY_ROW2__KEY_ROW2 0x260 0x648 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_ROW2__SD2_VSELECT 0x260 0x648 0x000 0x4 0x0 -#define MX6DL_PAD_KEY_ROW2__GPIO4_IO11 0x260 0x648 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x260 0x648 0x85c 0x6 0x1 -#define MX6DL_PAD_KEY_ROW3__ASRC_EXT_CLK 0x264 0x64c 0x794 0x1 0x2 -#define MX6DL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x264 0x64c 0x864 0x2 0x1 -#define MX6DL_PAD_KEY_ROW3__KEY_ROW3 0x264 0x64c 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_ROW3__I2C2_SDA 0x264 0x64c 0x874 0x4 0x1 -#define MX6DL_PAD_KEY_ROW3__GPIO4_IO13 0x264 0x64c 0x000 0x5 0x0 -#define MX6DL_PAD_KEY_ROW3__SD1_VSELECT 0x264 0x64c 0x000 0x6 0x0 -#define MX6DL_PAD_KEY_ROW4__FLEXCAN2_RX 0x268 0x650 0x7cc 0x0 0x0 -#define MX6DL_PAD_KEY_ROW4__IPU1_SISG5 0x268 0x650 0x000 0x1 0x0 -#define MX6DL_PAD_KEY_ROW4__USB_OTG_PWR 0x268 0x650 0x000 0x2 0x0 -#define MX6DL_PAD_KEY_ROW4__KEY_ROW4 0x268 0x650 0x000 0x3 0x0 -#define MX6DL_PAD_KEY_ROW4__UART5_CTS_B 0x268 0x650 0x000 0x4 0x0 -#define MX6DL_PAD_KEY_ROW4__UART5_RTS_B 0x268 0x650 0x918 0x4 0x3 -#define MX6DL_PAD_KEY_ROW4__GPIO4_IO15 0x268 0x650 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_ALE__NAND_ALE 0x26c 0x654 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_ALE__SD4_RESET 0x26c 0x654 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_ALE__GPIO6_IO08 0x26c 0x654 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_CLE__NAND_CLE 0x270 0x658 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_CLE__GPIO6_IO07 0x270 0x658 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_CS0__NAND_CE0_B 0x274 0x65c 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_CS0__GPIO6_IO11 0x274 0x65c 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_CS1__NAND_CE1_B 0x278 0x660 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_CS1__SD4_VSELECT 0x278 0x660 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_CS1__SD3_VSELECT 0x278 0x660 0x000 0x2 0x0 -#define MX6DL_PAD_NANDF_CS1__GPIO6_IO14 0x278 0x660 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_CS2__NAND_CE2_B 0x27c 0x664 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_CS2__IPU1_SISG0 0x27c 0x664 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_CS2__ESAI_TX0 0x27c 0x664 0x844 0x2 0x1 -#define MX6DL_PAD_NANDF_CS2__EIM_CRE 0x27c 0x664 0x000 0x3 0x0 -#define MX6DL_PAD_NANDF_CS2__CCM_CLKO2 0x27c 0x664 0x000 0x4 0x0 -#define MX6DL_PAD_NANDF_CS2__GPIO6_IO15 0x27c 0x664 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_CS3__NAND_CE3_B 0x280 0x668 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_CS3__IPU1_SISG1 0x280 0x668 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_CS3__ESAI_TX1 0x280 0x668 0x848 0x2 0x1 -#define MX6DL_PAD_NANDF_CS3__EIM_ADDR26 0x280 0x668 0x000 0x3 0x0 -#define MX6DL_PAD_NANDF_CS3__GPIO6_IO16 0x280 0x668 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_CS3__I2C4_SDA 0x280 0x668 0x884 0x9 0x2 -#define MX6DL_PAD_NANDF_D0__NAND_DATA00 0x284 0x66c 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_D0__SD1_DATA4 0x284 0x66c 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_D0__GPIO2_IO00 0x284 0x66c 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_D1__NAND_DATA01 0x288 0x670 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_D1__SD1_DATA5 0x288 0x670 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_D1__GPIO2_IO01 0x288 0x670 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_D2__NAND_DATA02 0x28c 0x674 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_D2__SD1_DATA6 0x28c 0x674 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_D2__GPIO2_IO02 0x28c 0x674 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_D3__NAND_DATA03 0x290 0x678 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_D3__SD1_DATA7 0x290 0x678 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_D3__GPIO2_IO03 0x290 0x678 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_D4__NAND_DATA04 0x294 0x67c 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_D4__SD2_DATA4 0x294 0x67c 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_D4__GPIO2_IO04 0x294 0x67c 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_D5__NAND_DATA05 0x298 0x680 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_D5__SD2_DATA5 0x298 0x680 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_D5__GPIO2_IO05 0x298 0x680 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_D6__NAND_DATA06 0x29c 0x684 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_D6__SD2_DATA6 0x29c 0x684 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_D6__GPIO2_IO06 0x29c 0x684 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_D7__NAND_DATA07 0x2a0 0x688 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_D7__SD2_DATA7 0x2a0 0x688 0x000 0x1 0x0 -#define MX6DL_PAD_NANDF_D7__GPIO2_IO07 0x2a0 0x688 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_RB0__NAND_READY_B 0x2a4 0x68c 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_RB0__GPIO6_IO10 0x2a4 0x68c 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_WP_B__NAND_WP_B 0x2a8 0x690 0x000 0x0 0x0 -#define MX6DL_PAD_NANDF_WP_B__GPIO6_IO09 0x2a8 0x690 0x000 0x5 0x0 -#define MX6DL_PAD_NANDF_WP_B__I2C4_SCL 0x2a8 0x690 0x880 0x9 0x2 -#define MX6DL_PAD_RGMII_RD0__HSI_RX_READY 0x2ac 0x694 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_RD0__RGMII_RD0 0x2ac 0x694 0x818 0x1 0x1 -#define MX6DL_PAD_RGMII_RD0__GPIO6_IO25 0x2ac 0x694 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_RD1__HSI_TX_FLAG 0x2b0 0x698 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_RD1__RGMII_RD1 0x2b0 0x698 0x81c 0x1 0x1 -#define MX6DL_PAD_RGMII_RD1__GPIO6_IO27 0x2b0 0x698 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_RD2__HSI_TX_DATA 0x2b4 0x69c 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_RD2__RGMII_RD2 0x2b4 0x69c 0x820 0x1 0x1 -#define MX6DL_PAD_RGMII_RD2__GPIO6_IO28 0x2b4 0x69c 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_RD3__HSI_TX_WAKE 0x2b8 0x6a0 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_RD3__RGMII_RD3 0x2b8 0x6a0 0x824 0x1 0x1 -#define MX6DL_PAD_RGMII_RD3__GPIO6_IO29 0x2b8 0x6a0 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_RX_CTL__USB_H3_DATA 0x2bc 0x6a4 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x2bc 0x6a4 0x828 0x1 0x1 -#define MX6DL_PAD_RGMII_RX_CTL__GPIO6_IO24 0x2bc 0x6a4 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_RXC__USB_H3_STROBE 0x2c0 0x6a8 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_RXC__RGMII_RXC 0x2c0 0x6a8 0x814 0x1 0x1 -#define MX6DL_PAD_RGMII_RXC__GPIO6_IO30 0x2c0 0x6a8 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_TD0__HSI_TX_READY 0x2c4 0x6ac 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_TD0__RGMII_TD0 0x2c4 0x6ac 0x000 0x1 0x0 -#define MX6DL_PAD_RGMII_TD0__GPIO6_IO20 0x2c4 0x6ac 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_TD1__HSI_RX_FLAG 0x2c8 0x6b0 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_TD1__RGMII_TD1 0x2c8 0x6b0 0x000 0x1 0x0 -#define MX6DL_PAD_RGMII_TD1__GPIO6_IO21 0x2c8 0x6b0 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_TD2__HSI_RX_DATA 0x2cc 0x6b4 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_TD2__RGMII_TD2 0x2cc 0x6b4 0x000 0x1 0x0 -#define MX6DL_PAD_RGMII_TD2__GPIO6_IO22 0x2cc 0x6b4 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_TD3__HSI_RX_WAKE 0x2d0 0x6b8 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_TD3__RGMII_TD3 0x2d0 0x6b8 0x000 0x1 0x0 -#define MX6DL_PAD_RGMII_TD3__GPIO6_IO23 0x2d0 0x6b8 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x2d4 0x6bc 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x2d4 0x6bc 0x000 0x1 0x0 -#define MX6DL_PAD_RGMII_TX_CTL__GPIO6_IO26 0x2d4 0x6bc 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_TX_CTL__ENET_REF_CLK 0x2d4 0x6bc 0x80c 0x7 0x1 -#define MX6DL_PAD_RGMII_TXC__USB_H2_DATA 0x2d8 0x6c0 0x000 0x0 0x0 -#define MX6DL_PAD_RGMII_TXC__RGMII_TXC 0x2d8 0x6c0 0x000 0x1 0x0 -#define MX6DL_PAD_RGMII_TXC__SPDIF_EXT_CLK 0x2d8 0x6c0 0x8f4 0x2 0x1 -#define MX6DL_PAD_RGMII_TXC__GPIO6_IO19 0x2d8 0x6c0 0x000 0x5 0x0 -#define MX6DL_PAD_RGMII_TXC__XTALOSC_REF_CLK_24M 0x2d8 0x6c0 0x000 0x7 0x0 -#define MX6DL_PAD_SD1_CLK__SD1_CLK 0x2dc 0x6c4 0x928 0x0 0x1 -#define MX6DL_PAD_SD1_CLK__GPT_CLKIN 0x2dc 0x6c4 0x000 0x3 0x0 -#define MX6DL_PAD_SD1_CLK__GPIO1_IO20 0x2dc 0x6c4 0x000 0x5 0x0 -#define MX6DL_PAD_SD1_CMD__SD1_CMD 0x2e0 0x6c8 0x000 0x0 0x0 -#define MX6DL_PAD_SD1_CMD__PWM4_OUT 0x2e0 0x6c8 0x000 0x2 0x0 -#define MX6DL_PAD_SD1_CMD__GPT_COMPARE1 0x2e0 0x6c8 0x000 0x3 0x0 -#define MX6DL_PAD_SD1_CMD__GPIO1_IO18 0x2e0 0x6c8 0x000 0x5 0x0 -#define MX6DL_PAD_SD1_DAT0__SD1_DATA0 0x2e4 0x6cc 0x000 0x0 0x0 -#define MX6DL_PAD_SD1_DAT0__GPT_CAPTURE1 0x2e4 0x6cc 0x000 0x3 0x0 -#define MX6DL_PAD_SD1_DAT0__GPIO1_IO16 0x2e4 0x6cc 0x000 0x5 0x0 -#define MX6DL_PAD_SD1_DAT1__SD1_DATA1 0x2e8 0x6d0 0x000 0x0 0x0 -#define MX6DL_PAD_SD1_DAT1__PWM3_OUT 0x2e8 0x6d0 0x000 0x2 0x0 -#define MX6DL_PAD_SD1_DAT1__GPT_CAPTURE2 0x2e8 0x6d0 0x000 0x3 0x0 -#define MX6DL_PAD_SD1_DAT1__GPIO1_IO17 0x2e8 0x6d0 0x000 0x5 0x0 -#define MX6DL_PAD_SD1_DAT2__SD1_DATA2 0x2ec 0x6d4 0x000 0x0 0x0 -#define MX6DL_PAD_SD1_DAT2__GPT_COMPARE2 0x2ec 0x6d4 0x000 0x2 0x0 -#define MX6DL_PAD_SD1_DAT2__PWM2_OUT 0x2ec 0x6d4 0x000 0x3 0x0 -#define MX6DL_PAD_SD1_DAT2__WDOG1_B 0x2ec 0x6d4 0x000 0x4 0x0 -#define MX6DL_PAD_SD1_DAT2__GPIO1_IO19 0x2ec 0x6d4 0x000 0x5 0x0 -#define MX6DL_PAD_SD1_DAT2__WDOG1_RESET_B_DEB 0x2ec 0x6d4 0x000 0x6 0x0 -#define MX6DL_PAD_SD1_DAT3__SD1_DATA3 0x2f0 0x6d8 0x000 0x0 0x0 -#define MX6DL_PAD_SD1_DAT3__GPT_COMPARE3 0x2f0 0x6d8 0x000 0x2 0x0 -#define MX6DL_PAD_SD1_DAT3__PWM1_OUT 0x2f0 0x6d8 0x000 0x3 0x0 -#define MX6DL_PAD_SD1_DAT3__WDOG2_B 0x2f0 0x6d8 0x000 0x4 0x0 -#define MX6DL_PAD_SD1_DAT3__GPIO1_IO21 0x2f0 0x6d8 0x000 0x5 0x0 -#define MX6DL_PAD_SD1_DAT3__WDOG2_RESET_B_DEB 0x2f0 0x6d8 0x000 0x6 0x0 -#define MX6DL_PAD_SD2_CLK__SD2_CLK 0x2f4 0x6dc 0x930 0x0 0x1 -#define MX6DL_PAD_SD2_CLK__KEY_COL5 0x2f4 0x6dc 0x8c0 0x2 0x3 -#define MX6DL_PAD_SD2_CLK__AUD4_RXFS 0x2f4 0x6dc 0x7a4 0x3 0x1 -#define MX6DL_PAD_SD2_CLK__GPIO1_IO10 0x2f4 0x6dc 0x000 0x5 0x0 -#define MX6DL_PAD_SD2_CMD__SD2_CMD 0x2f8 0x6e0 0x000 0x0 0x0 -#define MX6DL_PAD_SD2_CMD__KEY_ROW5 0x2f8 0x6e0 0x8cc 0x2 0x2 -#define MX6DL_PAD_SD2_CMD__AUD4_RXC 0x2f8 0x6e0 0x7a0 0x3 0x1 -#define MX6DL_PAD_SD2_CMD__GPIO1_IO11 0x2f8 0x6e0 0x000 0x5 0x0 -#define MX6DL_PAD_SD2_DAT0__SD2_DATA0 0x2fc 0x6e4 0x000 0x0 0x0 -#define MX6DL_PAD_SD2_DAT0__AUD4_RXD 0x2fc 0x6e4 0x798 0x3 0x1 -#define MX6DL_PAD_SD2_DAT0__KEY_ROW7 0x2fc 0x6e4 0x8d4 0x4 0x2 -#define MX6DL_PAD_SD2_DAT0__GPIO1_IO15 0x2fc 0x6e4 0x000 0x5 0x0 -#define MX6DL_PAD_SD2_DAT0__DCIC2_OUT 0x2fc 0x6e4 0x000 0x6 0x0 -#define MX6DL_PAD_SD2_DAT1__SD2_DATA1 0x300 0x6e8 0x000 0x0 0x0 -#define MX6DL_PAD_SD2_DAT1__EIM_CS2_B 0x300 0x6e8 0x000 0x2 0x0 -#define MX6DL_PAD_SD2_DAT1__AUD4_TXFS 0x300 0x6e8 0x7ac 0x3 0x1 -#define MX6DL_PAD_SD2_DAT1__KEY_COL7 0x300 0x6e8 0x8c8 0x4 0x2 -#define MX6DL_PAD_SD2_DAT1__GPIO1_IO14 0x300 0x6e8 0x000 0x5 0x0 -#define MX6DL_PAD_SD2_DAT2__SD2_DATA2 0x304 0x6ec 0x000 0x0 0x0 -#define MX6DL_PAD_SD2_DAT2__EIM_CS3_B 0x304 0x6ec 0x000 0x2 0x0 -#define MX6DL_PAD_SD2_DAT2__AUD4_TXD 0x304 0x6ec 0x79c 0x3 0x1 -#define MX6DL_PAD_SD2_DAT2__KEY_ROW6 0x304 0x6ec 0x8d0 0x4 0x2 -#define MX6DL_PAD_SD2_DAT2__GPIO1_IO13 0x304 0x6ec 0x000 0x5 0x0 -#define MX6DL_PAD_SD2_DAT3__SD2_DATA3 0x308 0x6f0 0x000 0x0 0x0 -#define MX6DL_PAD_SD2_DAT3__KEY_COL6 0x308 0x6f0 0x8c4 0x2 0x2 -#define MX6DL_PAD_SD2_DAT3__AUD4_TXC 0x308 0x6f0 0x7a8 0x3 0x1 -#define MX6DL_PAD_SD2_DAT3__GPIO1_IO12 0x308 0x6f0 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_CLK__SD3_CLK 0x30c 0x6f4 0x934 0x0 0x1 -#define MX6DL_PAD_SD3_CLK__UART2_RTS_B 0x30c 0x6f4 0x900 0x1 0x2 -#define MX6DL_PAD_SD3_CLK__UART2_CTS_B 0x30c 0x6f4 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_CLK__FLEXCAN1_RX 0x30c 0x6f4 0x7c8 0x2 0x2 -#define MX6DL_PAD_SD3_CLK__GPIO7_IO03 0x30c 0x6f4 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_CMD__SD3_CMD 0x310 0x6f8 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_CMD__UART2_CTS_B 0x310 0x6f8 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_CMD__UART2_RTS_B 0x310 0x6f8 0x900 0x1 0x3 -#define MX6DL_PAD_SD3_CMD__FLEXCAN1_TX 0x310 0x6f8 0x000 0x2 0x0 -#define MX6DL_PAD_SD3_CMD__GPIO7_IO02 0x310 0x6f8 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_DAT0__SD3_DATA0 0x314 0x6fc 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_DAT0__UART1_CTS_B 0x314 0x6fc 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_DAT0__UART1_RTS_B 0x314 0x6fc 0x8f8 0x1 0x2 -#define MX6DL_PAD_SD3_DAT0__FLEXCAN2_TX 0x314 0x6fc 0x000 0x2 0x0 -#define MX6DL_PAD_SD3_DAT0__GPIO7_IO04 0x314 0x6fc 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_DAT1__SD3_DATA1 0x318 0x700 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_DAT1__UART1_RTS_B 0x318 0x700 0x8f8 0x1 0x3 -#define MX6DL_PAD_SD3_DAT1__UART1_CTS_B 0x318 0x700 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_DAT1__FLEXCAN2_RX 0x318 0x700 0x7cc 0x2 0x1 -#define MX6DL_PAD_SD3_DAT1__GPIO7_IO05 0x318 0x700 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_DAT2__SD3_DATA2 0x31c 0x704 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_DAT2__GPIO7_IO06 0x31c 0x704 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_DAT3__SD3_DATA3 0x320 0x708 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_DAT3__UART3_CTS_B 0x320 0x708 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_DAT3__UART3_RTS_B 0x320 0x708 0x908 0x1 0x4 -#define MX6DL_PAD_SD3_DAT3__GPIO7_IO07 0x320 0x708 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_DAT4__SD3_DATA4 0x324 0x70c 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_DAT4__UART2_RX_DATA 0x324 0x70c 0x904 0x1 0x4 -#define MX6DL_PAD_SD3_DAT4__UART2_TX_DATA 0x324 0x70c 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_DAT4__GPIO7_IO01 0x324 0x70c 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_DAT5__SD3_DATA5 0x328 0x710 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_DAT5__UART2_TX_DATA 0x328 0x710 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_DAT5__UART2_RX_DATA 0x328 0x710 0x904 0x1 0x5 -#define MX6DL_PAD_SD3_DAT5__GPIO7_IO00 0x328 0x710 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_DAT6__SD3_DATA6 0x32c 0x714 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_DAT6__UART1_RX_DATA 0x32c 0x714 0x8fc 0x1 0x2 -#define MX6DL_PAD_SD3_DAT6__UART1_TX_DATA 0x32c 0x714 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_DAT6__GPIO6_IO18 0x32c 0x714 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_DAT7__SD3_DATA7 0x330 0x718 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_DAT7__UART1_TX_DATA 0x330 0x718 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_DAT7__UART1_RX_DATA 0x330 0x718 0x8fc 0x1 0x3 -#define MX6DL_PAD_SD3_DAT7__GPIO6_IO17 0x330 0x718 0x000 0x5 0x0 -#define MX6DL_PAD_SD3_RST__SD3_RESET 0x334 0x71c 0x000 0x0 0x0 -#define MX6DL_PAD_SD3_RST__UART3_RTS_B 0x334 0x71c 0x908 0x1 0x5 -#define MX6DL_PAD_SD3_RST__UART3_CTS_B 0x334 0x71c 0x000 0x1 0x0 -#define MX6DL_PAD_SD3_RST__GPIO7_IO08 0x334 0x71c 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_CLK__SD4_CLK 0x338 0x720 0x938 0x0 0x1 -#define MX6DL_PAD_SD4_CLK__NAND_WE_B 0x338 0x720 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_CLK__UART3_RX_DATA 0x338 0x720 0x90c 0x2 0x2 -#define MX6DL_PAD_SD4_CLK__UART3_TX_DATA 0x338 0x720 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_CLK__GPIO7_IO10 0x338 0x720 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_CMD__SD4_CMD 0x33c 0x724 0x000 0x0 0x0 -#define MX6DL_PAD_SD4_CMD__NAND_RE_B 0x33c 0x724 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_CMD__UART3_TX_DATA 0x33c 0x724 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_CMD__UART3_RX_DATA 0x33c 0x724 0x90c 0x2 0x3 -#define MX6DL_PAD_SD4_CMD__GPIO7_IO09 0x33c 0x724 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_DAT0__SD4_DATA0 0x340 0x728 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_DAT0__NAND_DQS 0x340 0x728 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_DAT0__GPIO2_IO08 0x340 0x728 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_DAT1__SD4_DATA1 0x344 0x72c 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_DAT1__PWM3_OUT 0x344 0x72c 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_DAT1__GPIO2_IO09 0x344 0x72c 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_DAT2__SD4_DATA2 0x348 0x730 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_DAT2__PWM4_OUT 0x348 0x730 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_DAT2__GPIO2_IO10 0x348 0x730 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_DAT3__SD4_DATA3 0x34c 0x734 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_DAT3__GPIO2_IO11 0x34c 0x734 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_DAT4__SD4_DATA4 0x350 0x738 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_DAT4__UART2_RX_DATA 0x350 0x738 0x904 0x2 0x6 -#define MX6DL_PAD_SD4_DAT4__UART2_TX_DATA 0x350 0x738 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_DAT4__GPIO2_IO12 0x350 0x738 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_DAT5__SD4_DATA5 0x354 0x73c 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_DAT5__UART2_RTS_B 0x354 0x73c 0x900 0x2 0x4 -#define MX6DL_PAD_SD4_DAT5__UART2_CTS_B 0x354 0x73c 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_DAT5__GPIO2_IO13 0x354 0x73c 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_DAT6__SD4_DATA6 0x358 0x740 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_DAT6__UART2_CTS_B 0x358 0x740 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_DAT6__UART2_RTS_B 0x358 0x740 0x900 0x2 0x5 -#define MX6DL_PAD_SD4_DAT6__GPIO2_IO14 0x358 0x740 0x000 0x5 0x0 -#define MX6DL_PAD_SD4_DAT7__SD4_DATA7 0x35c 0x744 0x000 0x1 0x0 -#define MX6DL_PAD_SD4_DAT7__UART2_TX_DATA 0x35c 0x744 0x000 0x2 0x0 -#define MX6DL_PAD_SD4_DAT7__UART2_RX_DATA 0x35c 0x744 0x904 0x2 0x7 -#define MX6DL_PAD_SD4_DAT7__GPIO2_IO15 0x35c 0x744 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10 0x04c 0x360 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT10__AUD3_RXC 0x04c 0x360 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT10__ECSPI2_MISO 0x04c 0x360 0x7f8 0x2 0x0 +#define MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x04c 0x360 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT10__UART1_RX_DATA 0x04c 0x360 0x8fc 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT10__GPIO5_IO28 0x04c 0x360 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT10__ARM_TRACE07 0x04c 0x360 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11 0x050 0x364 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT11__AUD3_RXFS 0x050 0x364 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT11__ECSPI2_SS0 0x050 0x364 0x800 0x2 0x0 +#define MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x050 0x364 0x8fc 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT11__UART1_TX_DATA 0x050 0x364 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT11__GPIO5_IO29 0x050 0x364 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT11__ARM_TRACE08 0x050 0x364 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x054 0x368 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT12__EIM_DATA08 0x054 0x368 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x054 0x368 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT12__UART4_RX_DATA 0x054 0x368 0x914 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT12__GPIO5_IO30 0x054 0x368 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT12__ARM_TRACE09 0x054 0x368 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x058 0x36c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT13__EIM_DATA09 0x058 0x36c 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x058 0x36c 0x914 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT13__UART4_TX_DATA 0x058 0x36c 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT13__GPIO5_IO31 0x058 0x36c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT13__ARM_TRACE10 0x058 0x36c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x05c 0x370 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT14__EIM_DATA10 0x05c 0x370 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT14__UART5_TX_DATA 0x05c 0x370 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT14__UART5_RX_DATA 0x05c 0x370 0x91c 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x05c 0x370 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT14__ARM_TRACE11 0x05c 0x370 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x060 0x374 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT15__EIM_DATA11 0x060 0x374 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT15__UART5_RX_DATA 0x060 0x374 0x91c 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT15__UART5_TX_DATA 0x060 0x374 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x060 0x374 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT15__ARM_TRACE12 0x060 0x374 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x064 0x378 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT16__EIM_DATA12 0x064 0x378 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x064 0x378 0x910 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT16__UART4_CTS_B 0x064 0x378 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT16__GPIO6_IO02 0x064 0x378 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT16__ARM_TRACE13 0x064 0x378 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x068 0x37c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT17__EIM_DATA13 0x068 0x37c 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x068 0x37c 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT17__UART4_RTS_B 0x068 0x37c 0x910 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03 0x068 0x37c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT17__ARM_TRACE14 0x068 0x37c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x06c 0x380 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT18__EIM_DATA14 0x06c 0x380 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT18__UART5_RTS_B 0x06c 0x380 0x918 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT18__UART5_CTS_B 0x06c 0x380 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x06c 0x380 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT18__ARM_TRACE15 0x06c 0x380 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x070 0x384 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT19__EIM_DATA15 0x070 0x384 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x070 0x384 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT19__UART5_RTS_B 0x070 0x384 0x918 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x070 0x384 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04 0x074 0x388 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT4__EIM_DATA02 0x074 0x388 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT4__ECSPI1_SCLK 0x074 0x388 0x7d8 0x2 0x0 +#define MX6QDL_PAD_CSI0_DAT4__KEY_COL5 0x074 0x388 0x8c0 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x074 0x388 0x000 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT4__GPIO5_IO22 0x074 0x388 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT4__ARM_TRACE01 0x074 0x388 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05 0x078 0x38c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT5__EIM_DATA03 0x078 0x38c 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT5__ECSPI1_MOSI 0x078 0x38c 0x7e0 0x2 0x0 +#define MX6QDL_PAD_CSI0_DAT5__KEY_ROW5 0x078 0x38c 0x8cc 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x078 0x38c 0x000 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT5__GPIO5_IO23 0x078 0x38c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT5__ARM_TRACE02 0x078 0x38c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06 0x07c 0x390 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT6__EIM_DATA04 0x07c 0x390 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT6__ECSPI1_MISO 0x07c 0x390 0x7dc 0x2 0x0 +#define MX6QDL_PAD_CSI0_DAT6__KEY_COL6 0x07c 0x390 0x8c4 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x07c 0x390 0x000 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT6__GPIO5_IO24 0x07c 0x390 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT6__ARM_TRACE03 0x07c 0x390 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07 0x080 0x394 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT7__EIM_DATA05 0x080 0x394 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT7__ECSPI1_SS0 0x080 0x394 0x7e4 0x2 0x0 +#define MX6QDL_PAD_CSI0_DAT7__KEY_ROW6 0x080 0x394 0x8d0 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x080 0x394 0x000 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT7__GPIO5_IO25 0x080 0x394 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT7__ARM_TRACE04 0x080 0x394 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08 0x084 0x398 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT8__EIM_DATA06 0x084 0x398 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT8__ECSPI2_SCLK 0x084 0x398 0x7f4 0x2 0x0 +#define MX6QDL_PAD_CSI0_DAT8__KEY_COL7 0x084 0x398 0x8c8 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x084 0x398 0x86c 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT8__GPIO5_IO26 0x084 0x398 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT8__ARM_TRACE05 0x084 0x398 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09 0x088 0x39c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT9__EIM_DATA07 0x088 0x39c 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT9__ECSPI2_MOSI 0x088 0x39c 0x7fc 0x2 0x0 +#define MX6QDL_PAD_CSI0_DAT9__KEY_ROW7 0x088 0x39c 0x8d4 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x088 0x39c 0x868 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT9__GPIO5_IO27 0x088 0x39c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT9__ARM_TRACE06 0x088 0x39c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x08c 0x3a0 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DATA_EN__EIM_DATA00 0x08c 0x3a0 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x08c 0x3a0 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DATA_EN__ARM_TRACE_CLK 0x08c 0x3a0 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x090 0x3a4 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_MCLK__CCM_CLKO1 0x090 0x3a4 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_MCLK__GPIO5_IO19 0x090 0x3a4 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_MCLK__ARM_TRACE_CTL 0x090 0x3a4 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x094 0x3a8 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18 0x094 0x3a8 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_PIXCLK__ARM_EVENTO 0x094 0x3a8 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x098 0x3ac 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_VSYNC__EIM_DATA01 0x098 0x3ac 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_VSYNC__GPIO5_IO21 0x098 0x3ac 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_VSYNC__ARM_TRACE00 0x098 0x3ac 0x000 0x7 0x0 +#define MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x09c 0x3b0 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_DISP_CLK__LCD_CLK 0x09c 0x3b0 0x000 0x1 0x0 +#define MX6QDL_PAD_DI0_DISP_CLK__GPIO4_IO16 0x09c 0x3b0 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_DISP_CLK__LCD_WR_RWN 0x09c 0x3b0 0x000 0x8 0x0 +#define MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x0a0 0x3b4 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_PIN15__LCD_ENABLE 0x0a0 0x3b4 0x000 0x1 0x0 +#define MX6QDL_PAD_DI0_PIN15__AUD6_TXC 0x0a0 0x3b4 0x000 0x2 0x0 +#define MX6QDL_PAD_DI0_PIN15__GPIO4_IO17 0x0a0 0x3b4 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_PIN15__LCD_RD_E 0x0a0 0x3b4 0x000 0x8 0x0 +#define MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x0a4 0x3b8 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_PIN2__LCD_HSYNC 0x0a4 0x3b8 0x8d8 0x1 0x0 +#define MX6QDL_PAD_DI0_PIN2__AUD6_TXD 0x0a4 0x3b8 0x000 0x2 0x0 +#define MX6QDL_PAD_DI0_PIN2__GPIO4_IO18 0x0a4 0x3b8 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_PIN2__LCD_RS 0x0a4 0x3b8 0x000 0x8 0x0 +#define MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x0a8 0x3bc 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_PIN3__LCD_VSYNC 0x0a8 0x3bc 0x000 0x1 0x0 +#define MX6QDL_PAD_DI0_PIN3__AUD6_TXFS 0x0a8 0x3bc 0x000 0x2 0x0 +#define MX6QDL_PAD_DI0_PIN3__GPIO4_IO19 0x0a8 0x3bc 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_PIN3__LCD_CS 0x0a8 0x3bc 0x000 0x8 0x0 +#define MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x0ac 0x3c0 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_PIN4__LCD_BUSY 0x0ac 0x3c0 0x8d8 0x1 0x1 +#define MX6QDL_PAD_DI0_PIN4__AUD6_RXD 0x0ac 0x3c0 0x000 0x2 0x0 +#define MX6QDL_PAD_DI0_PIN4__SD1_WP 0x0ac 0x3c0 0x92c 0x3 0x0 +#define MX6QDL_PAD_DI0_PIN4__GPIO4_IO20 0x0ac 0x3c0 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_PIN4__LCD_RESET 0x0ac 0x3c0 0x000 0x8 0x0 +#define MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x0b0 0x3c4 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT0__LCD_DATA00 0x0b0 0x3c4 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x0b0 0x3c4 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT0__GPIO4_IO21 0x0b0 0x3c4 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x0b4 0x3c8 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT1__LCD_DATA01 0x0b4 0x3c8 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x0b4 0x3c8 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT1__GPIO4_IO22 0x0b4 0x3c8 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x0b8 0x3cc 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT10__LCD_DATA10 0x0b8 0x3cc 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT10__GPIO4_IO31 0x0b8 0x3cc 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x0bc 0x3d0 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT11__LCD_DATA11 0x0bc 0x3d0 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x0bc 0x3d0 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x0c0 0x3d4 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT12__LCD_DATA12 0x0c0 0x3d4 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT12__GPIO5_IO06 0x0c0 0x3d4 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x0c4 0x3d8 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT13__LCD_DATA13 0x0c4 0x3d8 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT13__AUD5_RXFS 0x0c4 0x3d8 0x7bc 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT13__GPIO5_IO07 0x0c4 0x3d8 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x0c8 0x3dc 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT14__LCD_DATA14 0x0c8 0x3dc 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT14__AUD5_RXC 0x0c8 0x3dc 0x7b8 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT14__GPIO5_IO08 0x0c8 0x3dc 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x0cc 0x3e0 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT15__LCD_DATA15 0x0cc 0x3e0 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT15__ECSPI1_SS1 0x0cc 0x3e0 0x7e8 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT15__ECSPI2_SS1 0x0cc 0x3e0 0x804 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT15__GPIO5_IO09 0x0cc 0x3e0 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x0d0 0x3e4 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT16__LCD_DATA16 0x0d0 0x3e4 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT16__ECSPI2_MOSI 0x0d0 0x3e4 0x7fc 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT16__AUD5_TXC 0x0d0 0x3e4 0x7c0 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT16__SDMA_EXT_EVENT0 0x0d0 0x3e4 0x8e8 0x4 0x0 +#define MX6QDL_PAD_DISP0_DAT16__GPIO5_IO10 0x0d0 0x3e4 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x0d4 0x3e8 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT17__LCD_DATA17 0x0d4 0x3e8 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT17__ECSPI2_MISO 0x0d4 0x3e8 0x7f8 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT17__AUD5_TXD 0x0d4 0x3e8 0x7b4 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT17__SDMA_EXT_EVENT1 0x0d4 0x3e8 0x8ec 0x4 0x0 +#define MX6QDL_PAD_DISP0_DAT17__GPIO5_IO11 0x0d4 0x3e8 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x0d8 0x3ec 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT18__LCD_DATA18 0x0d8 0x3ec 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT18__ECSPI2_SS0 0x0d8 0x3ec 0x800 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT18__AUD5_TXFS 0x0d8 0x3ec 0x7c4 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT18__AUD4_RXFS 0x0d8 0x3ec 0x7a4 0x4 0x0 +#define MX6QDL_PAD_DISP0_DAT18__GPIO5_IO12 0x0d8 0x3ec 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT18__EIM_CS2_B 0x0d8 0x3ec 0x000 0x7 0x0 +#define MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x0dc 0x3f0 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT19__LCD_DATA19 0x0dc 0x3f0 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT19__ECSPI2_SCLK 0x0dc 0x3f0 0x7f4 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x0dc 0x3f0 0x7b0 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT19__AUD4_RXC 0x0dc 0x3f0 0x7a0 0x4 0x0 +#define MX6QDL_PAD_DISP0_DAT19__GPIO5_IO13 0x0dc 0x3f0 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT19__EIM_CS3_B 0x0dc 0x3f0 0x000 0x7 0x0 +#define MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x0e0 0x3f4 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT2__LCD_DATA02 0x0e0 0x3f4 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x0e0 0x3f4 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT2__GPIO4_IO23 0x0e0 0x3f4 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x0e4 0x3f8 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT20__LCD_DATA20 0x0e4 0x3f8 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT20__ECSPI1_SCLK 0x0e4 0x3f8 0x7d8 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT20__AUD4_TXC 0x0e4 0x3f8 0x7a8 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT20__GPIO5_IO14 0x0e4 0x3f8 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x0e8 0x3fc 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT21__LCD_DATA21 0x0e8 0x3fc 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT21__ECSPI1_MOSI 0x0e8 0x3fc 0x7e0 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT21__AUD4_TXD 0x0e8 0x3fc 0x79c 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT21__GPIO5_IO15 0x0e8 0x3fc 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x0ec 0x400 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT22__LCD_DATA22 0x0ec 0x400 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT22__ECSPI1_MISO 0x0ec 0x400 0x7dc 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT22__AUD4_TXFS 0x0ec 0x400 0x7ac 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT22__GPIO5_IO16 0x0ec 0x400 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x0f0 0x404 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT23__LCD_DATA23 0x0f0 0x404 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT23__ECSPI1_SS0 0x0f0 0x404 0x7e4 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT23__AUD4_RXD 0x0f0 0x404 0x798 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT23__GPIO5_IO17 0x0f0 0x404 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x0f4 0x408 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT3__LCD_DATA03 0x0f4 0x408 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT3__ECSPI3_SS0 0x0f4 0x408 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24 0x0f4 0x408 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x0f8 0x40c 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT4__LCD_DATA04 0x0f8 0x40c 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT4__ECSPI3_SS1 0x0f8 0x40c 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT4__GPIO4_IO25 0x0f8 0x40c 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x0fc 0x410 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT5__LCD_DATA05 0x0fc 0x410 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT5__ECSPI3_SS2 0x0fc 0x410 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT5__AUD6_RXFS 0x0fc 0x410 0x000 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT5__GPIO4_IO26 0x0fc 0x410 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x100 0x414 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT6__LCD_DATA06 0x100 0x414 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT6__ECSPI3_SS3 0x100 0x414 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT6__AUD6_RXC 0x100 0x414 0x000 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT6__GPIO4_IO27 0x100 0x414 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x104 0x418 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT7__LCD_DATA07 0x104 0x418 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT7__ECSPI3_RDY 0x104 0x418 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT7__GPIO4_IO28 0x104 0x418 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x108 0x41c 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT8__LCD_DATA08 0x108 0x41c 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT8__PWM1_OUT 0x108 0x41c 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT8__WDOG1_B 0x108 0x41c 0x000 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT8__GPIO4_IO29 0x108 0x41c 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x10c 0x420 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT9__LCD_DATA09 0x10c 0x420 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT9__PWM2_OUT 0x10c 0x420 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT9__WDOG2_B 0x10c 0x420 0x000 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT9__GPIO4_IO30 0x10c 0x420 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A16__EIM_ADDR16 0x110 0x4e0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A16__IPU1_DI1_DISP_CLK 0x110 0x4e0 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A16__IPU1_CSI1_PIXCLK 0x110 0x4e0 0x8b8 0x2 0x0 +#define MX6QDL_PAD_EIM_A16__GPIO2_IO22 0x110 0x4e0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A16__SRC_BOOT_CFG16 0x110 0x4e0 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A16__EPDC_DATA00 0x110 0x4e0 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A17__EIM_ADDR17 0x114 0x4e4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A17__IPU1_DISP1_DATA12 0x114 0x4e4 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A17__IPU1_CSI1_DATA12 0x114 0x4e4 0x890 0x2 0x0 +#define MX6QDL_PAD_EIM_A17__GPIO2_IO21 0x114 0x4e4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A17__SRC_BOOT_CFG17 0x114 0x4e4 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A17__EPDC_PWR_STAT 0x114 0x4e4 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A18__EIM_ADDR18 0x118 0x4e8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A18__IPU1_DISP1_DATA13 0x118 0x4e8 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A18__IPU1_CSI1_DATA13 0x118 0x4e8 0x894 0x2 0x0 +#define MX6QDL_PAD_EIM_A18__GPIO2_IO20 0x118 0x4e8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A18__SRC_BOOT_CFG18 0x118 0x4e8 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A18__EPDC_PWR_CTRL0 0x118 0x4e8 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A19__EIM_ADDR19 0x11c 0x4ec 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A19__IPU1_DISP1_DATA14 0x11c 0x4ec 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A19__IPU1_CSI1_DATA14 0x11c 0x4ec 0x898 0x2 0x0 +#define MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x11c 0x4ec 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A19__SRC_BOOT_CFG19 0x11c 0x4ec 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A19__EPDC_PWR_CTRL1 0x11c 0x4ec 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A20__EIM_ADDR20 0x120 0x4f0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A20__IPU1_DISP1_DATA15 0x120 0x4f0 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A20__IPU1_CSI1_DATA15 0x120 0x4f0 0x89c 0x2 0x0 +#define MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x120 0x4f0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A20__SRC_BOOT_CFG20 0x120 0x4f0 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A20__EPDC_PWR_CTRL2 0x120 0x4f0 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A21__EIM_ADDR21 0x124 0x4f4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A21__IPU1_DISP1_DATA16 0x124 0x4f4 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A21__IPU1_CSI1_DATA16 0x124 0x4f4 0x8a0 0x2 0x0 +#define MX6QDL_PAD_EIM_A21__GPIO2_IO17 0x124 0x4f4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A21__SRC_BOOT_CFG21 0x124 0x4f4 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A21__EPDC_GDCLK 0x124 0x4f4 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A22__EIM_ADDR22 0x128 0x4f8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A22__IPU1_DISP1_DATA17 0x128 0x4f8 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A22__IPU1_CSI1_DATA17 0x128 0x4f8 0x8a4 0x2 0x0 +#define MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x128 0x4f8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A22__SRC_BOOT_CFG22 0x128 0x4f8 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A22__EPDC_GDSP 0x128 0x4f8 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A23__EIM_ADDR23 0x12c 0x4fc 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A23__IPU1_DISP1_DATA18 0x12c 0x4fc 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A23__IPU1_CSI1_DATA18 0x12c 0x4fc 0x8a8 0x2 0x0 +#define MX6QDL_PAD_EIM_A23__IPU1_SISG3 0x12c 0x4fc 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x12c 0x4fc 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A23__SRC_BOOT_CFG23 0x12c 0x4fc 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A23__EPDC_GDOE 0x12c 0x4fc 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A24__EIM_ADDR24 0x130 0x500 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A24__IPU1_DISP1_DATA19 0x130 0x500 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A24__IPU1_CSI1_DATA19 0x130 0x500 0x8ac 0x2 0x0 +#define MX6QDL_PAD_EIM_A24__IPU1_SISG2 0x130 0x500 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_A24__GPIO5_IO04 0x130 0x500 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A24__SRC_BOOT_CFG24 0x130 0x500 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A24__EPDC_GDRL 0x130 0x500 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A25__EIM_ADDR25 0x134 0x504 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A25__ECSPI4_SS1 0x134 0x504 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A25__ECSPI2_RDY 0x134 0x504 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_A25__IPU1_DI1_PIN12 0x134 0x504 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_A25__IPU1_DI0_D1_CS 0x134 0x504 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x134 0x504 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A25__HDMI_TX_CEC_LINE 0x134 0x504 0x85c 0x6 0x0 +#define MX6QDL_PAD_EIM_A25__EPDC_DATA15 0x134 0x504 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_A25__EIM_ACLK_FREERUN 0x134 0x504 0x000 0x9 0x0 +#define MX6QDL_PAD_EIM_BCLK__EIM_BCLK 0x138 0x508 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_BCLK__IPU1_DI1_PIN16 0x138 0x508 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_BCLK__GPIO6_IO31 0x138 0x508 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_BCLK__EPDC_SDCE9 0x138 0x508 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_CS0__EIM_CS0_B 0x13c 0x50c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_CS0__IPU1_DI1_PIN05 0x13c 0x50c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_CS0__ECSPI2_SCLK 0x13c 0x50c 0x7f4 0x2 0x2 +#define MX6QDL_PAD_EIM_CS0__GPIO2_IO23 0x13c 0x50c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_CS0__EPDC_DATA06 0x13c 0x50c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_CS1__EIM_CS1_B 0x140 0x510 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_CS1__IPU1_DI1_PIN06 0x140 0x510 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_CS1__ECSPI2_MOSI 0x140 0x510 0x7fc 0x2 0x2 +#define MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x140 0x510 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_CS1__EPDC_DATA08 0x140 0x510 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D16__EIM_DATA16 0x144 0x514 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x144 0x514 0x7d8 0x1 0x2 +#define MX6QDL_PAD_EIM_D16__IPU1_DI0_PIN05 0x144 0x514 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D16__IPU1_CSI1_DATA18 0x144 0x514 0x8a8 0x3 0x1 +#define MX6QDL_PAD_EIM_D16__HDMI_TX_DDC_SDA 0x144 0x514 0x864 0x4 0x0 +#define MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x144 0x514 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D16__I2C2_SDA 0x144 0x514 0x874 0x6 0x0 +#define MX6QDL_PAD_EIM_D16__EPDC_DATA10 0x144 0x514 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D17__EIM_DATA17 0x148 0x518 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x148 0x518 0x7dc 0x1 0x2 +#define MX6QDL_PAD_EIM_D17__IPU1_DI0_PIN06 0x148 0x518 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D17__IPU1_CSI1_PIXCLK 0x148 0x518 0x8b8 0x3 0x1 +#define MX6QDL_PAD_EIM_D17__DCIC1_OUT 0x148 0x518 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D17__GPIO3_IO17 0x148 0x518 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D17__I2C3_SCL 0x148 0x518 0x878 0x6 0x0 +#define MX6QDL_PAD_EIM_D17__EPDC_VCOM0 0x148 0x518 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D18__EIM_DATA18 0x14c 0x51c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x14c 0x51c 0x7e0 0x1 0x2 +#define MX6QDL_PAD_EIM_D18__IPU1_DI0_PIN07 0x14c 0x51c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D18__IPU1_CSI1_DATA17 0x14c 0x51c 0x8a4 0x3 0x1 +#define MX6QDL_PAD_EIM_D18__IPU1_DI1_D0_CS 0x14c 0x51c 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D18__GPIO3_IO18 0x14c 0x51c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D18__I2C3_SDA 0x14c 0x51c 0x87c 0x6 0x0 +#define MX6QDL_PAD_EIM_D18__EPDC_VCOM1 0x14c 0x51c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D19__EIM_DATA19 0x150 0x520 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D19__ECSPI1_SS1 0x150 0x520 0x7e8 0x1 0x1 +#define MX6QDL_PAD_EIM_D19__IPU1_DI0_PIN08 0x150 0x520 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D19__IPU1_CSI1_DATA16 0x150 0x520 0x8a0 0x3 0x1 +#define MX6QDL_PAD_EIM_D19__UART1_CTS_B 0x150 0x520 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D19__UART1_RTS_B 0x150 0x520 0x8f8 0x4 0x0 +#define MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x150 0x520 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D19__EPIT1_OUT 0x150 0x520 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D19__EPDC_DATA12 0x150 0x520 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D20__EIM_DATA20 0x154 0x524 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D20__ECSPI4_SS0 0x154 0x524 0x808 0x1 0x0 +#define MX6QDL_PAD_EIM_D20__IPU1_DI0_PIN16 0x154 0x524 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D20__IPU1_CSI1_DATA15 0x154 0x524 0x89c 0x3 0x1 +#define MX6QDL_PAD_EIM_D20__UART1_RTS_B 0x154 0x524 0x8f8 0x4 0x1 +#define MX6QDL_PAD_EIM_D20__UART1_CTS_B 0x154 0x524 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x154 0x524 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D20__EPIT2_OUT 0x154 0x524 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D21__EIM_DATA21 0x158 0x528 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D21__ECSPI4_SCLK 0x158 0x528 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D21__IPU1_DI0_PIN17 0x158 0x528 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D21__IPU1_CSI1_DATA11 0x158 0x528 0x88c 0x3 0x0 +#define MX6QDL_PAD_EIM_D21__USB_OTG_OC 0x158 0x528 0x920 0x4 0x0 +#define MX6QDL_PAD_EIM_D21__GPIO3_IO21 0x158 0x528 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D21__I2C1_SCL 0x158 0x528 0x868 0x6 0x1 +#define MX6QDL_PAD_EIM_D21__SPDIF_IN 0x158 0x528 0x8f0 0x7 0x0 +#define MX6QDL_PAD_EIM_D22__EIM_DATA22 0x15c 0x52c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D22__ECSPI4_MISO 0x15c 0x52c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D22__IPU1_DI0_PIN01 0x15c 0x52c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D22__IPU1_CSI1_DATA10 0x15c 0x52c 0x888 0x3 0x0 +#define MX6QDL_PAD_EIM_D22__USB_OTG_PWR 0x15c 0x52c 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x15c 0x52c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D22__SPDIF_OUT 0x15c 0x52c 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D22__EPDC_SDCE6 0x15c 0x52c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D23__EIM_DATA23 0x160 0x530 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D23__IPU1_DI0_D0_CS 0x160 0x530 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D23__UART3_CTS_B 0x160 0x530 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D23__UART3_RTS_B 0x160 0x530 0x908 0x2 0x0 +#define MX6QDL_PAD_EIM_D23__UART1_DCD_B 0x160 0x530 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_D23__IPU1_CSI1_DATA_EN 0x160 0x530 0x8b0 0x4 0x0 +#define MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x160 0x530 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D23__IPU1_DI1_PIN02 0x160 0x530 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D23__IPU1_DI1_PIN14 0x160 0x530 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D23__EPDC_DATA11 0x160 0x530 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D24__EIM_DATA24 0x164 0x534 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D24__ECSPI4_SS2 0x164 0x534 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x164 0x534 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D24__UART3_RX_DATA 0x164 0x534 0x90c 0x2 0x0 +#define MX6QDL_PAD_EIM_D24__ECSPI1_SS2 0x164 0x534 0x7ec 0x3 0x0 +#define MX6QDL_PAD_EIM_D24__ECSPI2_SS2 0x164 0x534 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D24__GPIO3_IO24 0x164 0x534 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D24__AUD5_RXFS 0x164 0x534 0x7bc 0x6 0x1 +#define MX6QDL_PAD_EIM_D24__UART1_DTR_B 0x164 0x534 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D24__EPDC_SDCE7 0x164 0x534 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D25__EIM_DATA25 0x168 0x538 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D25__ECSPI4_SS3 0x168 0x538 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x168 0x538 0x90c 0x2 0x1 +#define MX6QDL_PAD_EIM_D25__UART3_TX_DATA 0x168 0x538 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D25__ECSPI1_SS3 0x168 0x538 0x7f0 0x3 0x0 +#define MX6QDL_PAD_EIM_D25__ECSPI2_SS3 0x168 0x538 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D25__GPIO3_IO25 0x168 0x538 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D25__AUD5_RXC 0x168 0x538 0x7b8 0x6 0x1 +#define MX6QDL_PAD_EIM_D25__UART1_DSR_B 0x168 0x538 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D25__EPDC_SDCE8 0x168 0x538 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D26__EIM_DATA26 0x16c 0x53c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_DI1_PIN11 0x16c 0x53c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_CSI0_DATA01 0x16c 0x53c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_CSI1_DATA14 0x16c 0x53c 0x898 0x3 0x1 +#define MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x16c 0x53c 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D26__UART2_RX_DATA 0x16c 0x53c 0x904 0x4 0x0 +#define MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x16c 0x53c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_SISG2 0x16c 0x53c 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_DISP1_DATA22 0x16c 0x53c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D26__EPDC_SDOED 0x16c 0x53c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D27__EIM_DATA27 0x170 0x540 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_DI1_PIN13 0x170 0x540 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_CSI0_DATA00 0x170 0x540 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_CSI1_DATA13 0x170 0x540 0x894 0x3 0x1 +#define MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x170 0x540 0x904 0x4 0x1 +#define MX6QDL_PAD_EIM_D27__UART2_TX_DATA 0x170 0x540 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D27__GPIO3_IO27 0x170 0x540 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_SISG3 0x170 0x540 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_DISP1_DATA23 0x170 0x540 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D27__EPDC_SDOE 0x170 0x540 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D28__EIM_DATA28 0x174 0x544 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D28__I2C1_SDA 0x174 0x544 0x86c 0x1 0x1 +#define MX6QDL_PAD_EIM_D28__ECSPI4_MOSI 0x174 0x544 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D28__IPU1_CSI1_DATA12 0x174 0x544 0x890 0x3 0x1 +#define MX6QDL_PAD_EIM_D28__UART2_CTS_B 0x174 0x544 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D28__UART2_RTS_B 0x174 0x544 0x900 0x4 0x0 +#define MX6QDL_PAD_EIM_D28__UART2_DTE_CTS_B 0x174 0x544 0x900 0x4 0x0 +#define MX6QDL_PAD_EIM_D28__UART2_DTE_RTS_B 0x174 0x544 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D28__GPIO3_IO28 0x174 0x544 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D28__IPU1_EXT_TRIG 0x174 0x544 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D28__IPU1_DI0_PIN13 0x174 0x544 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D28__EPDC_PWR_CTRL3 0x174 0x544 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D29__EIM_DATA29 0x178 0x548 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D29__IPU1_DI1_PIN15 0x178 0x548 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D29__ECSPI4_SS0 0x178 0x548 0x808 0x2 0x1 +#define MX6QDL_PAD_EIM_D29__UART2_RTS_B 0x178 0x548 0x900 0x4 0x1 +#define MX6QDL_PAD_EIM_D29__UART2_CTS_B 0x178 0x548 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B 0x178 0x548 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D29__UART2_DTE_CTS_B 0x178 0x548 0x900 0x4 0x1 +#define MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x178 0x548 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D29__IPU1_CSI1_VSYNC 0x178 0x548 0x8bc 0x6 0x0 +#define MX6QDL_PAD_EIM_D29__IPU1_DI0_PIN14 0x178 0x548 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D29__EPDC_PWR_WAKE 0x178 0x548 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D30__EIM_DATA30 0x17c 0x54c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D30__IPU1_DISP1_DATA21 0x17c 0x54c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D30__IPU1_DI0_PIN11 0x17c 0x54c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D30__IPU1_CSI0_DATA03 0x17c 0x54c 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_D30__UART3_CTS_B 0x17c 0x54c 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D30__UART3_RTS_B 0x17c 0x54c 0x908 0x4 0x1 +#define MX6QDL_PAD_EIM_D30__GPIO3_IO30 0x17c 0x54c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D30__USB_H1_OC 0x17c 0x54c 0x924 0x6 0x0 +#define MX6QDL_PAD_EIM_D30__EPDC_SDOEZ 0x17c 0x54c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D31__EIM_DATA31 0x180 0x550 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D31__IPU1_DISP1_DATA20 0x180 0x550 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D31__IPU1_DI0_PIN12 0x180 0x550 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D31__IPU1_CSI0_DATA02 0x180 0x550 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_D31__UART3_RTS_B 0x180 0x550 0x908 0x4 0x2 +#define MX6QDL_PAD_EIM_D31__UART3_CTS_B 0x180 0x550 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D31__GPIO3_IO31 0x180 0x550 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D31__USB_H1_PWR 0x180 0x550 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D31__EPDC_SDCLK_P 0x180 0x550 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_D31__EIM_ACLK_FREERUN 0x180 0x550 0x000 0x9 0x0 +#define MX6QDL_PAD_EIM_DA0__EIM_AD00 0x184 0x554 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA0__IPU1_DISP1_DATA09 0x184 0x554 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA0__IPU1_CSI1_DATA09 0x184 0x554 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA0__GPIO3_IO00 0x184 0x554 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA0__SRC_BOOT_CFG00 0x184 0x554 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA0__EPDC_SDCLK_N 0x184 0x554 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA1__EIM_AD01 0x188 0x558 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA1__IPU1_DISP1_DATA08 0x188 0x558 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA1__IPU1_CSI1_DATA08 0x188 0x558 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA1__GPIO3_IO01 0x188 0x558 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA1__SRC_BOOT_CFG01 0x188 0x558 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA1__EPDC_SDLE 0x188 0x558 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA10__EIM_AD10 0x18c 0x55c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA10__IPU1_DI1_PIN15 0x18c 0x55c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA10__IPU1_CSI1_DATA_EN 0x18c 0x55c 0x8b0 0x2 0x1 +#define MX6QDL_PAD_EIM_DA10__GPIO3_IO10 0x18c 0x55c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA10__SRC_BOOT_CFG10 0x18c 0x55c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA10__EPDC_DATA01 0x18c 0x55c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA11__EIM_AD11 0x190 0x560 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA11__IPU1_DI1_PIN02 0x190 0x560 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA11__IPU1_CSI1_HSYNC 0x190 0x560 0x8b4 0x2 0x0 +#define MX6QDL_PAD_EIM_DA11__GPIO3_IO11 0x190 0x560 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA11__SRC_BOOT_CFG11 0x190 0x560 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA11__EPDC_DATA03 0x190 0x560 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA12__EIM_AD12 0x194 0x564 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA12__IPU1_DI1_PIN03 0x194 0x564 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA12__IPU1_CSI1_VSYNC 0x194 0x564 0x8bc 0x2 0x1 +#define MX6QDL_PAD_EIM_DA12__GPIO3_IO12 0x194 0x564 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA12__SRC_BOOT_CFG12 0x194 0x564 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA12__EPDC_DATA02 0x194 0x564 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA13__EIM_AD13 0x198 0x568 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA13__IPU1_DI1_D0_CS 0x198 0x568 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA13__GPIO3_IO13 0x198 0x568 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA13__SRC_BOOT_CFG13 0x198 0x568 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA13__EPDC_DATA13 0x198 0x568 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA14__EIM_AD14 0x19c 0x56c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA14__IPU1_DI1_D1_CS 0x19c 0x56c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA14__GPIO3_IO14 0x19c 0x56c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA14__SRC_BOOT_CFG14 0x19c 0x56c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA14__EPDC_DATA14 0x19c 0x56c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA15__EIM_AD15 0x1a0 0x570 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA15__IPU1_DI1_PIN01 0x1a0 0x570 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA15__IPU1_DI1_PIN04 0x1a0 0x570 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA15__GPIO3_IO15 0x1a0 0x570 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA15__SRC_BOOT_CFG15 0x1a0 0x570 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA15__EPDC_DATA09 0x1a0 0x570 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA2__EIM_AD02 0x1a4 0x574 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA2__IPU1_DISP1_DATA07 0x1a4 0x574 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA2__IPU1_CSI1_DATA07 0x1a4 0x574 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA2__GPIO3_IO02 0x1a4 0x574 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA2__SRC_BOOT_CFG02 0x1a4 0x574 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA2__EPDC_BDR0 0x1a4 0x574 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA3__EIM_AD03 0x1a8 0x578 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA3__IPU1_DISP1_DATA06 0x1a8 0x578 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA3__IPU1_CSI1_DATA06 0x1a8 0x578 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA3__GPIO3_IO03 0x1a8 0x578 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA3__SRC_BOOT_CFG03 0x1a8 0x578 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA3__EPDC_BDR1 0x1a8 0x578 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA4__EIM_AD04 0x1ac 0x57c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA4__IPU1_DISP1_DATA05 0x1ac 0x57c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA4__IPU1_CSI1_DATA05 0x1ac 0x57c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA4__GPIO3_IO04 0x1ac 0x57c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA4__SRC_BOOT_CFG04 0x1ac 0x57c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA4__EPDC_SDCE0 0x1ac 0x57c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA5__EIM_AD05 0x1b0 0x580 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA5__IPU1_DISP1_DATA04 0x1b0 0x580 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA5__IPU1_CSI1_DATA04 0x1b0 0x580 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA5__GPIO3_IO05 0x1b0 0x580 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA5__SRC_BOOT_CFG05 0x1b0 0x580 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA5__EPDC_SDCE1 0x1b0 0x580 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA6__EIM_AD06 0x1b4 0x584 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA6__IPU1_DISP1_DATA03 0x1b4 0x584 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA6__IPU1_CSI1_DATA03 0x1b4 0x584 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA6__GPIO3_IO06 0x1b4 0x584 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA6__SRC_BOOT_CFG06 0x1b4 0x584 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA6__EPDC_SDCE2 0x1b4 0x584 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA7__EIM_AD07 0x1b8 0x588 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA7__IPU1_DISP1_DATA02 0x1b8 0x588 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA7__IPU1_CSI1_DATA02 0x1b8 0x588 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA7__GPIO3_IO07 0x1b8 0x588 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA7__SRC_BOOT_CFG07 0x1b8 0x588 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA7__EPDC_SDCE3 0x1b8 0x588 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA8__EIM_AD08 0x1bc 0x58c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA8__IPU1_DISP1_DATA01 0x1bc 0x58c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA8__IPU1_CSI1_DATA01 0x1bc 0x58c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA8__GPIO3_IO08 0x1bc 0x58c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA8__SRC_BOOT_CFG08 0x1bc 0x58c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA8__EPDC_SDCE4 0x1bc 0x58c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_DA9__EIM_AD09 0x1c0 0x590 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA9__IPU1_DISP1_DATA00 0x1c0 0x590 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA9__IPU1_CSI1_DATA00 0x1c0 0x590 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x1c0 0x590 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA9__SRC_BOOT_CFG09 0x1c0 0x590 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA9__EPDC_SDCE5 0x1c0 0x590 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_EB0__EIM_EB0_B 0x1c4 0x594 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_EB0__IPU1_DISP1_DATA11 0x1c4 0x594 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_EB0__IPU1_CSI1_DATA11 0x1c4 0x594 0x88c 0x2 0x1 +#define MX6QDL_PAD_EIM_EB0__CCM_PMIC_READY 0x1c4 0x594 0x7d4 0x4 0x0 +#define MX6QDL_PAD_EIM_EB0__GPIO2_IO28 0x1c4 0x594 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_EB0__SRC_BOOT_CFG27 0x1c4 0x594 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_EB0__EPDC_PWR_COM 0x1c4 0x594 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_EB1__EIM_EB1_B 0x1c8 0x598 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_EB1__IPU1_DISP1_DATA10 0x1c8 0x598 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_EB1__IPU1_CSI1_DATA10 0x1c8 0x598 0x888 0x2 0x1 +#define MX6QDL_PAD_EIM_EB1__GPIO2_IO29 0x1c8 0x598 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_EB1__SRC_BOOT_CFG28 0x1c8 0x598 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_EB1__EPDC_SDSHR 0x1c8 0x598 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_EB2__EIM_EB2_B 0x1cc 0x59c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_EB2__ECSPI1_SS0 0x1cc 0x59c 0x7e4 0x1 0x2 +#define MX6QDL_PAD_EIM_EB2__IPU1_CSI1_DATA19 0x1cc 0x59c 0x8ac 0x3 0x1 +#define MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x1cc 0x59c 0x860 0x4 0x0 +#define MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x1cc 0x59c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x1cc 0x59c 0x870 0x6 0x0 +#define MX6QDL_PAD_EIM_EB2__SRC_BOOT_CFG30 0x1cc 0x59c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_EB2__EPDC_DATA05 0x1cc 0x59c 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_EB3__EIM_EB3_B 0x1d0 0x5a0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_EB3__ECSPI4_RDY 0x1d0 0x5a0 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1d0 0x5a0 0x908 0x2 0x3 +#define MX6QDL_PAD_EIM_EB3__UART3_CTS_B 0x1d0 0x5a0 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_EB3__UART1_RI_B 0x1d0 0x5a0 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_EB3__IPU1_CSI1_HSYNC 0x1d0 0x5a0 0x8b4 0x4 0x1 +#define MX6QDL_PAD_EIM_EB3__GPIO2_IO31 0x1d0 0x5a0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_EB3__IPU1_DI1_PIN03 0x1d0 0x5a0 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_EB3__SRC_BOOT_CFG31 0x1d0 0x5a0 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_EB3__EPDC_SDCE0 0x1d0 0x5a0 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_EB3__EIM_ACLK_FREERUN 0x1d0 0x5a0 0x000 0x9 0x0 +#define MX6QDL_PAD_EIM_LBA__EIM_LBA_B 0x1d4 0x5a4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_LBA__IPU1_DI1_PIN17 0x1d4 0x5a4 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_LBA__ECSPI2_SS1 0x1d4 0x5a4 0x804 0x2 0x1 +#define MX6QDL_PAD_EIM_LBA__GPIO2_IO27 0x1d4 0x5a4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_LBA__SRC_BOOT_CFG26 0x1d4 0x5a4 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_LBA__EPDC_DATA04 0x1d4 0x5a4 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_OE__EIM_OE_B 0x1d8 0x5a8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_OE__IPU1_DI1_PIN07 0x1d8 0x5a8 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_OE__ECSPI2_MISO 0x1d8 0x5a8 0x7f8 0x2 0x2 +#define MX6QDL_PAD_EIM_OE__GPIO2_IO25 0x1d8 0x5a8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_OE__EPDC_PWR_IRQ 0x1d8 0x5a8 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_RW__EIM_RW 0x1dc 0x5ac 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_RW__IPU1_DI1_PIN08 0x1dc 0x5ac 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_RW__ECSPI2_SS0 0x1dc 0x5ac 0x800 0x2 0x2 +#define MX6QDL_PAD_EIM_RW__GPIO2_IO26 0x1dc 0x5ac 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_RW__SRC_BOOT_CFG29 0x1dc 0x5ac 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_RW__EPDC_DATA07 0x1dc 0x5ac 0x000 0x8 0x0 +#define MX6QDL_PAD_EIM_WAIT__EIM_WAIT_B 0x1e0 0x5b0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_WAIT__EIM_DTACK_B 0x1e0 0x5b0 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_WAIT__GPIO5_IO00 0x1e0 0x5b0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_WAIT__SRC_BOOT_CFG25 0x1e0 0x5b0 0x000 0x7 0x0 +#define MX6QDL_PAD_ENET_CRS_DV__ENET_RX_EN 0x1e4 0x5b4 0x828 0x1 0x0 +#define MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1e4 0x5b4 0x840 0x2 0x0 +#define MX6QDL_PAD_ENET_CRS_DV__SPDIF_EXT_CLK 0x1e4 0x5b4 0x8f4 0x3 0x0 +#define MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x1e4 0x5b4 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_MDC__MLB_DATA 0x1e8 0x5b8 0x8e0 0x0 0x0 +#define MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1e8 0x5b8 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1e8 0x5b8 0x858 0x2 0x0 +#define MX6QDL_PAD_ENET_MDC__ENET_1588_EVENT1_IN 0x1e8 0x5b8 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_MDC__GPIO1_IO31 0x1e8 0x5b8 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1ec 0x5bc 0x810 0x1 0x0 +#define MX6QDL_PAD_ENET_MDIO__ESAI_RX_CLK 0x1ec 0x5bc 0x83c 0x2 0x0 +#define MX6QDL_PAD_ENET_MDIO__ENET_1588_EVENT1_OUT 0x1ec 0x5bc 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_MDIO__GPIO1_IO22 0x1ec 0x5bc 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_MDIO__SPDIF_LOCK 0x1ec 0x5bc 0x000 0x6 0x0 +#define MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1f0 0x5c0 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_REF_CLK__ESAI_RX_FS 0x1f0 0x5c0 0x82c 0x2 0x0 +#define MX6QDL_PAD_ENET_REF_CLK__GPIO1_IO23 0x1f0 0x5c0 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_REF_CLK__SPDIF_SR_CLK 0x1f0 0x5c0 0x000 0x6 0x0 +#define MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x1f4 0x5c4 0x790 0x0 0x0 +#define MX6QDL_PAD_ENET_RX_ER__ENET_RX_ER 0x1f4 0x5c4 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_RX_ER__ESAI_RX_HF_CLK 0x1f4 0x5c4 0x834 0x2 0x0 +#define MX6QDL_PAD_ENET_RX_ER__SPDIF_IN 0x1f4 0x5c4 0x8f0 0x3 0x1 +#define MX6QDL_PAD_ENET_RX_ER__ENET_1588_EVENT2_OUT 0x1f4 0x5c4 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_RX_ER__GPIO1_IO24 0x1f4 0x5c4 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_RXD0__ENET_RX_DATA0 0x1f8 0x5c8 0x818 0x1 0x0 +#define MX6QDL_PAD_ENET_RXD0__ESAI_TX_HF_CLK 0x1f8 0x5c8 0x838 0x2 0x0 +#define MX6QDL_PAD_ENET_RXD0__SPDIF_OUT 0x1f8 0x5c8 0x000 0x3 0x0 +#define MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x1f8 0x5c8 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_RXD1__MLB_SIG 0x1fc 0x5cc 0x8e4 0x0 0x0 +#define MX6QDL_PAD_ENET_RXD1__ENET_RX_DATA1 0x1fc 0x5cc 0x81c 0x1 0x0 +#define MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1fc 0x5cc 0x830 0x2 0x0 +#define MX6QDL_PAD_ENET_RXD1__ENET_1588_EVENT3_OUT 0x1fc 0x5cc 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x1fc 0x5cc 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x200 0x5d0 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x200 0x5d0 0x850 0x2 0x0 +#define MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x200 0x5d0 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_TX_EN__I2C4_SCL 0x200 0x5d0 0x880 0x9 0x0 +#define MX6QDL_PAD_ENET_TXD0__ENET_TX_DATA0 0x204 0x5d4 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x204 0x5d4 0x854 0x2 0x0 +#define MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x204 0x5d4 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_TXD1__MLB_CLK 0x208 0x5d8 0x8dc 0x0 0x0 +#define MX6QDL_PAD_ENET_TXD1__ENET_TX_DATA1 0x208 0x5d8 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_TXD1__ESAI_TX2_RX3 0x208 0x5d8 0x84c 0x2 0x0 +#define MX6QDL_PAD_ENET_TXD1__ENET_1588_EVENT0_IN 0x208 0x5d8 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x208 0x5d8 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_TXD1__I2C4_SDA 0x208 0x5d8 0x884 0x9 0x0 +#define MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x20c 0x5dc 0x000 0x0 0x0 +#define MX6QDL_PAD_GPIO_0__KEY_COL5 0x20c 0x5dc 0x8c0 0x2 0x1 +#define MX6QDL_PAD_GPIO_0__ASRC_EXT_CLK 0x20c 0x5dc 0x794 0x3 0x0 +#define MX6QDL_PAD_GPIO_0__EPIT1_OUT 0x20c 0x5dc 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x20c 0x5dc 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_0__USB_H1_PWR 0x20c 0x5dc 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_0__SNVS_VIO_5 0x20c 0x5dc 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_1__ESAI_RX_CLK 0x210 0x5e0 0x83c 0x0 0x1 +#define MX6QDL_PAD_GPIO_1__WDOG2_B 0x210 0x5e0 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_1__KEY_ROW5 0x210 0x5e0 0x8cc 0x2 0x1 +#define MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x210 0x5e0 0x790 0x3 0x1 +#define MX6QDL_PAD_GPIO_1__PWM2_OUT 0x210 0x5e0 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_1__GPIO1_IO01 0x210 0x5e0 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_1__SD1_CD_B 0x210 0x5e0 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_16__ESAI_TX3_RX2 0x214 0x5e4 0x850 0x0 0x1 +#define MX6QDL_PAD_GPIO_16__ENET_1588_EVENT2_IN 0x214 0x5e4 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x214 0x5e4 0x80c 0x2 0x0 +#define MX6QDL_PAD_GPIO_16__SD1_LCTL 0x214 0x5e4 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_16__SPDIF_IN 0x214 0x5e4 0x8f0 0x4 0x2 +#define MX6QDL_PAD_GPIO_16__GPIO7_IO11 0x214 0x5e4 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_16__I2C3_SDA 0x214 0x5e4 0x87c 0x6 0x1 +#define MX6QDL_PAD_GPIO_16__JTAG_DE_B 0x214 0x5e4 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_17__ESAI_TX0 0x218 0x5e8 0x844 0x0 0x0 +#define MX6QDL_PAD_GPIO_17__ENET_1588_EVENT3_IN 0x218 0x5e8 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_17__CCM_PMIC_READY 0x218 0x5e8 0x7d4 0x2 0x1 +#define MX6QDL_PAD_GPIO_17__SDMA_EXT_EVENT0 0x218 0x5e8 0x8e8 0x3 0x1 +#define MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x218 0x5e8 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x218 0x5e8 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_18__ESAI_TX1 0x21c 0x5ec 0x848 0x0 0x0 +#define MX6QDL_PAD_GPIO_18__ENET_RX_CLK 0x21c 0x5ec 0x814 0x1 0x0 +#define MX6QDL_PAD_GPIO_18__SD3_VSELECT 0x21c 0x5ec 0x000 0x2 0x0 +#define MX6QDL_PAD_GPIO_18__SDMA_EXT_EVENT1 0x21c 0x5ec 0x8ec 0x3 0x1 +#define MX6QDL_PAD_GPIO_18__ASRC_EXT_CLK 0x21c 0x5ec 0x794 0x4 0x1 +#define MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x21c 0x5ec 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_18__SNVS_VIO_5_CTL 0x21c 0x5ec 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_19__KEY_COL5 0x220 0x5f0 0x8c0 0x0 0x2 +#define MX6QDL_PAD_GPIO_19__ENET_1588_EVENT0_OUT 0x220 0x5f0 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x220 0x5f0 0x000 0x2 0x0 +#define MX6QDL_PAD_GPIO_19__CCM_CLKO1 0x220 0x5f0 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_19__ECSPI1_RDY 0x220 0x5f0 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x220 0x5f0 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_19__ENET_TX_ER 0x220 0x5f0 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_2__ESAI_TX_FS 0x224 0x5f4 0x830 0x0 0x1 +#define MX6QDL_PAD_GPIO_2__KEY_ROW6 0x224 0x5f4 0x8d0 0x2 0x1 +#define MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x224 0x5f4 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_2__SD2_WP 0x224 0x5f4 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_2__MLB_DATA 0x224 0x5f4 0x8e0 0x7 0x1 +#define MX6QDL_PAD_GPIO_3__ESAI_RX_HF_CLK 0x228 0x5f8 0x834 0x0 0x1 +#define MX6QDL_PAD_GPIO_3__I2C3_SCL 0x228 0x5f8 0x878 0x2 0x1 +#define MX6QDL_PAD_GPIO_3__XTALOSC_REF_CLK_24M 0x228 0x5f8 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_3__CCM_CLKO2 0x228 0x5f8 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_3__GPIO1_IO03 0x228 0x5f8 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_3__USB_H1_OC 0x228 0x5f8 0x924 0x6 0x1 +#define MX6QDL_PAD_GPIO_3__MLB_CLK 0x228 0x5f8 0x8dc 0x7 0x1 +#define MX6QDL_PAD_GPIO_4__ESAI_TX_HF_CLK 0x22c 0x5fc 0x838 0x0 0x1 +#define MX6QDL_PAD_GPIO_4__KEY_COL7 0x22c 0x5fc 0x8c8 0x2 0x1 +#define MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x22c 0x5fc 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_4__SD2_CD_B 0x22c 0x5fc 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_5__ESAI_TX2_RX3 0x230 0x600 0x84c 0x0 0x1 +#define MX6QDL_PAD_GPIO_5__KEY_ROW7 0x230 0x600 0x8d4 0x2 0x1 +#define MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x230 0x600 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x230 0x600 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_5__I2C3_SCL 0x230 0x600 0x878 0x6 0x2 +#define MX6QDL_PAD_GPIO_5__ARM_EVENTI 0x230 0x600 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_6__ESAI_TX_CLK 0x234 0x604 0x840 0x0 0x1 +#define MX6QDL_PAD_GPIO_6__I2C3_SDA 0x234 0x604 0x87c 0x2 0x2 +#define MX6QDL_PAD_GPIO_6__GPIO1_IO06 0x234 0x604 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_6__SD2_LCTL 0x234 0x604 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_6__MLB_SIG 0x234 0x604 0x8e4 0x7 0x1 +#define MX6QDL_PAD_GPIO_7__ESAI_TX4_RX1 0x238 0x608 0x854 0x0 0x1 +#define MX6QDL_PAD_GPIO_7__EPIT1_OUT 0x238 0x608 0x000 0x2 0x0 +#define MX6QDL_PAD_GPIO_7__FLEXCAN1_TX 0x238 0x608 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x238 0x608 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_7__UART2_RX_DATA 0x238 0x608 0x904 0x4 0x2 +#define MX6QDL_PAD_GPIO_7__GPIO1_IO07 0x238 0x608 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_7__SPDIF_LOCK 0x238 0x608 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_7__USB_OTG_HOST_MODE 0x238 0x608 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_7__I2C4_SCL 0x238 0x608 0x880 0x8 0x1 +#define MX6QDL_PAD_GPIO_8__ESAI_TX5_RX0 0x23c 0x60c 0x858 0x0 0x1 +#define MX6QDL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x23c 0x60c 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_8__EPIT2_OUT 0x23c 0x60c 0x000 0x2 0x0 +#define MX6QDL_PAD_GPIO_8__FLEXCAN1_RX 0x23c 0x60c 0x7c8 0x3 0x0 +#define MX6QDL_PAD_GPIO_8__UART2_RX_DATA 0x23c 0x60c 0x904 0x4 0x3 +#define MX6QDL_PAD_GPIO_8__UART2_TX_DATA 0x23c 0x60c 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_8__GPIO1_IO08 0x23c 0x60c 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_8__SPDIF_SR_CLK 0x23c 0x60c 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_8__USB_OTG_PWR_CTL_WAKE 0x23c 0x60c 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_8__I2C4_SDA 0x23c 0x60c 0x884 0x8 0x1 +#define MX6QDL_PAD_GPIO_9__ESAI_RX_FS 0x240 0x610 0x82c 0x0 0x1 +#define MX6QDL_PAD_GPIO_9__WDOG1_B 0x240 0x610 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_9__KEY_COL6 0x240 0x610 0x8c4 0x2 0x1 +#define MX6QDL_PAD_GPIO_9__CCM_REF_EN_B 0x240 0x610 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_9__PWM1_OUT 0x240 0x610 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x240 0x610 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_9__SD1_WP 0x240 0x610 0x92c 0x6 0x1 +#define MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x244 0x62c 0x7d8 0x0 0x3 +#define MX6QDL_PAD_KEY_COL0__ENET_RX_DATA3 0x244 0x62c 0x824 0x1 0x0 +#define MX6QDL_PAD_KEY_COL0__AUD5_TXC 0x244 0x62c 0x7c0 0x2 0x1 +#define MX6QDL_PAD_KEY_COL0__KEY_COL0 0x244 0x62c 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x244 0x62c 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_COL0__UART4_RX_DATA 0x244 0x62c 0x914 0x4 0x2 +#define MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x244 0x62c 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL0__DCIC1_OUT 0x244 0x62c 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x248 0x630 0x7dc 0x0 0x3 +#define MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x248 0x630 0x810 0x1 0x1 +#define MX6QDL_PAD_KEY_COL1__AUD5_TXFS 0x248 0x630 0x7c4 0x2 0x1 +#define MX6QDL_PAD_KEY_COL1__KEY_COL1 0x248 0x630 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x248 0x630 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_COL1__UART5_RX_DATA 0x248 0x630 0x91c 0x4 0x2 +#define MX6QDL_PAD_KEY_COL1__GPIO4_IO08 0x248 0x630 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL1__SD1_VSELECT 0x248 0x630 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_COL2__ECSPI1_SS1 0x24c 0x634 0x7e8 0x0 0x2 +#define MX6QDL_PAD_KEY_COL2__ENET_RX_DATA2 0x24c 0x634 0x820 0x1 0x0 +#define MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x24c 0x634 0x000 0x2 0x0 +#define MX6QDL_PAD_KEY_COL2__KEY_COL2 0x24c 0x634 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL2__ENET_MDC 0x24c 0x634 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_COL2__GPIO4_IO10 0x24c 0x634 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL2__USB_H1_PWR_CTL_WAKE 0x24c 0x634 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_COL3__ECSPI1_SS3 0x250 0x638 0x7f0 0x0 0x1 +#define MX6QDL_PAD_KEY_COL3__ENET_CRS 0x250 0x638 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x250 0x638 0x860 0x2 0x1 +#define MX6QDL_PAD_KEY_COL3__KEY_COL3 0x250 0x638 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x250 0x638 0x870 0x4 0x1 +#define MX6QDL_PAD_KEY_COL3__GPIO4_IO12 0x250 0x638 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x250 0x638 0x8f0 0x6 0x3 +#define MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x254 0x63c 0x000 0x0 0x0 +#define MX6QDL_PAD_KEY_COL4__IPU1_SISG4 0x254 0x63c 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x254 0x63c 0x920 0x2 0x1 +#define MX6QDL_PAD_KEY_COL4__KEY_COL4 0x254 0x63c 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL4__UART5_RTS_B 0x254 0x63c 0x918 0x4 0x2 +#define MX6QDL_PAD_KEY_COL4__UART5_CTS_B 0x254 0x63c 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_COL4__GPIO4_IO14 0x254 0x63c 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x258 0x640 0x7e0 0x0 0x3 +#define MX6QDL_PAD_KEY_ROW0__ENET_TX_DATA3 0x258 0x640 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW0__AUD5_TXD 0x258 0x640 0x7b4 0x2 0x1 +#define MX6QDL_PAD_KEY_ROW0__KEY_ROW0 0x258 0x640 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x258 0x640 0x914 0x4 0x3 +#define MX6QDL_PAD_KEY_ROW0__UART4_TX_DATA 0x258 0x640 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x258 0x640 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW0__DCIC2_OUT 0x258 0x640 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_ROW1__ECSPI1_SS0 0x25c 0x644 0x7e4 0x0 0x3 +#define MX6QDL_PAD_KEY_ROW1__ENET_COL 0x25c 0x644 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW1__AUD5_RXD 0x25c 0x644 0x7b0 0x2 0x1 +#define MX6QDL_PAD_KEY_ROW1__KEY_ROW1 0x25c 0x644 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x25c 0x644 0x91c 0x4 0x3 +#define MX6QDL_PAD_KEY_ROW1__UART5_TX_DATA 0x25c 0x644 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_ROW1__GPIO4_IO09 0x25c 0x644 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW1__SD2_VSELECT 0x25c 0x644 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_ROW2__ECSPI1_SS2 0x260 0x648 0x7ec 0x0 0x1 +#define MX6QDL_PAD_KEY_ROW2__ENET_TX_DATA2 0x260 0x648 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x260 0x648 0x7c8 0x2 0x1 +#define MX6QDL_PAD_KEY_ROW2__KEY_ROW2 0x260 0x648 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW2__SD2_VSELECT 0x260 0x648 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_ROW2__GPIO4_IO11 0x260 0x648 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x260 0x648 0x85c 0x6 0x1 +#define MX6QDL_PAD_KEY_ROW3__ASRC_EXT_CLK 0x264 0x64c 0x794 0x1 0x2 +#define MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x264 0x64c 0x864 0x2 0x1 +#define MX6QDL_PAD_KEY_ROW3__KEY_ROW3 0x264 0x64c 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x264 0x64c 0x874 0x4 0x1 +#define MX6QDL_PAD_KEY_ROW3__GPIO4_IO13 0x264 0x64c 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW3__SD1_VSELECT 0x264 0x64c 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x268 0x650 0x7cc 0x0 0x0 +#define MX6QDL_PAD_KEY_ROW4__IPU1_SISG5 0x268 0x650 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW4__USB_OTG_PWR 0x268 0x650 0x000 0x2 0x0 +#define MX6QDL_PAD_KEY_ROW4__KEY_ROW4 0x268 0x650 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW4__UART5_CTS_B 0x268 0x650 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_ROW4__UART5_RTS_B 0x268 0x650 0x918 0x4 0x3 +#define MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x268 0x650 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_ALE__NAND_ALE 0x26c 0x654 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_ALE__SD4_RESET 0x26c 0x654 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_ALE__GPIO6_IO08 0x26c 0x654 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CLE__NAND_CLE 0x270 0x658 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CLE__GPIO6_IO07 0x270 0x658 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0x274 0x65c 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x274 0x65c 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0x278 0x660 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CS1__SD4_VSELECT 0x278 0x660 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_CS1__SD3_VSELECT 0x278 0x660 0x000 0x2 0x0 +#define MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x278 0x660 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS2__NAND_CE2_B 0x27c 0x664 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CS2__IPU1_SISG0 0x27c 0x664 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_CS2__ESAI_TX0 0x27c 0x664 0x844 0x2 0x1 +#define MX6QDL_PAD_NANDF_CS2__EIM_CRE 0x27c 0x664 0x000 0x3 0x0 +#define MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x27c 0x664 0x000 0x4 0x0 +#define MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x27c 0x664 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS3__NAND_CE3_B 0x280 0x668 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CS3__IPU1_SISG1 0x280 0x668 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x280 0x668 0x848 0x2 0x1 +#define MX6QDL_PAD_NANDF_CS3__EIM_ADDR26 0x280 0x668 0x000 0x3 0x0 +#define MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x280 0x668 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS3__I2C4_SDA 0x280 0x668 0x884 0x9 0x2 +#define MX6QDL_PAD_NANDF_D0__NAND_DATA00 0x284 0x66c 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D0__SD1_DATA4 0x284 0x66c 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x284 0x66c 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D1__NAND_DATA01 0x288 0x670 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D1__SD1_DATA5 0x288 0x670 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x288 0x670 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D2__NAND_DATA02 0x28c 0x674 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D2__SD1_DATA6 0x28c 0x674 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x28c 0x674 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D3__NAND_DATA03 0x290 0x678 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D3__SD1_DATA7 0x290 0x678 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x290 0x678 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D4__NAND_DATA04 0x294 0x67c 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x294 0x67c 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x294 0x67c 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D5__NAND_DATA05 0x298 0x680 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x298 0x680 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x298 0x680 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D6__NAND_DATA06 0x29c 0x684 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x29c 0x684 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x29c 0x684 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D7__NAND_DATA07 0x2a0 0x688 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x2a0 0x688 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D7__GPIO2_IO07 0x2a0 0x688 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0x2a4 0x68c 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_RB0__GPIO6_IO10 0x2a4 0x68c 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0x2a8 0x690 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_WP_B__GPIO6_IO09 0x2a8 0x690 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_WP_B__I2C4_SCL 0x2a8 0x690 0x880 0x9 0x2 +#define MX6QDL_PAD_RGMII_RD0__HSI_RX_READY 0x2ac 0x694 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x2ac 0x694 0x818 0x1 0x1 +#define MX6QDL_PAD_RGMII_RD0__GPIO6_IO25 0x2ac 0x694 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RD1__HSI_TX_FLAG 0x2b0 0x698 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x2b0 0x698 0x81c 0x1 0x1 +#define MX6QDL_PAD_RGMII_RD1__GPIO6_IO27 0x2b0 0x698 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RD2__HSI_TX_DATA 0x2b4 0x69c 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x2b4 0x69c 0x820 0x1 0x1 +#define MX6QDL_PAD_RGMII_RD2__GPIO6_IO28 0x2b4 0x69c 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RD3__HSI_TX_WAKE 0x2b8 0x6a0 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x2b8 0x6a0 0x824 0x1 0x1 +#define MX6QDL_PAD_RGMII_RD3__GPIO6_IO29 0x2b8 0x6a0 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RX_CTL__USB_H3_DATA 0x2bc 0x6a4 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x2bc 0x6a4 0x828 0x1 0x1 +#define MX6QDL_PAD_RGMII_RX_CTL__GPIO6_IO24 0x2bc 0x6a4 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RXC__USB_H3_STROBE 0x2c0 0x6a8 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x2c0 0x6a8 0x814 0x1 0x1 +#define MX6QDL_PAD_RGMII_RXC__GPIO6_IO30 0x2c0 0x6a8 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TD0__HSI_TX_READY 0x2c4 0x6ac 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x2c4 0x6ac 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TD0__GPIO6_IO20 0x2c4 0x6ac 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TD1__HSI_RX_FLAG 0x2c8 0x6b0 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x2c8 0x6b0 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TD1__GPIO6_IO21 0x2c8 0x6b0 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TD2__HSI_RX_DATA 0x2cc 0x6b4 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x2cc 0x6b4 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TD2__GPIO6_IO22 0x2cc 0x6b4 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TD3__HSI_RX_WAKE 0x2d0 0x6b8 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x2d0 0x6b8 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TD3__GPIO6_IO23 0x2d0 0x6b8 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x2d4 0x6bc 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x2d4 0x6bc 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TX_CTL__GPIO6_IO26 0x2d4 0x6bc 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TX_CTL__ENET_REF_CLK 0x2d4 0x6bc 0x80c 0x7 0x1 +#define MX6QDL_PAD_RGMII_TXC__USB_H2_DATA 0x2d8 0x6c0 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x2d8 0x6c0 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TXC__SPDIF_EXT_CLK 0x2d8 0x6c0 0x8f4 0x2 0x1 +#define MX6QDL_PAD_RGMII_TXC__GPIO6_IO19 0x2d8 0x6c0 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TXC__XTALOSC_REF_CLK_24M 0x2d8 0x6c0 0x000 0x7 0x0 +#define MX6QDL_PAD_SD1_CLK__SD1_CLK 0x2dc 0x6c4 0x928 0x0 0x1 +#define MX6QDL_PAD_SD1_CLK__GPT_CLKIN 0x2dc 0x6c4 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_CLK__GPIO1_IO20 0x2dc 0x6c4 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_CMD__SD1_CMD 0x2e0 0x6c8 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x2e0 0x6c8 0x000 0x2 0x0 +#define MX6QDL_PAD_SD1_CMD__GPT_COMPARE1 0x2e0 0x6c8 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_CMD__GPIO1_IO18 0x2e0 0x6c8 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x2e4 0x6cc 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_DAT0__GPT_CAPTURE1 0x2e4 0x6cc 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x2e4 0x6cc 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x2e8 0x6d0 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_DAT1__PWM3_OUT 0x2e8 0x6d0 0x000 0x2 0x0 +#define MX6QDL_PAD_SD1_DAT1__GPT_CAPTURE2 0x2e8 0x6d0 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_DAT1__GPIO1_IO17 0x2e8 0x6d0 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x2ec 0x6d4 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_DAT2__GPT_COMPARE2 0x2ec 0x6d4 0x000 0x2 0x0 +#define MX6QDL_PAD_SD1_DAT2__PWM2_OUT 0x2ec 0x6d4 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_DAT2__WDOG1_B 0x2ec 0x6d4 0x000 0x4 0x0 +#define MX6QDL_PAD_SD1_DAT2__GPIO1_IO19 0x2ec 0x6d4 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT2__WDOG1_RESET_B_DEB 0x2ec 0x6d4 0x000 0x6 0x0 +#define MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x2f0 0x6d8 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_DAT3__GPT_COMPARE3 0x2f0 0x6d8 0x000 0x2 0x0 +#define MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x2f0 0x6d8 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_DAT3__WDOG2_B 0x2f0 0x6d8 0x000 0x4 0x0 +#define MX6QDL_PAD_SD1_DAT3__GPIO1_IO21 0x2f0 0x6d8 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT3__WDOG2_RESET_B_DEB 0x2f0 0x6d8 0x000 0x6 0x0 +#define MX6QDL_PAD_SD2_CLK__SD2_CLK 0x2f4 0x6dc 0x930 0x0 0x1 +#define MX6QDL_PAD_SD2_CLK__KEY_COL5 0x2f4 0x6dc 0x8c0 0x2 0x3 +#define MX6QDL_PAD_SD2_CLK__AUD4_RXFS 0x2f4 0x6dc 0x7a4 0x3 0x1 +#define MX6QDL_PAD_SD2_CLK__GPIO1_IO10 0x2f4 0x6dc 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_CMD__SD2_CMD 0x2f8 0x6e0 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_CMD__KEY_ROW5 0x2f8 0x6e0 0x8cc 0x2 0x2 +#define MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x2f8 0x6e0 0x7a0 0x3 0x1 +#define MX6QDL_PAD_SD2_CMD__GPIO1_IO11 0x2f8 0x6e0 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x2fc 0x6e4 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x2fc 0x6e4 0x798 0x3 0x1 +#define MX6QDL_PAD_SD2_DAT0__KEY_ROW7 0x2fc 0x6e4 0x8d4 0x4 0x2 +#define MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x2fc 0x6e4 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT0__DCIC2_OUT 0x2fc 0x6e4 0x000 0x6 0x0 +#define MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x300 0x6e8 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_DAT1__EIM_CS2_B 0x300 0x6e8 0x000 0x2 0x0 +#define MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x300 0x6e8 0x7ac 0x3 0x1 +#define MX6QDL_PAD_SD2_DAT1__KEY_COL7 0x300 0x6e8 0x8c8 0x4 0x2 +#define MX6QDL_PAD_SD2_DAT1__GPIO1_IO14 0x300 0x6e8 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x304 0x6ec 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_DAT2__EIM_CS3_B 0x304 0x6ec 0x000 0x2 0x0 +#define MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x304 0x6ec 0x79c 0x3 0x1 +#define MX6QDL_PAD_SD2_DAT2__KEY_ROW6 0x304 0x6ec 0x8d0 0x4 0x2 +#define MX6QDL_PAD_SD2_DAT2__GPIO1_IO13 0x304 0x6ec 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x308 0x6f0 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_DAT3__KEY_COL6 0x308 0x6f0 0x8c4 0x2 0x2 +#define MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x308 0x6f0 0x7a8 0x3 0x1 +#define MX6QDL_PAD_SD2_DAT3__GPIO1_IO12 0x308 0x6f0 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_CLK__SD3_CLK 0x30c 0x6f4 0x934 0x0 0x1 +#define MX6QDL_PAD_SD3_CLK__UART2_RTS_B 0x30c 0x6f4 0x900 0x1 0x2 +#define MX6QDL_PAD_SD3_CLK__UART2_CTS_B 0x30c 0x6f4 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x30c 0x6f4 0x7c8 0x2 0x2 +#define MX6QDL_PAD_SD3_CLK__GPIO7_IO03 0x30c 0x6f4 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_CMD__SD3_CMD 0x310 0x6f8 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_CMD__UART2_CTS_B 0x310 0x6f8 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_CMD__UART2_RTS_B 0x310 0x6f8 0x900 0x1 0x3 +#define MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x310 0x6f8 0x000 0x2 0x0 +#define MX6QDL_PAD_SD3_CMD__GPIO7_IO02 0x310 0x6f8 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x314 0x6fc 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT0__UART1_CTS_B 0x314 0x6fc 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT0__UART1_RTS_B 0x314 0x6fc 0x8f8 0x1 0x2 +#define MX6QDL_PAD_SD3_DAT0__FLEXCAN2_TX 0x314 0x6fc 0x000 0x2 0x0 +#define MX6QDL_PAD_SD3_DAT0__GPIO7_IO04 0x314 0x6fc 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x318 0x700 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT1__UART1_RTS_B 0x318 0x700 0x8f8 0x1 0x3 +#define MX6QDL_PAD_SD3_DAT1__UART1_CTS_B 0x318 0x700 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT1__FLEXCAN2_RX 0x318 0x700 0x7cc 0x2 0x1 +#define MX6QDL_PAD_SD3_DAT1__GPIO7_IO05 0x318 0x700 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x31c 0x704 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT2__GPIO7_IO06 0x31c 0x704 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x320 0x708 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT3__UART3_CTS_B 0x320 0x708 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT3__UART3_RTS_B 0x320 0x708 0x908 0x1 0x4 +#define MX6QDL_PAD_SD3_DAT3__GPIO7_IO07 0x320 0x708 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x324 0x70c 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT4__UART2_RX_DATA 0x324 0x70c 0x904 0x1 0x4 +#define MX6QDL_PAD_SD3_DAT4__UART2_TX_DATA 0x324 0x70c 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x324 0x70c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x328 0x710 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT5__UART2_TX_DATA 0x328 0x710 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT5__UART2_RX_DATA 0x328 0x710 0x904 0x1 0x5 +#define MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x328 0x710 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x32c 0x714 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x32c 0x714 0x8fc 0x1 0x2 +#define MX6QDL_PAD_SD3_DAT6__UART1_TX_DATA 0x32c 0x714 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT6__GPIO6_IO18 0x32c 0x714 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x330 0x718 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x330 0x718 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT7__UART1_RX_DATA 0x330 0x718 0x8fc 0x1 0x3 +#define MX6QDL_PAD_SD3_DAT7__GPIO6_IO17 0x330 0x718 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_RST__SD3_RESET 0x334 0x71c 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_RST__UART3_RTS_B 0x334 0x71c 0x908 0x1 0x5 +#define MX6QDL_PAD_SD3_RST__UART3_CTS_B 0x334 0x71c 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x334 0x71c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_CLK__SD4_CLK 0x338 0x720 0x938 0x0 0x1 +#define MX6QDL_PAD_SD4_CLK__NAND_WE_B 0x338 0x720 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_CLK__UART3_RX_DATA 0x338 0x720 0x90c 0x2 0x2 +#define MX6QDL_PAD_SD4_CLK__UART3_TX_DATA 0x338 0x720 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_CLK__GPIO7_IO10 0x338 0x720 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_CMD__SD4_CMD 0x33c 0x724 0x000 0x0 0x0 +#define MX6QDL_PAD_SD4_CMD__NAND_RE_B 0x33c 0x724 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_CMD__UART3_TX_DATA 0x33c 0x724 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_CMD__UART3_RX_DATA 0x33c 0x724 0x90c 0x2 0x3 +#define MX6QDL_PAD_SD4_CMD__GPIO7_IO09 0x33c 0x724 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x340 0x728 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x340 0x728 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT0__GPIO2_IO08 0x340 0x728 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x344 0x72c 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x344 0x72c 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT1__GPIO2_IO09 0x344 0x72c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x348 0x730 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT2__PWM4_OUT 0x348 0x730 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT2__GPIO2_IO10 0x348 0x730 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x34c 0x734 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT3__GPIO2_IO11 0x34c 0x734 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x350 0x738 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x350 0x738 0x904 0x2 0x6 +#define MX6QDL_PAD_SD4_DAT4__UART2_TX_DATA 0x350 0x738 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT4__GPIO2_IO12 0x350 0x738 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x354 0x73c 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT5__UART2_RTS_B 0x354 0x73c 0x900 0x2 0x4 +#define MX6QDL_PAD_SD4_DAT5__UART2_CTS_B 0x354 0x73c 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT5__GPIO2_IO13 0x354 0x73c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x358 0x740 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT6__UART2_CTS_B 0x358 0x740 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT6__UART2_RTS_B 0x358 0x740 0x900 0x2 0x5 +#define MX6QDL_PAD_SD4_DAT6__GPIO2_IO14 0x358 0x740 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x35c 0x744 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x35c 0x744 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT7__UART2_RX_DATA 0x35c 0x744 0x904 0x2 0x7 +#define MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x35c 0x744 0x000 0x5 0x0 #endif /* __DTS_IMX6DL_PINFUNC_H */ diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-sabreauto-ecspi.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabreauto-ecspi.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-sabreauto-ecspi.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabreauto-ecspi.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6dl-sabreauto.dts" + +&ecspi1 { + status = "okay"; +}; + +&flexcan2 { + /* max7310_c on i2c3 is gone */ + status = "disabled"; +}; + +&i2c3 { + /* pin conflict with ecspi1 */ + status = "disabled"; +}; + +&uart3 { + /* the uart3 depends on the i2c3, so disable it too. */ + status = "disabled"; +}; + +&usbh1 { + /* max7310_b on i2c3 is gone */ + status = "disabled"; +}; + +&usbotg { + dr_mode = "peripheral"; + status = "okay"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-sabreauto-flexcan1.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabreauto-flexcan1.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-sabreauto-flexcan1.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabreauto-flexcan1.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6dl-sabreauto.dts" + +&flexcan1{ + status = "okay"; +}; + +&fec { + /* pin conflict with flexcan1 */ + status = "disabled"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-sabreauto-gpmi-weim.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabreauto-gpmi-weim.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-sabreauto-gpmi-weim.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabreauto-gpmi-weim.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6dl-sabreauto.dts" + +&ecspi1 { + /* pin conflict with weim */ + status = "disabled"; +}; + +&flexcan2 { + /* max7310_c on i2c3 is gone */ + status = "disabled"; +}; + +&gpmi { + status = "okay"; +}; + +&i2c3 { + /* pin conflict with weim */ + status = "disabled"; +}; + +&uart3 { + /* pin conflict with gpmi and weim */ + status = "disabled"; +}; + +&usbh1 { + /* max7310_b on i2c3 is gone */ + status = "disabled"; +}; + +&usbotg { + dr_mode = "peripheral"; + status = "okay"; +}; + +&weim { + status = "okay"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-sabreauto.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabreauto.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-sabreauto.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabreauto.dts 2014-03-08 20:32:53.000000000 +0100 @@ -16,16 +16,15 @@ compatible = "fsl,imx6dl-sabreauto", "fsl,imx6dl"; }; -&iomuxc { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; +&ldb { + ipu_id = <0>; + sec_ipu_id = <0>; +}; + +&mxcfb1 { + status = "okay"; +}; - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - MX6DL_PAD_NANDF_CS2__GPIO6_IO15 0x80000000 - MX6DL_PAD_SD2_DAT2__GPIO1_IO13 0x80000000 - >; - }; - }; +&mxcfb2 { + status = "okay"; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-sabresd-hdcp.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabresd-hdcp.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-sabresd-hdcp.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabresd-hdcp.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6dl-sabresd.dts" + +&hdmi_video { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; + fsl,hdcp; +}; + +&i2c2 { + status = "disable"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-sabresd-ldo.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabresd-ldo.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-sabresd-ldo.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabresd-ldo.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6dl-sabresd.dts" + +&cpu0 { + arm-supply = <®_arm>; + soc-supply = <®_soc>; + pu-supply = <®_pu>; /* use pu_dummy if VDDSOC share with VDDPU */ +}; + +&gpc { + fsl,ldo-bypass = <0>; /* use ldo-bypass, u-boot will check it and configure */ + fsl,wdog-reset = <1>; /* watchdog select of reset source */ + pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; + +&gpu { + pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; + +&vpu { + pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-sabresd.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabresd.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-sabresd.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-sabresd.dts 2014-03-08 20:32:53.000000000 +0100 @@ -16,20 +16,126 @@ compatible = "fsl,imx6dl-sabresd", "fsl,imx6dl"; }; +&i2c3 { + max17135@48 { + compatible = "maxim,max17135"; + reg = <0x48>; + vneg_pwrup = <1>; + gvee_pwrup = <1>; + vpos_pwrup = <2>; + gvdd_pwrup = <1>; + gvdd_pwrdn = <1>; + vpos_pwrdn = <2>; + gvee_pwrdn = <1>; + vneg_pwrdn = <1>; + SENSOR-supply = <®_sensor>; + gpio_pmic_pwrgood = <&gpio2 21 0>; + gpio_pmic_vcom_ctrl = <&gpio3 17 0>; + gpio_pmic_wakeup = <&gpio3 20 0>; + gpio_pmic_v3p3 = <&gpio2 20 0>; + gpio_pmic_intr = <&gpio2 25 0>; + + regulators { + DISPLAY_reg: DISPLAY { + regulator-name = "DISPLAY"; + }; + + GVDD_reg: GVDD { + /* 20v */ + regulator-name = "GVDD"; + }; + + GVEE_reg: GVEE { + /* -22v */ + regulator-name = "GVEE"; + }; + + HVINN_reg: HVINN { + /* -22v */ + regulator-name = "HVINN"; + }; + + HVINP_reg: HVINP { + /* 20v */ + regulator-name = "HVINP"; + }; + + VCOM_reg: VCOM { + regulator-name = "VCOM"; + /* 2's-compliment, -4325000 */ + regulator-min-microvolt = <0xffbe0178>; + /* 2's-compliment, -500000 */ + regulator-max-microvolt = <0xfff85ee0>; + }; + + VNEG_reg: VNEG { + /* -15v */ + regulator-name = "VNEG"; + }; + + VPOS_reg: VPOS { + /* 15v */ + regulator-name = "VPOS"; + }; + + V3P3_reg: V3P3 { + regulator-name = "V3P3"; + }; + }; + }; +}; + &iomuxc { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; + pinctrl-0 = <&pinctrl_hog_1>, <&pinctrl_hog_2>; hog { - pinctrl_hog: hoggrp { + pinctrl_hog_2: hoggrp-2 { fsl,pins = < - MX6DL_PAD_GPIO_4__GPIO1_IO04 0x80000000 - MX6DL_PAD_GPIO_5__GPIO1_IO05 0x80000000 - MX6DL_PAD_NANDF_D0__GPIO2_IO00 0x80000000 - MX6DL_PAD_NANDF_D1__GPIO2_IO01 0x80000000 - MX6DL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 - MX6DL_PAD_NANDF_D3__GPIO2_IO03 0x80000000 + /* MAX17135 */ + MX6QDL_PAD_EIM_A17__GPIO2_IO21 0x80000000 + MX6QDL_PAD_EIM_D17__GPIO3_IO17 0x80000000 + MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x80000000 + MX6QDL_PAD_EIM_A18__GPIO2_IO20 0x80000000 + MX6QDL_PAD_EIM_OE__GPIO2_IO25 0x80000000 + /* elan touch */ + MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x80000000 + MX6QDL_PAD_EIM_DA8__GPIO3_IO08 0x80000000 + MX6QDL_PAD_EIM_D28__GPIO3_IO28 0x170b0 >; }; }; }; + +&epdc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_epdc_0>; + V3P3-supply = <&V3P3_reg>; + VCOM-supply = <&VCOM_reg>; + DISPLAY-supply = <&DISPLAY_reg>; + status = "okay"; +}; + +&ldb { + ipu_id = <0>; + sec_ipu_id = <0>; +}; + +&pxp { + status = "okay"; +}; + +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + status = "okay"; +}; + +&battery { + offset-charger = <1485>; + offset-discharger = <1464>; + offset-usb-charger = <1285>; +}; + diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl-wandboard.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-wandboard.dts --- linux-3.10.30/arch/arm/boot/dts/imx6dl-wandboard.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl-wandboard.dts 2014-03-08 20:32:53.000000000 +0100 @@ -10,6 +10,7 @@ */ /dts-v1/; #include "imx6dl.dtsi" +#include "imx6qdl-wandboard.dtsi" / { model = "Wandboard i.MX6 Dual Lite Board"; @@ -19,26 +20,3 @@ reg = <0x10000000 0x40000000>; }; }; - -&fec { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet_1>; - phy-mode = "rgmii"; - status = "okay"; -}; - -&uart1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1_1>; - status = "okay"; -}; - -&usbh1 { - status = "okay"; -}; - -&usdhc3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc3_2>; - status = "okay"; -}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6dl.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6dl.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6dl.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -8,19 +8,39 @@ * */ -#include "imx6qdl.dtsi" #include "imx6dl-pinfunc.h" +#include "imx6qdl.dtsi" / { cpus { #address-cells = <1>; #size-cells = <0>; - cpu@0 { + cpu0: cpu@0 { compatible = "arm,cortex-a9"; device_type = "cpu"; reg = <0>; next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 996000 1275000 + 792000 1175000 + 396000 1075000 + >; + fsl,soc-operating-points = < + /* ARM kHz SOC-PU uV */ + 996000 1175000 + 792000 1175000 + 396000 1175000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + clocks = <&clks 104>, <&clks 6>, <&clks 16>, + <&clks 17>, <&clks 170>; + clock-names = "arm", "pll2_pfd2_396m", "step", + "pll1_sw", "pll1_sys"; + arm-supply = <®_arm>; + pu-supply = <®_pu>; + soc-supply = <®_soc>; }; cpu@1 { @@ -28,140 +48,125 @@ device_type = "cpu"; reg = <1>; next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 996000 1250000 + 792000 1175000 + 396000 1075000 + >; + fsl,soc-operating-points = < + /* ARM kHz SOC-PU uV */ + 996000 1175000 + 792000 1175000 + 396000 1175000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + clocks = <&clks 104>, <&clks 6>, <&clks 16>, + <&clks 17>, <&clks 170>; + clock-names = "arm", "pll2_pfd2_396m", "step", + "pll1_sw", "pll1_sys"; + arm-supply = <®_arm>; + pu-supply = <®_pu>; + soc-supply = <®_soc>; }; }; soc { - aips1: aips-bus@02000000 { - iomuxc: iomuxc@020e0000 { - compatible = "fsl,imx6dl-iomuxc"; - reg = <0x020e0000 0x4000>; - enet { - pinctrl_enet_1: enetgrp-1 { - fsl,pins = < - MX6DL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 - MX6DL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 - MX6DL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 - MX6DL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6DL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6DL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6DL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6DL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 - MX6DL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 - MX6DL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 - MX6DL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 - MX6DL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 - MX6DL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 - MX6DL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 - MX6DL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 - MX6DL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 - >; - }; - - pinctrl_enet_2: enetgrp-2 { - fsl,pins = < - MX6DL_PAD_KEY_COL1__ENET_MDIO 0x1b0b0 - MX6DL_PAD_KEY_COL2__ENET_MDC 0x1b0b0 - MX6DL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 - MX6DL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6DL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6DL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6DL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6DL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 - MX6DL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 - MX6DL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 - MX6DL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 - MX6DL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 - MX6DL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 - MX6DL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 - MX6DL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 - >; - }; - }; - - uart1 { - pinctrl_uart1_1: uart1grp-1 { - fsl,pins = < - MX6DL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 - MX6DL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 - >; - }; - }; - - uart4 { - pinctrl_uart4_1: uart4grp-1 { - fsl,pins = < - MX6DL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 - MX6DL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 - >; - }; - }; - - usbotg { - pinctrl_usbotg_2: usbotggrp-2 { - fsl,pins = < - MX6DL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 - >; - }; - }; - - usdhc2 { - pinctrl_usdhc2_1: usdhc2grp-1 { - fsl,pins = < - MX6DL_PAD_SD2_CMD__SD2_CMD 0x17059 - MX6DL_PAD_SD2_CLK__SD2_CLK 0x10059 - MX6DL_PAD_SD2_DAT0__SD2_DATA0 0x17059 - MX6DL_PAD_SD2_DAT1__SD2_DATA1 0x17059 - MX6DL_PAD_SD2_DAT2__SD2_DATA2 0x17059 - MX6DL_PAD_SD2_DAT3__SD2_DATA3 0x17059 - MX6DL_PAD_NANDF_D4__SD2_DATA4 0x17059 - MX6DL_PAD_NANDF_D5__SD2_DATA5 0x17059 - MX6DL_PAD_NANDF_D6__SD2_DATA6 0x17059 - MX6DL_PAD_NANDF_D7__SD2_DATA7 0x17059 - >; - }; - }; - - usdhc3 { - pinctrl_usdhc3_1: usdhc3grp-1 { - fsl,pins = < - MX6DL_PAD_SD3_CMD__SD3_CMD 0x17059 - MX6DL_PAD_SD3_CLK__SD3_CLK 0x10059 - MX6DL_PAD_SD3_DAT0__SD3_DATA0 0x17059 - MX6DL_PAD_SD3_DAT1__SD3_DATA1 0x17059 - MX6DL_PAD_SD3_DAT2__SD3_DATA2 0x17059 - MX6DL_PAD_SD3_DAT3__SD3_DATA3 0x17059 - MX6DL_PAD_SD3_DAT4__SD3_DATA4 0x17059 - MX6DL_PAD_SD3_DAT5__SD3_DATA5 0x17059 - MX6DL_PAD_SD3_DAT6__SD3_DATA6 0x17059 - MX6DL_PAD_SD3_DAT7__SD3_DATA7 0x17059 - >; - }; - - pinctrl_usdhc3_2: usdhc3grp_2 { - fsl,pins = < - MX6DL_PAD_SD3_CMD__SD3_CMD 0x17059 - MX6DL_PAD_SD3_CLK__SD3_CLK 0x10059 - MX6DL_PAD_SD3_DAT0__SD3_DATA0 0x17059 - MX6DL_PAD_SD3_DAT1__SD3_DATA1 0x17059 - MX6DL_PAD_SD3_DAT2__SD3_DATA2 0x17059 - MX6DL_PAD_SD3_DAT3__SD3_DATA3 0x17059 - >; - }; - }; + busfreq { /* BUSFREQ */ + compatible = "fsl,imx6_busfreq"; + clocks = <&clks 171>, <&clks 6>, <&clks 11>, <&clks 104>, <&clks 172>, <&clks 58>, + <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>, <&clks 22> , <&clks 8>; + clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", + "periph_pre", "periph_clk2", "periph_clk2_sel", "osc", "axi_sel", "pll3_pfd1_540m"; + interrupts = <0 107 0x04>, <0 112 0x4>; + interrupt-names = "irq_busfreq_0", "irq_busfreq_1"; + fsl,max_ddr_freq = <400000000>; + }; + + gpu: gpu@00130000 { + compatible = "fsl,imx6dl-gpu", "fsl,imx6q-gpu"; + reg = <0x00130000 0x4000>, <0x00134000 0x4000>, + <0x0 0x0>; + reg-names = "iobase_3d", "iobase_2d", + "phys_baseaddr"; + interrupts = <0 9 0x04>, <0 10 0x04>; + interrupt-names = "irq_3d", "irq_2d"; + clocks = <&clks 143>, <&clks 27>, + <&clks 121>, <&clks 122>, + <&clks 0>; + clock-names = "gpu2d_axi_clk", "gpu3d_axi_clk", + "gpu2d_clk", "gpu3d_clk", + "gpu3d_shader_clk"; + resets = <&src 0>, <&src 3>; + reset-names = "gpu3d", "gpu2d"; + pu-supply = <®_pu>; + }; + + ocram: sram@00900000 { + compatible = "mmio-sram"; + reg = <0x00900000 0x20000>; + clocks = <&clks 142>; + }; + hdmi_core: hdmi_core@00120000 { + compatible = "fsl,imx6dl-hdmi-core"; + reg = <0x00120000 0x9000>; + clocks = <&clks 124>, <&clks 123>; + clock-names = "hdmi_isfr", "hdmi_iahb"; + status = "disabled"; + }; + + hdmi_video: hdmi_video@020e0000 { + compatible = "fsl,imx6dl-hdmi-video"; + reg = <0x020e0000 0x1000>; + reg-names = "hdmi_gpr"; + interrupts = <0 115 0x04>; + clocks = <&clks 124>, <&clks 123>; + clock-names = "hdmi_isfr", "hdmi_iahb"; + status = "disabled"; + }; + + hdmi_audio: hdmi_audio@00120000 { + compatible = "fsl,imx6dl-hdmi-audio"; + clocks = <&clks 124>, <&clks 123>; + clock-names = "hdmi_isfr", "hdmi_iahb"; + dmas = <&sdma 2 22 0>; + dma-names = "tx"; + status = "disabled"; + }; + + hdmi_cec: hdmi_cec@00120000 { + compatible = "fsl,imx6dl-hdmi-cec"; + interrupts = <0 115 0x04>; + status = "disabled"; + }; + + aips1: aips-bus@02000000 { + vpu@02040000 { + iramsize = <0>; + status = "okay"; + }; + iomuxc: iomuxc@020e0000 { + compatible = "fsl,imx6dl-iomuxc"; }; pxp: pxp@020f0000 { + compatible = "fsl,imx6dl-pxp-dma"; reg = <0x020f0000 0x4000>; interrupts = <0 98 0x04>; + clocks = <&clks 133>; + clock-names = "pxp-axi"; + status = "disabled"; }; epdc: epdc@020f4000 { + compatible = "fsl,imx6dl-epdc"; reg = <0x020f4000 0x4000>; interrupts = <0 97 0x04>; + clocks = <&clks 133>, <&clks 137>; + clock-names = "epdc_axi", "epdc_pix"; }; lcdif: lcdif@020f8000 { @@ -171,6 +176,16 @@ }; aips2: aips-bus@02100000 { + mipi_dsi: mipi@021e0000 { + compatible = "fsl,imx6dl-mipi-dsi"; + reg = <0x021e0000 0x4000>; + interrupts = <0 102 0x04>; + gpr = <&gpr>; + clocks = <&clks 138>, <&clks 204>; + clock-names = "mipi_pllref_clk", "mipi_cfg_clk"; + status = "disabled"; + }; + i2c4: i2c@021f8000 { #address-cells = <1>; #size-cells = <0>; @@ -182,3 +197,54 @@ }; }; }; + +&ldb { + clocks = <&clks 33>, <&clks 34>, + <&clks 39>, <&clks 40>, + <&clks 135>, <&clks 136>; + clock-names = "di0_pll", "di1_pll", + "di0_sel", "di1_sel", + "di0", "di1"; + + lvds-channel@0 { + crtcs = <&ipu1 0>, <&ipu1 1>; + }; + + lvds-channel@1 { + crtcs = <&ipu1 0>, <&ipu1 1>; + }; +}; + +&iomuxc { + epdc { + pinctrl_epdc_0: epdcgrp-0 { + fsl,pins = < + MX6QDL_PAD_EIM_A16__EPDC_DATA00 0x80000000 + MX6QDL_PAD_EIM_DA10__EPDC_DATA01 0x80000000 + MX6QDL_PAD_EIM_DA12__EPDC_DATA02 0x80000000 + MX6QDL_PAD_EIM_DA11__EPDC_DATA03 0x80000000 + MX6QDL_PAD_EIM_LBA__EPDC_DATA04 0x80000000 + MX6QDL_PAD_EIM_EB2__EPDC_DATA05 0x80000000 + MX6QDL_PAD_EIM_CS0__EPDC_DATA06 0x80000000 + MX6QDL_PAD_EIM_RW__EPDC_DATA07 0x80000000 + MX6QDL_PAD_EIM_A21__EPDC_GDCLK 0x80000000 + MX6QDL_PAD_EIM_A22__EPDC_GDSP 0x80000000 + MX6QDL_PAD_EIM_A23__EPDC_GDOE 0x80000000 + MX6QDL_PAD_EIM_A24__EPDC_GDRL 0x80000000 + MX6QDL_PAD_EIM_D31__EPDC_SDCLK_P 0x80000000 + MX6QDL_PAD_EIM_D27__EPDC_SDOE 0x80000000 + MX6QDL_PAD_EIM_DA1__EPDC_SDLE 0x80000000 + MX6QDL_PAD_EIM_EB1__EPDC_SDSHR 0x80000000 + MX6QDL_PAD_EIM_DA2__EPDC_BDR0 0x80000000 + MX6QDL_PAD_EIM_DA4__EPDC_SDCE0 0x80000000 + MX6QDL_PAD_EIM_DA5__EPDC_SDCE1 0x80000000 + MX6QDL_PAD_EIM_DA6__EPDC_SDCE2 0x80000000 + >; + }; + }; +}; + +&hdmi { + compatible = "fsl,imx6dl-hdmi"; + crtcs = <&ipu1 0>, <&ipu1 1>; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-arm2.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-arm2.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-arm2.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-arm2.dts 2014-03-08 20:32:53.000000000 +0100 @@ -31,6 +31,15 @@ regulator-max-microvolt = <3300000>; regulator-always-on; }; + + reg_usb_otg_vbus: usb_otg_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio3 22 0>; + enable-active-high; + }; }; leds { @@ -42,6 +51,15 @@ linux,default-trigger = "heartbeat"; }; }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif", + "fsl,imx-sabreauto-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + spdif-in; + }; }; &gpmi { @@ -57,7 +75,7 @@ hog { pinctrl_hog: hoggrp { fsl,pins = < - MX6Q_PAD_EIM_D25__GPIO3_IO25 0x80000000 + MX6QDL_PAD_EIM_D25__GPIO3_IO25 0x80000000 >; }; }; @@ -65,8 +83,8 @@ arm2 { pinctrl_usdhc3_arm2: usdhc3grp-arm2 { fsl,pins = < - MX6Q_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 - MX6Q_PAD_NANDF_CS1__GPIO6_IO14 0x80000000 + MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 + MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x80000000 >; }; }; @@ -79,6 +97,14 @@ status = "okay"; }; +&usbotg { + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg_1>; + disable-over-current; + status = "okay"; +}; + &usdhc3 { cd-gpios = <&gpio6 11 0>; wp-gpios = <&gpio6 14 0>; @@ -97,6 +123,30 @@ status = "okay"; }; +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_2>; + fsl,dte-mode; + fsl,uart-has-rtscts; + status = "okay"; +}; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif_2>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_2>; + fsl,dte-mode; + fsl,uart-has-rtscts; + dma-names = "rx", "tx"; + dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; + status = "okay"; +}; + &uart4 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart4_1>; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-cubox-i.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-cubox-i.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-cubox-i.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-cubox-i.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2014 Russell King + */ +/dts-v1/; + +#include "imx6q.dtsi" +#include "imx6qdl-cubox-i.dtsi" + +/ { + model = "SolidRun Cubox-i Dual/Quad"; + compatible = "solidrun,cubox-i/q", "fsl,imx6q"; +}; + +&sata { + status = "okay"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-pinfunc.h linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-pinfunc.h --- linux-3.10.30/arch/arm/boot/dts/imx6q-pinfunc.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-pinfunc.h 2014-03-08 20:32:53.000000000 +0100 @@ -14,1028 +14,1032 @@ * The pin function ID is a tuple of * */ -#define MX6Q_PAD_SD2_DAT1__SD2_DATA1 0x04c 0x360 0x000 0x0 0x0 -#define MX6Q_PAD_SD2_DAT1__ECSPI5_SS0 0x04c 0x360 0x834 0x1 0x0 -#define MX6Q_PAD_SD2_DAT1__EIM_CS2_B 0x04c 0x360 0x000 0x2 0x0 -#define MX6Q_PAD_SD2_DAT1__AUD4_TXFS 0x04c 0x360 0x7c8 0x3 0x0 -#define MX6Q_PAD_SD2_DAT1__KEY_COL7 0x04c 0x360 0x8f0 0x4 0x0 -#define MX6Q_PAD_SD2_DAT1__GPIO1_IO14 0x04c 0x360 0x000 0x5 0x0 -#define MX6Q_PAD_SD2_DAT2__SD2_DATA2 0x050 0x364 0x000 0x0 0x0 -#define MX6Q_PAD_SD2_DAT2__ECSPI5_SS1 0x050 0x364 0x838 0x1 0x0 -#define MX6Q_PAD_SD2_DAT2__EIM_CS3_B 0x050 0x364 0x000 0x2 0x0 -#define MX6Q_PAD_SD2_DAT2__AUD4_TXD 0x050 0x364 0x7b8 0x3 0x0 -#define MX6Q_PAD_SD2_DAT2__KEY_ROW6 0x050 0x364 0x8f8 0x4 0x0 -#define MX6Q_PAD_SD2_DAT2__GPIO1_IO13 0x050 0x364 0x000 0x5 0x0 -#define MX6Q_PAD_SD2_DAT0__SD2_DATA0 0x054 0x368 0x000 0x0 0x0 -#define MX6Q_PAD_SD2_DAT0__ECSPI5_MISO 0x054 0x368 0x82c 0x1 0x0 -#define MX6Q_PAD_SD2_DAT0__AUD4_RXD 0x054 0x368 0x7b4 0x3 0x0 -#define MX6Q_PAD_SD2_DAT0__KEY_ROW7 0x054 0x368 0x8fc 0x4 0x0 -#define MX6Q_PAD_SD2_DAT0__GPIO1_IO15 0x054 0x368 0x000 0x5 0x0 -#define MX6Q_PAD_SD2_DAT0__DCIC2_OUT 0x054 0x368 0x000 0x6 0x0 -#define MX6Q_PAD_RGMII_TXC__USB_H2_DATA 0x058 0x36c 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_TXC__RGMII_TXC 0x058 0x36c 0x000 0x1 0x0 -#define MX6Q_PAD_RGMII_TXC__SPDIF_EXT_CLK 0x058 0x36c 0x918 0x2 0x0 -#define MX6Q_PAD_RGMII_TXC__GPIO6_IO19 0x058 0x36c 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_TXC__XTALOSC_REF_CLK_24M 0x058 0x36c 0x000 0x7 0x0 -#define MX6Q_PAD_RGMII_TD0__HSI_TX_READY 0x05c 0x370 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_TD0__RGMII_TD0 0x05c 0x370 0x000 0x1 0x0 -#define MX6Q_PAD_RGMII_TD0__GPIO6_IO20 0x05c 0x370 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_TD1__HSI_RX_FLAG 0x060 0x374 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_TD1__RGMII_TD1 0x060 0x374 0x000 0x1 0x0 -#define MX6Q_PAD_RGMII_TD1__GPIO6_IO21 0x060 0x374 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_TD2__HSI_RX_DATA 0x064 0x378 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_TD2__RGMII_TD2 0x064 0x378 0x000 0x1 0x0 -#define MX6Q_PAD_RGMII_TD2__GPIO6_IO22 0x064 0x378 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_TD3__HSI_RX_WAKE 0x068 0x37c 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_TD3__RGMII_TD3 0x068 0x37c 0x000 0x1 0x0 -#define MX6Q_PAD_RGMII_TD3__GPIO6_IO23 0x068 0x37c 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_RX_CTL__USB_H3_DATA 0x06c 0x380 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x06c 0x380 0x858 0x1 0x0 -#define MX6Q_PAD_RGMII_RX_CTL__GPIO6_IO24 0x06c 0x380 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_RD0__HSI_RX_READY 0x070 0x384 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_RD0__RGMII_RD0 0x070 0x384 0x848 0x1 0x0 -#define MX6Q_PAD_RGMII_RD0__GPIO6_IO25 0x070 0x384 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x074 0x388 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x074 0x388 0x000 0x1 0x0 -#define MX6Q_PAD_RGMII_TX_CTL__GPIO6_IO26 0x074 0x388 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_TX_CTL__ENET_REF_CLK 0x074 0x388 0x83c 0x7 0x0 -#define MX6Q_PAD_RGMII_RD1__HSI_TX_FLAG 0x078 0x38c 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_RD1__RGMII_RD1 0x078 0x38c 0x84c 0x1 0x0 -#define MX6Q_PAD_RGMII_RD1__GPIO6_IO27 0x078 0x38c 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_RD2__HSI_TX_DATA 0x07c 0x390 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_RD2__RGMII_RD2 0x07c 0x390 0x850 0x1 0x0 -#define MX6Q_PAD_RGMII_RD2__GPIO6_IO28 0x07c 0x390 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_RD3__HSI_TX_WAKE 0x080 0x394 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_RD3__RGMII_RD3 0x080 0x394 0x854 0x1 0x0 -#define MX6Q_PAD_RGMII_RD3__GPIO6_IO29 0x080 0x394 0x000 0x5 0x0 -#define MX6Q_PAD_RGMII_RXC__USB_H3_STROBE 0x084 0x398 0x000 0x0 0x0 -#define MX6Q_PAD_RGMII_RXC__RGMII_RXC 0x084 0x398 0x844 0x1 0x0 -#define MX6Q_PAD_RGMII_RXC__GPIO6_IO30 0x084 0x398 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A25__EIM_ADDR25 0x088 0x39c 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A25__ECSPI4_SS1 0x088 0x39c 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A25__ECSPI2_RDY 0x088 0x39c 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_A25__IPU1_DI1_PIN12 0x088 0x39c 0x000 0x3 0x0 -#define MX6Q_PAD_EIM_A25__IPU1_DI0_D1_CS 0x088 0x39c 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_A25__GPIO5_IO02 0x088 0x39c 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A25__HDMI_TX_CEC_LINE 0x088 0x39c 0x88c 0x6 0x0 -#define MX6Q_PAD_EIM_EB2__EIM_EB2_B 0x08c 0x3a0 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_EB2__ECSPI1_SS0 0x08c 0x3a0 0x800 0x1 0x0 -#define MX6Q_PAD_EIM_EB2__IPU2_CSI1_DATA19 0x08c 0x3a0 0x8d4 0x3 0x0 -#define MX6Q_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x08c 0x3a0 0x890 0x4 0x0 -#define MX6Q_PAD_EIM_EB2__GPIO2_IO30 0x08c 0x3a0 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_EB2__I2C2_SCL 0x08c 0x3a0 0x8a0 0x6 0x0 -#define MX6Q_PAD_EIM_EB2__SRC_BOOT_CFG30 0x08c 0x3a0 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_D16__EIM_DATA16 0x090 0x3a4 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D16__ECSPI1_SCLK 0x090 0x3a4 0x7f4 0x1 0x0 -#define MX6Q_PAD_EIM_D16__IPU1_DI0_PIN05 0x090 0x3a4 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D16__IPU2_CSI1_DATA18 0x090 0x3a4 0x8d0 0x3 0x0 -#define MX6Q_PAD_EIM_D16__HDMI_TX_DDC_SDA 0x090 0x3a4 0x894 0x4 0x0 -#define MX6Q_PAD_EIM_D16__GPIO3_IO16 0x090 0x3a4 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D16__I2C2_SDA 0x090 0x3a4 0x8a4 0x6 0x0 -#define MX6Q_PAD_EIM_D17__EIM_DATA17 0x094 0x3a8 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D17__ECSPI1_MISO 0x094 0x3a8 0x7f8 0x1 0x0 -#define MX6Q_PAD_EIM_D17__IPU1_DI0_PIN06 0x094 0x3a8 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D17__IPU2_CSI1_PIXCLK 0x094 0x3a8 0x8e0 0x3 0x0 -#define MX6Q_PAD_EIM_D17__DCIC1_OUT 0x094 0x3a8 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D17__GPIO3_IO17 0x094 0x3a8 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D17__I2C3_SCL 0x094 0x3a8 0x8a8 0x6 0x0 -#define MX6Q_PAD_EIM_D18__EIM_DATA18 0x098 0x3ac 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D18__ECSPI1_MOSI 0x098 0x3ac 0x7fc 0x1 0x0 -#define MX6Q_PAD_EIM_D18__IPU1_DI0_PIN07 0x098 0x3ac 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D18__IPU2_CSI1_DATA17 0x098 0x3ac 0x8cc 0x3 0x0 -#define MX6Q_PAD_EIM_D18__IPU1_DI1_D0_CS 0x098 0x3ac 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D18__GPIO3_IO18 0x098 0x3ac 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D18__I2C3_SDA 0x098 0x3ac 0x8ac 0x6 0x0 -#define MX6Q_PAD_EIM_D19__EIM_DATA19 0x09c 0x3b0 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D19__ECSPI1_SS1 0x09c 0x3b0 0x804 0x1 0x0 -#define MX6Q_PAD_EIM_D19__IPU1_DI0_PIN08 0x09c 0x3b0 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D19__IPU2_CSI1_DATA16 0x09c 0x3b0 0x8c8 0x3 0x0 -#define MX6Q_PAD_EIM_D19__UART1_CTS_B 0x09c 0x3b0 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D19__UART1_RTS_B 0x09c 0x3b0 0x91c 0x4 0x0 -#define MX6Q_PAD_EIM_D19__GPIO3_IO19 0x09c 0x3b0 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D19__EPIT1_OUT 0x09c 0x3b0 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_D20__EIM_DATA20 0x0a0 0x3b4 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D20__ECSPI4_SS0 0x0a0 0x3b4 0x824 0x1 0x0 -#define MX6Q_PAD_EIM_D20__IPU1_DI0_PIN16 0x0a0 0x3b4 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D20__IPU2_CSI1_DATA15 0x0a0 0x3b4 0x8c4 0x3 0x0 -#define MX6Q_PAD_EIM_D20__UART1_RTS_B 0x0a0 0x3b4 0x91c 0x4 0x1 -#define MX6Q_PAD_EIM_D20__UART1_CTS_B 0x0a0 0x3b4 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D20__GPIO3_IO20 0x0a0 0x3b4 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D20__EPIT2_OUT 0x0a0 0x3b4 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_D21__EIM_DATA21 0x0a4 0x3b8 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D21__ECSPI4_SCLK 0x0a4 0x3b8 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D21__IPU1_DI0_PIN17 0x0a4 0x3b8 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D21__IPU2_CSI1_DATA11 0x0a4 0x3b8 0x8b4 0x3 0x0 -#define MX6Q_PAD_EIM_D21__USB_OTG_OC 0x0a4 0x3b8 0x944 0x4 0x0 -#define MX6Q_PAD_EIM_D21__GPIO3_IO21 0x0a4 0x3b8 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D21__I2C1_SCL 0x0a4 0x3b8 0x898 0x6 0x0 -#define MX6Q_PAD_EIM_D21__SPDIF_IN 0x0a4 0x3b8 0x914 0x7 0x0 -#define MX6Q_PAD_EIM_D22__EIM_DATA22 0x0a8 0x3bc 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D22__ECSPI4_MISO 0x0a8 0x3bc 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D22__IPU1_DI0_PIN01 0x0a8 0x3bc 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D22__IPU2_CSI1_DATA10 0x0a8 0x3bc 0x8b0 0x3 0x0 -#define MX6Q_PAD_EIM_D22__USB_OTG_PWR 0x0a8 0x3bc 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D22__GPIO3_IO22 0x0a8 0x3bc 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D22__SPDIF_OUT 0x0a8 0x3bc 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_D23__EIM_DATA23 0x0ac 0x3c0 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D23__IPU1_DI0_D0_CS 0x0ac 0x3c0 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D23__UART3_CTS_B 0x0ac 0x3c0 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D23__UART3_RTS_B 0x0ac 0x3c0 0x92c 0x2 0x0 -#define MX6Q_PAD_EIM_D23__UART1_DCD_B 0x0ac 0x3c0 0x000 0x3 0x0 -#define MX6Q_PAD_EIM_D23__IPU2_CSI1_DATA_EN 0x0ac 0x3c0 0x8d8 0x4 0x0 -#define MX6Q_PAD_EIM_D23__GPIO3_IO23 0x0ac 0x3c0 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D23__IPU1_DI1_PIN02 0x0ac 0x3c0 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_D23__IPU1_DI1_PIN14 0x0ac 0x3c0 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_EB3__EIM_EB3_B 0x0b0 0x3c4 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_EB3__ECSPI4_RDY 0x0b0 0x3c4 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_EB3__UART3_RTS_B 0x0b0 0x3c4 0x92c 0x2 0x1 -#define MX6Q_PAD_EIM_EB3__UART3_CTS_B 0x0b0 0x3c4 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_EB3__UART1_RI_B 0x0b0 0x3c4 0x000 0x3 0x0 -#define MX6Q_PAD_EIM_EB3__IPU2_CSI1_HSYNC 0x0b0 0x3c4 0x8dc 0x4 0x0 -#define MX6Q_PAD_EIM_EB3__GPIO2_IO31 0x0b0 0x3c4 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_EB3__IPU1_DI1_PIN03 0x0b0 0x3c4 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_EB3__SRC_BOOT_CFG31 0x0b0 0x3c4 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_D24__EIM_DATA24 0x0b4 0x3c8 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D24__ECSPI4_SS2 0x0b4 0x3c8 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D24__UART3_TX_DATA 0x0b4 0x3c8 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D24__UART3_RX_DATA 0x0b4 0x3c8 0x930 0x2 0x0 -#define MX6Q_PAD_EIM_D24__ECSPI1_SS2 0x0b4 0x3c8 0x808 0x3 0x0 -#define MX6Q_PAD_EIM_D24__ECSPI2_SS2 0x0b4 0x3c8 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D24__GPIO3_IO24 0x0b4 0x3c8 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D24__AUD5_RXFS 0x0b4 0x3c8 0x7d8 0x6 0x0 -#define MX6Q_PAD_EIM_D24__UART1_DTR_B 0x0b4 0x3c8 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_D25__EIM_DATA25 0x0b8 0x3cc 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D25__ECSPI4_SS3 0x0b8 0x3cc 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D25__UART3_RX_DATA 0x0b8 0x3cc 0x930 0x2 0x1 -#define MX6Q_PAD_EIM_D25__UART3_TX_DATA 0x0b8 0x3cc 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D25__ECSPI1_SS3 0x0b8 0x3cc 0x80c 0x3 0x0 -#define MX6Q_PAD_EIM_D25__ECSPI2_SS3 0x0b8 0x3cc 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D25__GPIO3_IO25 0x0b8 0x3cc 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D25__AUD5_RXC 0x0b8 0x3cc 0x7d4 0x6 0x0 -#define MX6Q_PAD_EIM_D25__UART1_DSR_B 0x0b8 0x3cc 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_D26__EIM_DATA26 0x0bc 0x3d0 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D26__IPU1_DI1_PIN11 0x0bc 0x3d0 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D26__IPU1_CSI0_DATA01 0x0bc 0x3d0 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D26__IPU2_CSI1_DATA14 0x0bc 0x3d0 0x8c0 0x3 0x0 -#define MX6Q_PAD_EIM_D26__UART2_TX_DATA 0x0bc 0x3d0 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D26__UART2_RX_DATA 0x0bc 0x3d0 0x928 0x4 0x0 -#define MX6Q_PAD_EIM_D26__GPIO3_IO26 0x0bc 0x3d0 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D26__IPU1_SISG2 0x0bc 0x3d0 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_D26__IPU1_DISP1_DATA22 0x0bc 0x3d0 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_D27__EIM_DATA27 0x0c0 0x3d4 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D27__IPU1_DI1_PIN13 0x0c0 0x3d4 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D27__IPU1_CSI0_DATA00 0x0c0 0x3d4 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D27__IPU2_CSI1_DATA13 0x0c0 0x3d4 0x8bc 0x3 0x0 -#define MX6Q_PAD_EIM_D27__UART2_RX_DATA 0x0c0 0x3d4 0x928 0x4 0x1 -#define MX6Q_PAD_EIM_D27__UART2_TX_DATA 0x0c0 0x3d4 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D27__GPIO3_IO27 0x0c0 0x3d4 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D27__IPU1_SISG3 0x0c0 0x3d4 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_D27__IPU1_DISP1_DATA23 0x0c0 0x3d4 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_D28__EIM_DATA28 0x0c4 0x3d8 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D28__I2C1_SDA 0x0c4 0x3d8 0x89c 0x1 0x0 -#define MX6Q_PAD_EIM_D28__ECSPI4_MOSI 0x0c4 0x3d8 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D28__IPU2_CSI1_DATA12 0x0c4 0x3d8 0x8b8 0x3 0x0 -#define MX6Q_PAD_EIM_D28__UART2_CTS_B 0x0c4 0x3d8 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D28__UART2_RTS_B 0x0c4 0x3d8 0x924 0x4 0x0 -#define MX6Q_PAD_EIM_D28__GPIO3_IO28 0x0c4 0x3d8 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D28__IPU1_EXT_TRIG 0x0c4 0x3d8 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_D28__IPU1_DI0_PIN13 0x0c4 0x3d8 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_D29__EIM_DATA29 0x0c8 0x3dc 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D29__IPU1_DI1_PIN15 0x0c8 0x3dc 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D29__ECSPI4_SS0 0x0c8 0x3dc 0x824 0x2 0x1 -#define MX6Q_PAD_EIM_D29__UART2_RTS_B 0x0c8 0x3dc 0x924 0x4 0x1 -#define MX6Q_PAD_EIM_D29__UART2_CTS_B 0x0c8 0x3dc 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D29__GPIO3_IO29 0x0c8 0x3dc 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D29__IPU2_CSI1_VSYNC 0x0c8 0x3dc 0x8e4 0x6 0x0 -#define MX6Q_PAD_EIM_D29__IPU1_DI0_PIN14 0x0c8 0x3dc 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_D30__EIM_DATA30 0x0cc 0x3e0 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D30__IPU1_DISP1_DATA21 0x0cc 0x3e0 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D30__IPU1_DI0_PIN11 0x0cc 0x3e0 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D30__IPU1_CSI0_DATA03 0x0cc 0x3e0 0x000 0x3 0x0 -#define MX6Q_PAD_EIM_D30__UART3_CTS_B 0x0cc 0x3e0 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D30__UART3_RTS_B 0x0cc 0x3e0 0x92c 0x4 0x2 -#define MX6Q_PAD_EIM_D30__GPIO3_IO30 0x0cc 0x3e0 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D30__USB_H1_OC 0x0cc 0x3e0 0x948 0x6 0x0 -#define MX6Q_PAD_EIM_D31__EIM_DATA31 0x0d0 0x3e4 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_D31__IPU1_DISP1_DATA20 0x0d0 0x3e4 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_D31__IPU1_DI0_PIN12 0x0d0 0x3e4 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_D31__IPU1_CSI0_DATA02 0x0d0 0x3e4 0x000 0x3 0x0 -#define MX6Q_PAD_EIM_D31__UART3_RTS_B 0x0d0 0x3e4 0x92c 0x4 0x3 -#define MX6Q_PAD_EIM_D31__UART3_CTS_B 0x0d0 0x3e4 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_D31__GPIO3_IO31 0x0d0 0x3e4 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_D31__USB_H1_PWR 0x0d0 0x3e4 0x000 0x6 0x0 -#define MX6Q_PAD_EIM_A24__EIM_ADDR24 0x0d4 0x3e8 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A24__IPU1_DISP1_DATA19 0x0d4 0x3e8 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A24__IPU2_CSI1_DATA19 0x0d4 0x3e8 0x8d4 0x2 0x1 -#define MX6Q_PAD_EIM_A24__IPU2_SISG2 0x0d4 0x3e8 0x000 0x3 0x0 -#define MX6Q_PAD_EIM_A24__IPU1_SISG2 0x0d4 0x3e8 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_A24__GPIO5_IO04 0x0d4 0x3e8 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A24__SRC_BOOT_CFG24 0x0d4 0x3e8 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_A23__EIM_ADDR23 0x0d8 0x3ec 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A23__IPU1_DISP1_DATA18 0x0d8 0x3ec 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A23__IPU2_CSI1_DATA18 0x0d8 0x3ec 0x8d0 0x2 0x1 -#define MX6Q_PAD_EIM_A23__IPU2_SISG3 0x0d8 0x3ec 0x000 0x3 0x0 -#define MX6Q_PAD_EIM_A23__IPU1_SISG3 0x0d8 0x3ec 0x000 0x4 0x0 -#define MX6Q_PAD_EIM_A23__GPIO6_IO06 0x0d8 0x3ec 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A23__SRC_BOOT_CFG23 0x0d8 0x3ec 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_A22__EIM_ADDR22 0x0dc 0x3f0 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A22__IPU1_DISP1_DATA17 0x0dc 0x3f0 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A22__IPU2_CSI1_DATA17 0x0dc 0x3f0 0x8cc 0x2 0x1 -#define MX6Q_PAD_EIM_A22__GPIO2_IO16 0x0dc 0x3f0 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A22__SRC_BOOT_CFG22 0x0dc 0x3f0 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_A21__EIM_ADDR21 0x0e0 0x3f4 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A21__IPU1_DISP1_DATA16 0x0e0 0x3f4 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A21__IPU2_CSI1_DATA16 0x0e0 0x3f4 0x8c8 0x2 0x1 -#define MX6Q_PAD_EIM_A21__GPIO2_IO17 0x0e0 0x3f4 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A21__SRC_BOOT_CFG21 0x0e0 0x3f4 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_A20__EIM_ADDR20 0x0e4 0x3f8 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A20__IPU1_DISP1_DATA15 0x0e4 0x3f8 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A20__IPU2_CSI1_DATA15 0x0e4 0x3f8 0x8c4 0x2 0x1 -#define MX6Q_PAD_EIM_A20__GPIO2_IO18 0x0e4 0x3f8 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A20__SRC_BOOT_CFG20 0x0e4 0x3f8 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_A19__EIM_ADDR19 0x0e8 0x3fc 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A19__IPU1_DISP1_DATA14 0x0e8 0x3fc 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A19__IPU2_CSI1_DATA14 0x0e8 0x3fc 0x8c0 0x2 0x1 -#define MX6Q_PAD_EIM_A19__GPIO2_IO19 0x0e8 0x3fc 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A19__SRC_BOOT_CFG19 0x0e8 0x3fc 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_A18__EIM_ADDR18 0x0ec 0x400 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A18__IPU1_DISP1_DATA13 0x0ec 0x400 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A18__IPU2_CSI1_DATA13 0x0ec 0x400 0x8bc 0x2 0x1 -#define MX6Q_PAD_EIM_A18__GPIO2_IO20 0x0ec 0x400 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A18__SRC_BOOT_CFG18 0x0ec 0x400 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_A17__EIM_ADDR17 0x0f0 0x404 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A17__IPU1_DISP1_DATA12 0x0f0 0x404 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A17__IPU2_CSI1_DATA12 0x0f0 0x404 0x8b8 0x2 0x1 -#define MX6Q_PAD_EIM_A17__GPIO2_IO21 0x0f0 0x404 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A17__SRC_BOOT_CFG17 0x0f0 0x404 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_A16__EIM_ADDR16 0x0f4 0x408 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_A16__IPU1_DI1_DISP_CLK 0x0f4 0x408 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_A16__IPU2_CSI1_PIXCLK 0x0f4 0x408 0x8e0 0x2 0x1 -#define MX6Q_PAD_EIM_A16__GPIO2_IO22 0x0f4 0x408 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_A16__SRC_BOOT_CFG16 0x0f4 0x408 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_CS0__EIM_CS0_B 0x0f8 0x40c 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_CS0__IPU1_DI1_PIN05 0x0f8 0x40c 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_CS0__ECSPI2_SCLK 0x0f8 0x40c 0x810 0x2 0x0 -#define MX6Q_PAD_EIM_CS0__GPIO2_IO23 0x0f8 0x40c 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_CS1__EIM_CS1_B 0x0fc 0x410 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_CS1__IPU1_DI1_PIN06 0x0fc 0x410 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_CS1__ECSPI2_MOSI 0x0fc 0x410 0x818 0x2 0x0 -#define MX6Q_PAD_EIM_CS1__GPIO2_IO24 0x0fc 0x410 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_OE__EIM_OE_B 0x100 0x414 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_OE__IPU1_DI1_PIN07 0x100 0x414 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_OE__ECSPI2_MISO 0x100 0x414 0x814 0x2 0x0 -#define MX6Q_PAD_EIM_OE__GPIO2_IO25 0x100 0x414 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_RW__EIM_RW 0x104 0x418 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_RW__IPU1_DI1_PIN08 0x104 0x418 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_RW__ECSPI2_SS0 0x104 0x418 0x81c 0x2 0x0 -#define MX6Q_PAD_EIM_RW__GPIO2_IO26 0x104 0x418 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_RW__SRC_BOOT_CFG29 0x104 0x418 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_LBA__EIM_LBA_B 0x108 0x41c 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_LBA__IPU1_DI1_PIN17 0x108 0x41c 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_LBA__ECSPI2_SS1 0x108 0x41c 0x820 0x2 0x0 -#define MX6Q_PAD_EIM_LBA__GPIO2_IO27 0x108 0x41c 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_LBA__SRC_BOOT_CFG26 0x108 0x41c 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_EB0__EIM_EB0_B 0x10c 0x420 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_EB0__IPU1_DISP1_DATA11 0x10c 0x420 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_EB0__IPU2_CSI1_DATA11 0x10c 0x420 0x8b4 0x2 0x1 -#define MX6Q_PAD_EIM_EB0__CCM_PMIC_READY 0x10c 0x420 0x7f0 0x4 0x0 -#define MX6Q_PAD_EIM_EB0__GPIO2_IO28 0x10c 0x420 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_EB0__SRC_BOOT_CFG27 0x10c 0x420 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_EB1__EIM_EB1_B 0x110 0x424 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_EB1__IPU1_DISP1_DATA10 0x110 0x424 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_EB1__IPU2_CSI1_DATA10 0x110 0x424 0x8b0 0x2 0x1 -#define MX6Q_PAD_EIM_EB1__GPIO2_IO29 0x110 0x424 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_EB1__SRC_BOOT_CFG28 0x110 0x424 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA0__EIM_AD00 0x114 0x428 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA0__IPU1_DISP1_DATA09 0x114 0x428 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA0__IPU2_CSI1_DATA09 0x114 0x428 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA0__GPIO3_IO00 0x114 0x428 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA0__SRC_BOOT_CFG00 0x114 0x428 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA1__EIM_AD01 0x118 0x42c 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA1__IPU1_DISP1_DATA08 0x118 0x42c 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA1__IPU2_CSI1_DATA08 0x118 0x42c 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA1__GPIO3_IO01 0x118 0x42c 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA1__SRC_BOOT_CFG01 0x118 0x42c 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA2__EIM_AD02 0x11c 0x430 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA2__IPU1_DISP1_DATA07 0x11c 0x430 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA2__IPU2_CSI1_DATA07 0x11c 0x430 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA2__GPIO3_IO02 0x11c 0x430 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA2__SRC_BOOT_CFG02 0x11c 0x430 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA3__EIM_AD03 0x120 0x434 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA3__IPU1_DISP1_DATA06 0x120 0x434 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA3__IPU2_CSI1_DATA06 0x120 0x434 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA3__GPIO3_IO03 0x120 0x434 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA3__SRC_BOOT_CFG03 0x120 0x434 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA4__EIM_AD04 0x124 0x438 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA4__IPU1_DISP1_DATA05 0x124 0x438 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA4__IPU2_CSI1_DATA05 0x124 0x438 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA4__GPIO3_IO04 0x124 0x438 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA4__SRC_BOOT_CFG04 0x124 0x438 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA5__EIM_AD05 0x128 0x43c 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA5__IPU1_DISP1_DATA04 0x128 0x43c 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA5__IPU2_CSI1_DATA04 0x128 0x43c 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA5__GPIO3_IO05 0x128 0x43c 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA5__SRC_BOOT_CFG05 0x128 0x43c 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA6__EIM_AD06 0x12c 0x440 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA6__IPU1_DISP1_DATA03 0x12c 0x440 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA6__IPU2_CSI1_DATA03 0x12c 0x440 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA6__GPIO3_IO06 0x12c 0x440 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA6__SRC_BOOT_CFG06 0x12c 0x440 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA7__EIM_AD07 0x130 0x444 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA7__IPU1_DISP1_DATA02 0x130 0x444 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA7__IPU2_CSI1_DATA02 0x130 0x444 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA7__GPIO3_IO07 0x130 0x444 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA7__SRC_BOOT_CFG07 0x130 0x444 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA8__EIM_AD08 0x134 0x448 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA8__IPU1_DISP1_DATA01 0x134 0x448 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA8__IPU2_CSI1_DATA01 0x134 0x448 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA8__GPIO3_IO08 0x134 0x448 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA8__SRC_BOOT_CFG08 0x134 0x448 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA9__EIM_AD09 0x138 0x44c 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA9__IPU1_DISP1_DATA00 0x138 0x44c 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA9__IPU2_CSI1_DATA00 0x138 0x44c 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA9__GPIO3_IO09 0x138 0x44c 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA9__SRC_BOOT_CFG09 0x138 0x44c 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA10__EIM_AD10 0x13c 0x450 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA10__IPU1_DI1_PIN15 0x13c 0x450 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA10__IPU2_CSI1_DATA_EN 0x13c 0x450 0x8d8 0x2 0x1 -#define MX6Q_PAD_EIM_DA10__GPIO3_IO10 0x13c 0x450 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA10__SRC_BOOT_CFG10 0x13c 0x450 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA11__EIM_AD11 0x140 0x454 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA11__IPU1_DI1_PIN02 0x140 0x454 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA11__IPU2_CSI1_HSYNC 0x140 0x454 0x8dc 0x2 0x1 -#define MX6Q_PAD_EIM_DA11__GPIO3_IO11 0x140 0x454 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA11__SRC_BOOT_CFG11 0x140 0x454 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA12__EIM_AD12 0x144 0x458 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA12__IPU1_DI1_PIN03 0x144 0x458 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA12__IPU2_CSI1_VSYNC 0x144 0x458 0x8e4 0x2 0x1 -#define MX6Q_PAD_EIM_DA12__GPIO3_IO12 0x144 0x458 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA12__SRC_BOOT_CFG12 0x144 0x458 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA13__EIM_AD13 0x148 0x45c 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA13__IPU1_DI1_D0_CS 0x148 0x45c 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA13__GPIO3_IO13 0x148 0x45c 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA13__SRC_BOOT_CFG13 0x148 0x45c 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA14__EIM_AD14 0x14c 0x460 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA14__IPU1_DI1_D1_CS 0x14c 0x460 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA14__GPIO3_IO14 0x14c 0x460 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA14__SRC_BOOT_CFG14 0x14c 0x460 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_DA15__EIM_AD15 0x150 0x464 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_DA15__IPU1_DI1_PIN01 0x150 0x464 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_DA15__IPU1_DI1_PIN04 0x150 0x464 0x000 0x2 0x0 -#define MX6Q_PAD_EIM_DA15__GPIO3_IO15 0x150 0x464 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_DA15__SRC_BOOT_CFG15 0x150 0x464 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_WAIT__EIM_WAIT_B 0x154 0x468 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_WAIT__EIM_DTACK_B 0x154 0x468 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_WAIT__GPIO5_IO00 0x154 0x468 0x000 0x5 0x0 -#define MX6Q_PAD_EIM_WAIT__SRC_BOOT_CFG25 0x154 0x468 0x000 0x7 0x0 -#define MX6Q_PAD_EIM_BCLK__EIM_BCLK 0x158 0x46c 0x000 0x0 0x0 -#define MX6Q_PAD_EIM_BCLK__IPU1_DI1_PIN16 0x158 0x46c 0x000 0x1 0x0 -#define MX6Q_PAD_EIM_BCLK__GPIO6_IO31 0x158 0x46c 0x000 0x5 0x0 -#define MX6Q_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x15c 0x470 0x000 0x0 0x0 -#define MX6Q_PAD_DI0_DISP_CLK__IPU2_DI0_DISP_CLK 0x15c 0x470 0x000 0x1 0x0 -#define MX6Q_PAD_DI0_DISP_CLK__GPIO4_IO16 0x15c 0x470 0x000 0x5 0x0 -#define MX6Q_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x160 0x474 0x000 0x0 0x0 -#define MX6Q_PAD_DI0_PIN15__IPU2_DI0_PIN15 0x160 0x474 0x000 0x1 0x0 -#define MX6Q_PAD_DI0_PIN15__AUD6_TXC 0x160 0x474 0x000 0x2 0x0 -#define MX6Q_PAD_DI0_PIN15__GPIO4_IO17 0x160 0x474 0x000 0x5 0x0 -#define MX6Q_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x164 0x478 0x000 0x0 0x0 -#define MX6Q_PAD_DI0_PIN2__IPU2_DI0_PIN02 0x164 0x478 0x000 0x1 0x0 -#define MX6Q_PAD_DI0_PIN2__AUD6_TXD 0x164 0x478 0x000 0x2 0x0 -#define MX6Q_PAD_DI0_PIN2__GPIO4_IO18 0x164 0x478 0x000 0x5 0x0 -#define MX6Q_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x168 0x47c 0x000 0x0 0x0 -#define MX6Q_PAD_DI0_PIN3__IPU2_DI0_PIN03 0x168 0x47c 0x000 0x1 0x0 -#define MX6Q_PAD_DI0_PIN3__AUD6_TXFS 0x168 0x47c 0x000 0x2 0x0 -#define MX6Q_PAD_DI0_PIN3__GPIO4_IO19 0x168 0x47c 0x000 0x5 0x0 -#define MX6Q_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x16c 0x480 0x000 0x0 0x0 -#define MX6Q_PAD_DI0_PIN4__IPU2_DI0_PIN04 0x16c 0x480 0x000 0x1 0x0 -#define MX6Q_PAD_DI0_PIN4__AUD6_RXD 0x16c 0x480 0x000 0x2 0x0 -#define MX6Q_PAD_DI0_PIN4__SD1_WP 0x16c 0x480 0x94c 0x3 0x0 -#define MX6Q_PAD_DI0_PIN4__GPIO4_IO20 0x16c 0x480 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x170 0x484 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT0__IPU2_DISP0_DATA00 0x170 0x484 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT0__ECSPI3_SCLK 0x170 0x484 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT0__GPIO4_IO21 0x170 0x484 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x174 0x488 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT1__IPU2_DISP0_DATA01 0x174 0x488 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT1__ECSPI3_MOSI 0x174 0x488 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT1__GPIO4_IO22 0x174 0x488 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x178 0x48c 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT2__IPU2_DISP0_DATA02 0x178 0x48c 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT2__ECSPI3_MISO 0x178 0x48c 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT2__GPIO4_IO23 0x178 0x48c 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x17c 0x490 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT3__IPU2_DISP0_DATA03 0x17c 0x490 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT3__ECSPI3_SS0 0x17c 0x490 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT3__GPIO4_IO24 0x17c 0x490 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x180 0x494 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT4__IPU2_DISP0_DATA04 0x180 0x494 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT4__ECSPI3_SS1 0x180 0x494 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT4__GPIO4_IO25 0x180 0x494 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x184 0x498 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT5__IPU2_DISP0_DATA05 0x184 0x498 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT5__ECSPI3_SS2 0x184 0x498 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT5__AUD6_RXFS 0x184 0x498 0x000 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT5__GPIO4_IO26 0x184 0x498 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x188 0x49c 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT6__IPU2_DISP0_DATA06 0x188 0x49c 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT6__ECSPI3_SS3 0x188 0x49c 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT6__AUD6_RXC 0x188 0x49c 0x000 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT6__GPIO4_IO27 0x188 0x49c 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x18c 0x4a0 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT7__IPU2_DISP0_DATA07 0x18c 0x4a0 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT7__ECSPI3_RDY 0x18c 0x4a0 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT7__GPIO4_IO28 0x18c 0x4a0 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x190 0x4a4 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT8__IPU2_DISP0_DATA08 0x190 0x4a4 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT8__PWM1_OUT 0x190 0x4a4 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT8__WDOG1_B 0x190 0x4a4 0x000 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT8__GPIO4_IO29 0x190 0x4a4 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x194 0x4a8 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT9__IPU2_DISP0_DATA09 0x194 0x4a8 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT9__PWM2_OUT 0x194 0x4a8 0x000 0x2 0x0 -#define MX6Q_PAD_DISP0_DAT9__WDOG2_B 0x194 0x4a8 0x000 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT9__GPIO4_IO30 0x194 0x4a8 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x198 0x4ac 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT10__IPU2_DISP0_DATA10 0x198 0x4ac 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT10__GPIO4_IO31 0x198 0x4ac 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x19c 0x4b0 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT11__IPU2_DISP0_DATA11 0x19c 0x4b0 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT11__GPIO5_IO05 0x19c 0x4b0 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x1a0 0x4b4 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT12__IPU2_DISP0_DATA12 0x1a0 0x4b4 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT12__GPIO5_IO06 0x1a0 0x4b4 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x1a4 0x4b8 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT13__IPU2_DISP0_DATA13 0x1a4 0x4b8 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT13__AUD5_RXFS 0x1a4 0x4b8 0x7d8 0x3 0x1 -#define MX6Q_PAD_DISP0_DAT13__GPIO5_IO07 0x1a4 0x4b8 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x1a8 0x4bc 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT14__IPU2_DISP0_DATA14 0x1a8 0x4bc 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT14__AUD5_RXC 0x1a8 0x4bc 0x7d4 0x3 0x1 -#define MX6Q_PAD_DISP0_DAT14__GPIO5_IO08 0x1a8 0x4bc 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x1ac 0x4c0 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT15__IPU2_DISP0_DATA15 0x1ac 0x4c0 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT15__ECSPI1_SS1 0x1ac 0x4c0 0x804 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT15__ECSPI2_SS1 0x1ac 0x4c0 0x820 0x3 0x1 -#define MX6Q_PAD_DISP0_DAT15__GPIO5_IO09 0x1ac 0x4c0 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x1b0 0x4c4 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT16__IPU2_DISP0_DATA16 0x1b0 0x4c4 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT16__ECSPI2_MOSI 0x1b0 0x4c4 0x818 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT16__AUD5_TXC 0x1b0 0x4c4 0x7dc 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT16__SDMA_EXT_EVENT0 0x1b0 0x4c4 0x90c 0x4 0x0 -#define MX6Q_PAD_DISP0_DAT16__GPIO5_IO10 0x1b0 0x4c4 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x1b4 0x4c8 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT17__IPU2_DISP0_DATA17 0x1b4 0x4c8 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT17__ECSPI2_MISO 0x1b4 0x4c8 0x814 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT17__AUD5_TXD 0x1b4 0x4c8 0x7d0 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT17__SDMA_EXT_EVENT1 0x1b4 0x4c8 0x910 0x4 0x0 -#define MX6Q_PAD_DISP0_DAT17__GPIO5_IO11 0x1b4 0x4c8 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x1b8 0x4cc 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT18__IPU2_DISP0_DATA18 0x1b8 0x4cc 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT18__ECSPI2_SS0 0x1b8 0x4cc 0x81c 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT18__AUD5_TXFS 0x1b8 0x4cc 0x7e0 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT18__AUD4_RXFS 0x1b8 0x4cc 0x7c0 0x4 0x0 -#define MX6Q_PAD_DISP0_DAT18__GPIO5_IO12 0x1b8 0x4cc 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT18__EIM_CS2_B 0x1b8 0x4cc 0x000 0x7 0x0 -#define MX6Q_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x1bc 0x4d0 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT19__IPU2_DISP0_DATA19 0x1bc 0x4d0 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT19__ECSPI2_SCLK 0x1bc 0x4d0 0x810 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT19__AUD5_RXD 0x1bc 0x4d0 0x7cc 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT19__AUD4_RXC 0x1bc 0x4d0 0x7bc 0x4 0x0 -#define MX6Q_PAD_DISP0_DAT19__GPIO5_IO13 0x1bc 0x4d0 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT19__EIM_CS3_B 0x1bc 0x4d0 0x000 0x7 0x0 -#define MX6Q_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x1c0 0x4d4 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT20__IPU2_DISP0_DATA20 0x1c0 0x4d4 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT20__ECSPI1_SCLK 0x1c0 0x4d4 0x7f4 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT20__AUD4_TXC 0x1c0 0x4d4 0x7c4 0x3 0x0 -#define MX6Q_PAD_DISP0_DAT20__GPIO5_IO14 0x1c0 0x4d4 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x1c4 0x4d8 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT21__IPU2_DISP0_DATA21 0x1c4 0x4d8 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT21__ECSPI1_MOSI 0x1c4 0x4d8 0x7fc 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT21__AUD4_TXD 0x1c4 0x4d8 0x7b8 0x3 0x1 -#define MX6Q_PAD_DISP0_DAT21__GPIO5_IO15 0x1c4 0x4d8 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x1c8 0x4dc 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT22__IPU2_DISP0_DATA22 0x1c8 0x4dc 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT22__ECSPI1_MISO 0x1c8 0x4dc 0x7f8 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT22__AUD4_TXFS 0x1c8 0x4dc 0x7c8 0x3 0x1 -#define MX6Q_PAD_DISP0_DAT22__GPIO5_IO16 0x1c8 0x4dc 0x000 0x5 0x0 -#define MX6Q_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x1cc 0x4e0 0x000 0x0 0x0 -#define MX6Q_PAD_DISP0_DAT23__IPU2_DISP0_DATA23 0x1cc 0x4e0 0x000 0x1 0x0 -#define MX6Q_PAD_DISP0_DAT23__ECSPI1_SS0 0x1cc 0x4e0 0x800 0x2 0x1 -#define MX6Q_PAD_DISP0_DAT23__AUD4_RXD 0x1cc 0x4e0 0x7b4 0x3 0x1 -#define MX6Q_PAD_DISP0_DAT23__GPIO5_IO17 0x1cc 0x4e0 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_MDIO__ENET_MDIO 0x1d0 0x4e4 0x840 0x1 0x0 -#define MX6Q_PAD_ENET_MDIO__ESAI_RX_CLK 0x1d0 0x4e4 0x86c 0x2 0x0 -#define MX6Q_PAD_ENET_MDIO__ENET_1588_EVENT1_OUT 0x1d0 0x4e4 0x000 0x4 0x0 -#define MX6Q_PAD_ENET_MDIO__GPIO1_IO22 0x1d0 0x4e4 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_MDIO__SPDIF_LOCK 0x1d0 0x4e4 0x000 0x6 0x0 -#define MX6Q_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1d4 0x4e8 0x000 0x1 0x0 -#define MX6Q_PAD_ENET_REF_CLK__ESAI_RX_FS 0x1d4 0x4e8 0x85c 0x2 0x0 -#define MX6Q_PAD_ENET_REF_CLK__GPIO1_IO23 0x1d4 0x4e8 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_REF_CLK__SPDIF_SR_CLK 0x1d4 0x4e8 0x000 0x6 0x0 -#define MX6Q_PAD_ENET_RX_ER__USB_OTG_ID 0x1d8 0x4ec 0x000 0x0 0x0 -#define MX6Q_PAD_ENET_RX_ER__ENET_RX_ER 0x1d8 0x4ec 0x000 0x1 0x0 -#define MX6Q_PAD_ENET_RX_ER__ESAI_RX_HF_CLK 0x1d8 0x4ec 0x864 0x2 0x0 -#define MX6Q_PAD_ENET_RX_ER__SPDIF_IN 0x1d8 0x4ec 0x914 0x3 0x1 -#define MX6Q_PAD_ENET_RX_ER__ENET_1588_EVENT2_OUT 0x1d8 0x4ec 0x000 0x4 0x0 -#define MX6Q_PAD_ENET_RX_ER__GPIO1_IO24 0x1d8 0x4ec 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_CRS_DV__ENET_RX_EN 0x1dc 0x4f0 0x858 0x1 0x1 -#define MX6Q_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1dc 0x4f0 0x870 0x2 0x0 -#define MX6Q_PAD_ENET_CRS_DV__SPDIF_EXT_CLK 0x1dc 0x4f0 0x918 0x3 0x1 -#define MX6Q_PAD_ENET_CRS_DV__GPIO1_IO25 0x1dc 0x4f0 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_RXD1__MLB_SIG 0x1e0 0x4f4 0x908 0x0 0x0 -#define MX6Q_PAD_ENET_RXD1__ENET_RX_DATA1 0x1e0 0x4f4 0x84c 0x1 0x1 -#define MX6Q_PAD_ENET_RXD1__ESAI_TX_FS 0x1e0 0x4f4 0x860 0x2 0x0 -#define MX6Q_PAD_ENET_RXD1__ENET_1588_EVENT3_OUT 0x1e0 0x4f4 0x000 0x4 0x0 -#define MX6Q_PAD_ENET_RXD1__GPIO1_IO26 0x1e0 0x4f4 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_RXD0__ENET_RX_DATA0 0x1e4 0x4f8 0x848 0x1 0x1 -#define MX6Q_PAD_ENET_RXD0__ESAI_TX_HF_CLK 0x1e4 0x4f8 0x868 0x2 0x0 -#define MX6Q_PAD_ENET_RXD0__SPDIF_OUT 0x1e4 0x4f8 0x000 0x3 0x0 -#define MX6Q_PAD_ENET_RXD0__GPIO1_IO27 0x1e4 0x4f8 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_TX_EN__ENET_TX_EN 0x1e8 0x4fc 0x000 0x1 0x0 -#define MX6Q_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1e8 0x4fc 0x880 0x2 0x0 -#define MX6Q_PAD_ENET_TX_EN__GPIO1_IO28 0x1e8 0x4fc 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_TXD1__MLB_CLK 0x1ec 0x500 0x900 0x0 0x0 -#define MX6Q_PAD_ENET_TXD1__ENET_TX_DATA1 0x1ec 0x500 0x000 0x1 0x0 -#define MX6Q_PAD_ENET_TXD1__ESAI_TX2_RX3 0x1ec 0x500 0x87c 0x2 0x0 -#define MX6Q_PAD_ENET_TXD1__ENET_1588_EVENT0_IN 0x1ec 0x500 0x000 0x4 0x0 -#define MX6Q_PAD_ENET_TXD1__GPIO1_IO29 0x1ec 0x500 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_TXD0__ENET_TX_DATA0 0x1f0 0x504 0x000 0x1 0x0 -#define MX6Q_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1f0 0x504 0x884 0x2 0x0 -#define MX6Q_PAD_ENET_TXD0__GPIO1_IO30 0x1f0 0x504 0x000 0x5 0x0 -#define MX6Q_PAD_ENET_MDC__MLB_DATA 0x1f4 0x508 0x904 0x0 0x0 -#define MX6Q_PAD_ENET_MDC__ENET_MDC 0x1f4 0x508 0x000 0x1 0x0 -#define MX6Q_PAD_ENET_MDC__ESAI_TX5_RX0 0x1f4 0x508 0x888 0x2 0x0 -#define MX6Q_PAD_ENET_MDC__ENET_1588_EVENT1_IN 0x1f4 0x508 0x000 0x4 0x0 -#define MX6Q_PAD_ENET_MDC__GPIO1_IO31 0x1f4 0x508 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_COL0__ECSPI1_SCLK 0x1f8 0x5c8 0x7f4 0x0 0x2 -#define MX6Q_PAD_KEY_COL0__ENET_RX_DATA3 0x1f8 0x5c8 0x854 0x1 0x1 -#define MX6Q_PAD_KEY_COL0__AUD5_TXC 0x1f8 0x5c8 0x7dc 0x2 0x1 -#define MX6Q_PAD_KEY_COL0__KEY_COL0 0x1f8 0x5c8 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_COL0__UART4_TX_DATA 0x1f8 0x5c8 0x000 0x4 0x0 -#define MX6Q_PAD_KEY_COL0__UART4_RX_DATA 0x1f8 0x5c8 0x938 0x4 0x0 -#define MX6Q_PAD_KEY_COL0__GPIO4_IO06 0x1f8 0x5c8 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_COL0__DCIC1_OUT 0x1f8 0x5c8 0x000 0x6 0x0 -#define MX6Q_PAD_KEY_ROW0__ECSPI1_MOSI 0x1fc 0x5cc 0x7fc 0x0 0x2 -#define MX6Q_PAD_KEY_ROW0__ENET_TX_DATA3 0x1fc 0x5cc 0x000 0x1 0x0 -#define MX6Q_PAD_KEY_ROW0__AUD5_TXD 0x1fc 0x5cc 0x7d0 0x2 0x1 -#define MX6Q_PAD_KEY_ROW0__KEY_ROW0 0x1fc 0x5cc 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_ROW0__UART4_RX_DATA 0x1fc 0x5cc 0x938 0x4 0x1 -#define MX6Q_PAD_KEY_ROW0__UART4_TX_DATA 0x1fc 0x5cc 0x000 0x4 0x0 -#define MX6Q_PAD_KEY_ROW0__GPIO4_IO07 0x1fc 0x5cc 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_ROW0__DCIC2_OUT 0x1fc 0x5cc 0x000 0x6 0x0 -#define MX6Q_PAD_KEY_COL1__ECSPI1_MISO 0x200 0x5d0 0x7f8 0x0 0x2 -#define MX6Q_PAD_KEY_COL1__ENET_MDIO 0x200 0x5d0 0x840 0x1 0x1 -#define MX6Q_PAD_KEY_COL1__AUD5_TXFS 0x200 0x5d0 0x7e0 0x2 0x1 -#define MX6Q_PAD_KEY_COL1__KEY_COL1 0x200 0x5d0 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_COL1__UART5_TX_DATA 0x200 0x5d0 0x000 0x4 0x0 -#define MX6Q_PAD_KEY_COL1__UART5_RX_DATA 0x200 0x5d0 0x940 0x4 0x0 -#define MX6Q_PAD_KEY_COL1__GPIO4_IO08 0x200 0x5d0 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_COL1__SD1_VSELECT 0x200 0x5d0 0x000 0x6 0x0 -#define MX6Q_PAD_KEY_ROW1__ECSPI1_SS0 0x204 0x5d4 0x800 0x0 0x2 -#define MX6Q_PAD_KEY_ROW1__ENET_COL 0x204 0x5d4 0x000 0x1 0x0 -#define MX6Q_PAD_KEY_ROW1__AUD5_RXD 0x204 0x5d4 0x7cc 0x2 0x1 -#define MX6Q_PAD_KEY_ROW1__KEY_ROW1 0x204 0x5d4 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_ROW1__UART5_RX_DATA 0x204 0x5d4 0x940 0x4 0x1 -#define MX6Q_PAD_KEY_ROW1__UART5_TX_DATA 0x204 0x5d4 0x000 0x4 0x0 -#define MX6Q_PAD_KEY_ROW1__GPIO4_IO09 0x204 0x5d4 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_ROW1__SD2_VSELECT 0x204 0x5d4 0x000 0x6 0x0 -#define MX6Q_PAD_KEY_COL2__ECSPI1_SS1 0x208 0x5d8 0x804 0x0 0x2 -#define MX6Q_PAD_KEY_COL2__ENET_RX_DATA2 0x208 0x5d8 0x850 0x1 0x1 -#define MX6Q_PAD_KEY_COL2__FLEXCAN1_TX 0x208 0x5d8 0x000 0x2 0x0 -#define MX6Q_PAD_KEY_COL2__KEY_COL2 0x208 0x5d8 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_COL2__ENET_MDC 0x208 0x5d8 0x000 0x4 0x0 -#define MX6Q_PAD_KEY_COL2__GPIO4_IO10 0x208 0x5d8 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_COL2__USB_H1_PWR_CTL_WAKE 0x208 0x5d8 0x000 0x6 0x0 -#define MX6Q_PAD_KEY_ROW2__ECSPI1_SS2 0x20c 0x5dc 0x808 0x0 0x1 -#define MX6Q_PAD_KEY_ROW2__ENET_TX_DATA2 0x20c 0x5dc 0x000 0x1 0x0 -#define MX6Q_PAD_KEY_ROW2__FLEXCAN1_RX 0x20c 0x5dc 0x7e4 0x2 0x0 -#define MX6Q_PAD_KEY_ROW2__KEY_ROW2 0x20c 0x5dc 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_ROW2__SD2_VSELECT 0x20c 0x5dc 0x000 0x4 0x0 -#define MX6Q_PAD_KEY_ROW2__GPIO4_IO11 0x20c 0x5dc 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x20c 0x5dc 0x88c 0x6 0x1 -#define MX6Q_PAD_KEY_COL3__ECSPI1_SS3 0x210 0x5e0 0x80c 0x0 0x1 -#define MX6Q_PAD_KEY_COL3__ENET_CRS 0x210 0x5e0 0x000 0x1 0x0 -#define MX6Q_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x210 0x5e0 0x890 0x2 0x1 -#define MX6Q_PAD_KEY_COL3__KEY_COL3 0x210 0x5e0 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_COL3__I2C2_SCL 0x210 0x5e0 0x8a0 0x4 0x1 -#define MX6Q_PAD_KEY_COL3__GPIO4_IO12 0x210 0x5e0 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_COL3__SPDIF_IN 0x210 0x5e0 0x914 0x6 0x2 -#define MX6Q_PAD_KEY_ROW3__ASRC_EXT_CLK 0x214 0x5e4 0x7b0 0x1 0x0 -#define MX6Q_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x214 0x5e4 0x894 0x2 0x1 -#define MX6Q_PAD_KEY_ROW3__KEY_ROW3 0x214 0x5e4 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_ROW3__I2C2_SDA 0x214 0x5e4 0x8a4 0x4 0x1 -#define MX6Q_PAD_KEY_ROW3__GPIO4_IO13 0x214 0x5e4 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_ROW3__SD1_VSELECT 0x214 0x5e4 0x000 0x6 0x0 -#define MX6Q_PAD_KEY_COL4__FLEXCAN2_TX 0x218 0x5e8 0x000 0x0 0x0 -#define MX6Q_PAD_KEY_COL4__IPU1_SISG4 0x218 0x5e8 0x000 0x1 0x0 -#define MX6Q_PAD_KEY_COL4__USB_OTG_OC 0x218 0x5e8 0x944 0x2 0x1 -#define MX6Q_PAD_KEY_COL4__KEY_COL4 0x218 0x5e8 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_COL4__UART5_RTS_B 0x218 0x5e8 0x93c 0x4 0x0 -#define MX6Q_PAD_KEY_COL4__UART5_CTS_B 0x218 0x5e8 0x000 0x4 0x0 -#define MX6Q_PAD_KEY_COL4__GPIO4_IO14 0x218 0x5e8 0x000 0x5 0x0 -#define MX6Q_PAD_KEY_ROW4__FLEXCAN2_RX 0x21c 0x5ec 0x7e8 0x0 0x0 -#define MX6Q_PAD_KEY_ROW4__IPU1_SISG5 0x21c 0x5ec 0x000 0x1 0x0 -#define MX6Q_PAD_KEY_ROW4__USB_OTG_PWR 0x21c 0x5ec 0x000 0x2 0x0 -#define MX6Q_PAD_KEY_ROW4__KEY_ROW4 0x21c 0x5ec 0x000 0x3 0x0 -#define MX6Q_PAD_KEY_ROW4__UART5_CTS_B 0x21c 0x5ec 0x000 0x4 0x0 -#define MX6Q_PAD_KEY_ROW4__UART5_RTS_B 0x21c 0x5ec 0x93c 0x4 0x1 -#define MX6Q_PAD_KEY_ROW4__GPIO4_IO15 0x21c 0x5ec 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_0__CCM_CLKO1 0x220 0x5f0 0x000 0x0 0x0 -#define MX6Q_PAD_GPIO_0__KEY_COL5 0x220 0x5f0 0x8e8 0x2 0x0 -#define MX6Q_PAD_GPIO_0__ASRC_EXT_CLK 0x220 0x5f0 0x7b0 0x3 0x1 -#define MX6Q_PAD_GPIO_0__EPIT1_OUT 0x220 0x5f0 0x000 0x4 0x0 -#define MX6Q_PAD_GPIO_0__GPIO1_IO00 0x220 0x5f0 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_0__USB_H1_PWR 0x220 0x5f0 0x000 0x6 0x0 -#define MX6Q_PAD_GPIO_0__SNVS_VIO_5 0x220 0x5f0 0x000 0x7 0x0 -#define MX6Q_PAD_GPIO_1__ESAI_RX_CLK 0x224 0x5f4 0x86c 0x0 0x1 -#define MX6Q_PAD_GPIO_1__WDOG2_B 0x224 0x5f4 0x000 0x1 0x0 -#define MX6Q_PAD_GPIO_1__KEY_ROW5 0x224 0x5f4 0x8f4 0x2 0x0 -#define MX6Q_PAD_GPIO_1__USB_OTG_ID 0x224 0x5f4 0x000 0x3 0x0 -#define MX6Q_PAD_GPIO_1__PWM2_OUT 0x224 0x5f4 0x000 0x4 0x0 -#define MX6Q_PAD_GPIO_1__GPIO1_IO01 0x224 0x5f4 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_1__SD1_CD_B 0x224 0x5f4 0x000 0x6 0x0 -#define MX6Q_PAD_GPIO_9__ESAI_RX_FS 0x228 0x5f8 0x85c 0x0 0x1 -#define MX6Q_PAD_GPIO_9__WDOG1_B 0x228 0x5f8 0x000 0x1 0x0 -#define MX6Q_PAD_GPIO_9__KEY_COL6 0x228 0x5f8 0x8ec 0x2 0x0 -#define MX6Q_PAD_GPIO_9__CCM_REF_EN_B 0x228 0x5f8 0x000 0x3 0x0 -#define MX6Q_PAD_GPIO_9__PWM1_OUT 0x228 0x5f8 0x000 0x4 0x0 -#define MX6Q_PAD_GPIO_9__GPIO1_IO09 0x228 0x5f8 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_9__SD1_WP 0x228 0x5f8 0x94c 0x6 0x1 -#define MX6Q_PAD_GPIO_3__ESAI_RX_HF_CLK 0x22c 0x5fc 0x864 0x0 0x1 -#define MX6Q_PAD_GPIO_3__I2C3_SCL 0x22c 0x5fc 0x8a8 0x2 0x1 -#define MX6Q_PAD_GPIO_3__XTALOSC_REF_CLK_24M 0x22c 0x5fc 0x000 0x3 0x0 -#define MX6Q_PAD_GPIO_3__CCM_CLKO2 0x22c 0x5fc 0x000 0x4 0x0 -#define MX6Q_PAD_GPIO_3__GPIO1_IO03 0x22c 0x5fc 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_3__USB_H1_OC 0x22c 0x5fc 0x948 0x6 0x1 -#define MX6Q_PAD_GPIO_3__MLB_CLK 0x22c 0x5fc 0x900 0x7 0x1 -#define MX6Q_PAD_GPIO_6__ESAI_TX_CLK 0x230 0x600 0x870 0x0 0x1 -#define MX6Q_PAD_GPIO_6__I2C3_SDA 0x230 0x600 0x8ac 0x2 0x1 -#define MX6Q_PAD_GPIO_6__GPIO1_IO06 0x230 0x600 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_6__SD2_LCTL 0x230 0x600 0x000 0x6 0x0 -#define MX6Q_PAD_GPIO_6__MLB_SIG 0x230 0x600 0x908 0x7 0x1 -#define MX6Q_PAD_GPIO_2__ESAI_TX_FS 0x234 0x604 0x860 0x0 0x1 -#define MX6Q_PAD_GPIO_2__KEY_ROW6 0x234 0x604 0x8f8 0x2 0x1 -#define MX6Q_PAD_GPIO_2__GPIO1_IO02 0x234 0x604 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_2__SD2_WP 0x234 0x604 0x000 0x6 0x0 -#define MX6Q_PAD_GPIO_2__MLB_DATA 0x234 0x604 0x904 0x7 0x1 -#define MX6Q_PAD_GPIO_4__ESAI_TX_HF_CLK 0x238 0x608 0x868 0x0 0x1 -#define MX6Q_PAD_GPIO_4__KEY_COL7 0x238 0x608 0x8f0 0x2 0x1 -#define MX6Q_PAD_GPIO_4__GPIO1_IO04 0x238 0x608 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_4__SD2_CD_B 0x238 0x608 0x000 0x6 0x0 -#define MX6Q_PAD_GPIO_5__ESAI_TX2_RX3 0x23c 0x60c 0x87c 0x0 0x1 -#define MX6Q_PAD_GPIO_5__KEY_ROW7 0x23c 0x60c 0x8fc 0x2 0x1 -#define MX6Q_PAD_GPIO_5__CCM_CLKO1 0x23c 0x60c 0x000 0x3 0x0 -#define MX6Q_PAD_GPIO_5__GPIO1_IO05 0x23c 0x60c 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_5__I2C3_SCL 0x23c 0x60c 0x8a8 0x6 0x2 -#define MX6Q_PAD_GPIO_5__ARM_EVENTI 0x23c 0x60c 0x000 0x7 0x0 -#define MX6Q_PAD_GPIO_7__ESAI_TX4_RX1 0x240 0x610 0x884 0x0 0x1 -#define MX6Q_PAD_GPIO_7__ECSPI5_RDY 0x240 0x610 0x000 0x1 0x0 -#define MX6Q_PAD_GPIO_7__EPIT1_OUT 0x240 0x610 0x000 0x2 0x0 -#define MX6Q_PAD_GPIO_7__FLEXCAN1_TX 0x240 0x610 0x000 0x3 0x0 -#define MX6Q_PAD_GPIO_7__UART2_TX_DATA 0x240 0x610 0x000 0x4 0x0 -#define MX6Q_PAD_GPIO_7__UART2_RX_DATA 0x240 0x610 0x928 0x4 0x2 -#define MX6Q_PAD_GPIO_7__GPIO1_IO07 0x240 0x610 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_7__SPDIF_LOCK 0x240 0x610 0x000 0x6 0x0 -#define MX6Q_PAD_GPIO_7__USB_OTG_HOST_MODE 0x240 0x610 0x000 0x7 0x0 -#define MX6Q_PAD_GPIO_8__ESAI_TX5_RX0 0x244 0x614 0x888 0x0 0x1 -#define MX6Q_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x244 0x614 0x000 0x1 0x0 -#define MX6Q_PAD_GPIO_8__EPIT2_OUT 0x244 0x614 0x000 0x2 0x0 -#define MX6Q_PAD_GPIO_8__FLEXCAN1_RX 0x244 0x614 0x7e4 0x3 0x1 -#define MX6Q_PAD_GPIO_8__UART2_RX_DATA 0x244 0x614 0x928 0x4 0x3 -#define MX6Q_PAD_GPIO_8__UART2_TX_DATA 0x244 0x614 0x000 0x4 0x0 -#define MX6Q_PAD_GPIO_8__GPIO1_IO08 0x244 0x614 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_8__SPDIF_SR_CLK 0x244 0x614 0x000 0x6 0x0 -#define MX6Q_PAD_GPIO_8__USB_OTG_PWR_CTL_WAKE 0x244 0x614 0x000 0x7 0x0 -#define MX6Q_PAD_GPIO_16__ESAI_TX3_RX2 0x248 0x618 0x880 0x0 0x1 -#define MX6Q_PAD_GPIO_16__ENET_1588_EVENT2_IN 0x248 0x618 0x000 0x1 0x0 -#define MX6Q_PAD_GPIO_16__ENET_REF_CLK 0x248 0x618 0x83c 0x2 0x1 -#define MX6Q_PAD_GPIO_16__SD1_LCTL 0x248 0x618 0x000 0x3 0x0 -#define MX6Q_PAD_GPIO_16__SPDIF_IN 0x248 0x618 0x914 0x4 0x3 -#define MX6Q_PAD_GPIO_16__GPIO7_IO11 0x248 0x618 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_16__I2C3_SDA 0x248 0x618 0x8ac 0x6 0x2 -#define MX6Q_PAD_GPIO_16__JTAG_DE_B 0x248 0x618 0x000 0x7 0x0 -#define MX6Q_PAD_GPIO_17__ESAI_TX0 0x24c 0x61c 0x874 0x0 0x0 -#define MX6Q_PAD_GPIO_17__ENET_1588_EVENT3_IN 0x24c 0x61c 0x000 0x1 0x0 -#define MX6Q_PAD_GPIO_17__CCM_PMIC_READY 0x24c 0x61c 0x7f0 0x2 0x1 -#define MX6Q_PAD_GPIO_17__SDMA_EXT_EVENT0 0x24c 0x61c 0x90c 0x3 0x1 -#define MX6Q_PAD_GPIO_17__SPDIF_OUT 0x24c 0x61c 0x000 0x4 0x0 -#define MX6Q_PAD_GPIO_17__GPIO7_IO12 0x24c 0x61c 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_18__ESAI_TX1 0x250 0x620 0x878 0x0 0x0 -#define MX6Q_PAD_GPIO_18__ENET_RX_CLK 0x250 0x620 0x844 0x1 0x1 -#define MX6Q_PAD_GPIO_18__SD3_VSELECT 0x250 0x620 0x000 0x2 0x0 -#define MX6Q_PAD_GPIO_18__SDMA_EXT_EVENT1 0x250 0x620 0x910 0x3 0x1 -#define MX6Q_PAD_GPIO_18__ASRC_EXT_CLK 0x250 0x620 0x7b0 0x4 0x2 -#define MX6Q_PAD_GPIO_18__GPIO7_IO13 0x250 0x620 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_18__SNVS_VIO_5_CTL 0x250 0x620 0x000 0x6 0x0 -#define MX6Q_PAD_GPIO_19__KEY_COL5 0x254 0x624 0x8e8 0x0 0x1 -#define MX6Q_PAD_GPIO_19__ENET_1588_EVENT0_OUT 0x254 0x624 0x000 0x1 0x0 -#define MX6Q_PAD_GPIO_19__SPDIF_OUT 0x254 0x624 0x000 0x2 0x0 -#define MX6Q_PAD_GPIO_19__CCM_CLKO1 0x254 0x624 0x000 0x3 0x0 -#define MX6Q_PAD_GPIO_19__ECSPI1_RDY 0x254 0x624 0x000 0x4 0x0 -#define MX6Q_PAD_GPIO_19__GPIO4_IO05 0x254 0x624 0x000 0x5 0x0 -#define MX6Q_PAD_GPIO_19__ENET_TX_ER 0x254 0x624 0x000 0x6 0x0 -#define MX6Q_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x258 0x628 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_PIXCLK__GPIO5_IO18 0x258 0x628 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_PIXCLK__ARM_EVENTO 0x258 0x628 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x25c 0x62c 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_MCLK__CCM_CLKO1 0x25c 0x62c 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_MCLK__GPIO5_IO19 0x25c 0x62c 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_MCLK__ARM_TRACE_CTL 0x25c 0x62c 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x260 0x630 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DATA_EN__EIM_DATA00 0x260 0x630 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DATA_EN__GPIO5_IO20 0x260 0x630 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DATA_EN__ARM_TRACE_CLK 0x260 0x630 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x264 0x634 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_VSYNC__EIM_DATA01 0x264 0x634 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_VSYNC__GPIO5_IO21 0x264 0x634 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_VSYNC__ARM_TRACE00 0x264 0x634 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT4__IPU1_CSI0_DATA04 0x268 0x638 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT4__EIM_DATA02 0x268 0x638 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT4__ECSPI1_SCLK 0x268 0x638 0x7f4 0x2 0x3 -#define MX6Q_PAD_CSI0_DAT4__KEY_COL5 0x268 0x638 0x8e8 0x3 0x2 -#define MX6Q_PAD_CSI0_DAT4__AUD3_TXC 0x268 0x638 0x000 0x4 0x0 -#define MX6Q_PAD_CSI0_DAT4__GPIO5_IO22 0x268 0x638 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT4__ARM_TRACE01 0x268 0x638 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT5__IPU1_CSI0_DATA05 0x26c 0x63c 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT5__EIM_DATA03 0x26c 0x63c 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT5__ECSPI1_MOSI 0x26c 0x63c 0x7fc 0x2 0x3 -#define MX6Q_PAD_CSI0_DAT5__KEY_ROW5 0x26c 0x63c 0x8f4 0x3 0x1 -#define MX6Q_PAD_CSI0_DAT5__AUD3_TXD 0x26c 0x63c 0x000 0x4 0x0 -#define MX6Q_PAD_CSI0_DAT5__GPIO5_IO23 0x26c 0x63c 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT5__ARM_TRACE02 0x26c 0x63c 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT6__IPU1_CSI0_DATA06 0x270 0x640 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT6__EIM_DATA04 0x270 0x640 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT6__ECSPI1_MISO 0x270 0x640 0x7f8 0x2 0x3 -#define MX6Q_PAD_CSI0_DAT6__KEY_COL6 0x270 0x640 0x8ec 0x3 0x1 -#define MX6Q_PAD_CSI0_DAT6__AUD3_TXFS 0x270 0x640 0x000 0x4 0x0 -#define MX6Q_PAD_CSI0_DAT6__GPIO5_IO24 0x270 0x640 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT6__ARM_TRACE03 0x270 0x640 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT7__IPU1_CSI0_DATA07 0x274 0x644 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT7__EIM_DATA05 0x274 0x644 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT7__ECSPI1_SS0 0x274 0x644 0x800 0x2 0x3 -#define MX6Q_PAD_CSI0_DAT7__KEY_ROW6 0x274 0x644 0x8f8 0x3 0x2 -#define MX6Q_PAD_CSI0_DAT7__AUD3_RXD 0x274 0x644 0x000 0x4 0x0 -#define MX6Q_PAD_CSI0_DAT7__GPIO5_IO25 0x274 0x644 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT7__ARM_TRACE04 0x274 0x644 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT8__IPU1_CSI0_DATA08 0x278 0x648 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT8__EIM_DATA06 0x278 0x648 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT8__ECSPI2_SCLK 0x278 0x648 0x810 0x2 0x2 -#define MX6Q_PAD_CSI0_DAT8__KEY_COL7 0x278 0x648 0x8f0 0x3 0x2 -#define MX6Q_PAD_CSI0_DAT8__I2C1_SDA 0x278 0x648 0x89c 0x4 0x1 -#define MX6Q_PAD_CSI0_DAT8__GPIO5_IO26 0x278 0x648 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT8__ARM_TRACE05 0x278 0x648 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT9__IPU1_CSI0_DATA09 0x27c 0x64c 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT9__EIM_DATA07 0x27c 0x64c 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT9__ECSPI2_MOSI 0x27c 0x64c 0x818 0x2 0x2 -#define MX6Q_PAD_CSI0_DAT9__KEY_ROW7 0x27c 0x64c 0x8fc 0x3 0x2 -#define MX6Q_PAD_CSI0_DAT9__I2C1_SCL 0x27c 0x64c 0x898 0x4 0x1 -#define MX6Q_PAD_CSI0_DAT9__GPIO5_IO27 0x27c 0x64c 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT9__ARM_TRACE06 0x27c 0x64c 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT10__IPU1_CSI0_DATA10 0x280 0x650 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT10__AUD3_RXC 0x280 0x650 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT10__ECSPI2_MISO 0x280 0x650 0x814 0x2 0x2 -#define MX6Q_PAD_CSI0_DAT10__UART1_TX_DATA 0x280 0x650 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT10__UART1_RX_DATA 0x280 0x650 0x920 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT10__GPIO5_IO28 0x280 0x650 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT10__ARM_TRACE07 0x280 0x650 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT11__IPU1_CSI0_DATA11 0x284 0x654 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT11__AUD3_RXFS 0x284 0x654 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT11__ECSPI2_SS0 0x284 0x654 0x81c 0x2 0x2 -#define MX6Q_PAD_CSI0_DAT11__UART1_RX_DATA 0x284 0x654 0x920 0x3 0x1 -#define MX6Q_PAD_CSI0_DAT11__UART1_TX_DATA 0x284 0x654 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT11__GPIO5_IO29 0x284 0x654 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT11__ARM_TRACE08 0x284 0x654 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x288 0x658 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT12__EIM_DATA08 0x288 0x658 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT12__UART4_TX_DATA 0x288 0x658 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT12__UART4_RX_DATA 0x288 0x658 0x938 0x3 0x2 -#define MX6Q_PAD_CSI0_DAT12__GPIO5_IO30 0x288 0x658 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT12__ARM_TRACE09 0x288 0x658 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x28c 0x65c 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT13__EIM_DATA09 0x28c 0x65c 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT13__UART4_RX_DATA 0x28c 0x65c 0x938 0x3 0x3 -#define MX6Q_PAD_CSI0_DAT13__UART4_TX_DATA 0x28c 0x65c 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT13__GPIO5_IO31 0x28c 0x65c 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT13__ARM_TRACE10 0x28c 0x65c 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x290 0x660 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT14__EIM_DATA10 0x290 0x660 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT14__UART5_TX_DATA 0x290 0x660 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT14__UART5_RX_DATA 0x290 0x660 0x940 0x3 0x2 -#define MX6Q_PAD_CSI0_DAT14__GPIO6_IO00 0x290 0x660 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT14__ARM_TRACE11 0x290 0x660 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x294 0x664 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT15__EIM_DATA11 0x294 0x664 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT15__UART5_RX_DATA 0x294 0x664 0x940 0x3 0x3 -#define MX6Q_PAD_CSI0_DAT15__UART5_TX_DATA 0x294 0x664 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT15__GPIO6_IO01 0x294 0x664 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT15__ARM_TRACE12 0x294 0x664 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x298 0x668 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT16__EIM_DATA12 0x298 0x668 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT16__UART4_RTS_B 0x298 0x668 0x934 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT16__UART4_CTS_B 0x298 0x668 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT16__GPIO6_IO02 0x298 0x668 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT16__ARM_TRACE13 0x298 0x668 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x29c 0x66c 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT17__EIM_DATA13 0x29c 0x66c 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT17__UART4_CTS_B 0x29c 0x66c 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT17__UART4_RTS_B 0x29c 0x66c 0x934 0x3 0x1 -#define MX6Q_PAD_CSI0_DAT17__GPIO6_IO03 0x29c 0x66c 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT17__ARM_TRACE14 0x29c 0x66c 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x2a0 0x670 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT18__EIM_DATA14 0x2a0 0x670 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT18__UART5_RTS_B 0x2a0 0x670 0x93c 0x3 0x2 -#define MX6Q_PAD_CSI0_DAT18__UART5_CTS_B 0x2a0 0x670 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT18__GPIO6_IO04 0x2a0 0x670 0x000 0x5 0x0 -#define MX6Q_PAD_CSI0_DAT18__ARM_TRACE15 0x2a0 0x670 0x000 0x7 0x0 -#define MX6Q_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x2a4 0x674 0x000 0x0 0x0 -#define MX6Q_PAD_CSI0_DAT19__EIM_DATA15 0x2a4 0x674 0x000 0x1 0x0 -#define MX6Q_PAD_CSI0_DAT19__UART5_CTS_B 0x2a4 0x674 0x000 0x3 0x0 -#define MX6Q_PAD_CSI0_DAT19__UART5_RTS_B 0x2a4 0x674 0x93c 0x3 0x3 -#define MX6Q_PAD_CSI0_DAT19__GPIO6_IO05 0x2a4 0x674 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_DAT7__SD3_DATA7 0x2a8 0x690 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_DAT7__UART1_TX_DATA 0x2a8 0x690 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_DAT7__UART1_RX_DATA 0x2a8 0x690 0x920 0x1 0x2 -#define MX6Q_PAD_SD3_DAT7__GPIO6_IO17 0x2a8 0x690 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_DAT6__SD3_DATA6 0x2ac 0x694 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_DAT6__UART1_RX_DATA 0x2ac 0x694 0x920 0x1 0x3 -#define MX6Q_PAD_SD3_DAT6__UART1_TX_DATA 0x2ac 0x694 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_DAT6__GPIO6_IO18 0x2ac 0x694 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_DAT5__SD3_DATA5 0x2b0 0x698 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_DAT5__UART2_TX_DATA 0x2b0 0x698 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_DAT5__UART2_RX_DATA 0x2b0 0x698 0x928 0x1 0x4 -#define MX6Q_PAD_SD3_DAT5__GPIO7_IO00 0x2b0 0x698 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_DAT4__SD3_DATA4 0x2b4 0x69c 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_DAT4__UART2_RX_DATA 0x2b4 0x69c 0x928 0x1 0x5 -#define MX6Q_PAD_SD3_DAT4__UART2_TX_DATA 0x2b4 0x69c 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_DAT4__GPIO7_IO01 0x2b4 0x69c 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_CMD__SD3_CMD 0x2b8 0x6a0 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_CMD__UART2_CTS_B 0x2b8 0x6a0 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_CMD__UART2_RTS_B 0x2b8 0x6a0 0x924 0x1 0x2 -#define MX6Q_PAD_SD3_CMD__FLEXCAN1_TX 0x2b8 0x6a0 0x000 0x2 0x0 -#define MX6Q_PAD_SD3_CMD__GPIO7_IO02 0x2b8 0x6a0 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_CLK__SD3_CLK 0x2bc 0x6a4 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_CLK__UART2_RTS_B 0x2bc 0x6a4 0x924 0x1 0x3 -#define MX6Q_PAD_SD3_CLK__UART2_CTS_B 0x2bc 0x6a4 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_CLK__FLEXCAN1_RX 0x2bc 0x6a4 0x7e4 0x2 0x2 -#define MX6Q_PAD_SD3_CLK__GPIO7_IO03 0x2bc 0x6a4 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_DAT0__SD3_DATA0 0x2c0 0x6a8 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_DAT0__UART1_CTS_B 0x2c0 0x6a8 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_DAT0__UART1_RTS_B 0x2c0 0x6a8 0x91c 0x1 0x2 -#define MX6Q_PAD_SD3_DAT0__FLEXCAN2_TX 0x2c0 0x6a8 0x000 0x2 0x0 -#define MX6Q_PAD_SD3_DAT0__GPIO7_IO04 0x2c0 0x6a8 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_DAT1__SD3_DATA1 0x2c4 0x6ac 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_DAT1__UART1_RTS_B 0x2c4 0x6ac 0x91c 0x1 0x3 -#define MX6Q_PAD_SD3_DAT1__UART1_CTS_B 0x2c4 0x6ac 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_DAT1__FLEXCAN2_RX 0x2c4 0x6ac 0x7e8 0x2 0x1 -#define MX6Q_PAD_SD3_DAT1__GPIO7_IO05 0x2c4 0x6ac 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_DAT2__SD3_DATA2 0x2c8 0x6b0 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_DAT2__GPIO7_IO06 0x2c8 0x6b0 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_DAT3__SD3_DATA3 0x2cc 0x6b4 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_DAT3__UART3_CTS_B 0x2cc 0x6b4 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_DAT3__UART3_RTS_B 0x2cc 0x6b4 0x92c 0x1 0x4 -#define MX6Q_PAD_SD3_DAT3__GPIO7_IO07 0x2cc 0x6b4 0x000 0x5 0x0 -#define MX6Q_PAD_SD3_RST__SD3_RESET 0x2d0 0x6b8 0x000 0x0 0x0 -#define MX6Q_PAD_SD3_RST__UART3_RTS_B 0x2d0 0x6b8 0x92c 0x1 0x5 -#define MX6Q_PAD_SD3_RST__UART3_CTS_B 0x2d0 0x6b8 0x000 0x1 0x0 -#define MX6Q_PAD_SD3_RST__GPIO7_IO08 0x2d0 0x6b8 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_CLE__NAND_CLE 0x2d4 0x6bc 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_CLE__IPU2_SISG4 0x2d4 0x6bc 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_CLE__GPIO6_IO07 0x2d4 0x6bc 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_ALE__NAND_ALE 0x2d8 0x6c0 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_ALE__SD4_RESET 0x2d8 0x6c0 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_ALE__GPIO6_IO08 0x2d8 0x6c0 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_WP_B__NAND_WP_B 0x2dc 0x6c4 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_WP_B__IPU2_SISG5 0x2dc 0x6c4 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_WP_B__GPIO6_IO09 0x2dc 0x6c4 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_RB0__NAND_READY_B 0x2e0 0x6c8 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_RB0__IPU2_DI0_PIN01 0x2e0 0x6c8 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_RB0__GPIO6_IO10 0x2e0 0x6c8 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_CS0__NAND_CE0_B 0x2e4 0x6cc 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_CS0__GPIO6_IO11 0x2e4 0x6cc 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_CS1__NAND_CE1_B 0x2e8 0x6d0 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_CS1__SD4_VSELECT 0x2e8 0x6d0 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_CS1__SD3_VSELECT 0x2e8 0x6d0 0x000 0x2 0x0 -#define MX6Q_PAD_NANDF_CS1__GPIO6_IO14 0x2e8 0x6d0 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_CS2__NAND_CE2_B 0x2ec 0x6d4 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_CS2__IPU1_SISG0 0x2ec 0x6d4 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_CS2__ESAI_TX0 0x2ec 0x6d4 0x874 0x2 0x1 -#define MX6Q_PAD_NANDF_CS2__EIM_CRE 0x2ec 0x6d4 0x000 0x3 0x0 -#define MX6Q_PAD_NANDF_CS2__CCM_CLKO2 0x2ec 0x6d4 0x000 0x4 0x0 -#define MX6Q_PAD_NANDF_CS2__GPIO6_IO15 0x2ec 0x6d4 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_CS2__IPU2_SISG0 0x2ec 0x6d4 0x000 0x6 0x0 -#define MX6Q_PAD_NANDF_CS3__NAND_CE3_B 0x2f0 0x6d8 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_CS3__IPU1_SISG1 0x2f0 0x6d8 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_CS3__ESAI_TX1 0x2f0 0x6d8 0x878 0x2 0x1 -#define MX6Q_PAD_NANDF_CS3__EIM_ADDR26 0x2f0 0x6d8 0x000 0x3 0x0 -#define MX6Q_PAD_NANDF_CS3__GPIO6_IO16 0x2f0 0x6d8 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_CS3__IPU2_SISG1 0x2f0 0x6d8 0x000 0x6 0x0 -#define MX6Q_PAD_SD4_CMD__SD4_CMD 0x2f4 0x6dc 0x000 0x0 0x0 -#define MX6Q_PAD_SD4_CMD__NAND_RE_B 0x2f4 0x6dc 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_CMD__UART3_TX_DATA 0x2f4 0x6dc 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_CMD__UART3_RX_DATA 0x2f4 0x6dc 0x930 0x2 0x2 -#define MX6Q_PAD_SD4_CMD__GPIO7_IO09 0x2f4 0x6dc 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_CLK__SD4_CLK 0x2f8 0x6e0 0x000 0x0 0x0 -#define MX6Q_PAD_SD4_CLK__NAND_WE_B 0x2f8 0x6e0 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_CLK__UART3_RX_DATA 0x2f8 0x6e0 0x930 0x2 0x3 -#define MX6Q_PAD_SD4_CLK__UART3_TX_DATA 0x2f8 0x6e0 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_CLK__GPIO7_IO10 0x2f8 0x6e0 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_D0__NAND_DATA00 0x2fc 0x6e4 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_D0__SD1_DATA4 0x2fc 0x6e4 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_D0__GPIO2_IO00 0x2fc 0x6e4 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_D1__NAND_DATA01 0x300 0x6e8 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_D1__SD1_DATA5 0x300 0x6e8 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_D1__GPIO2_IO01 0x300 0x6e8 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_D2__NAND_DATA02 0x304 0x6ec 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_D2__SD1_DATA6 0x304 0x6ec 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_D2__GPIO2_IO02 0x304 0x6ec 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_D3__NAND_DATA03 0x308 0x6f0 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_D3__SD1_DATA7 0x308 0x6f0 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_D3__GPIO2_IO03 0x308 0x6f0 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_D4__NAND_DATA04 0x30c 0x6f4 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_D4__SD2_DATA4 0x30c 0x6f4 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_D4__GPIO2_IO04 0x30c 0x6f4 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_D5__NAND_DATA05 0x310 0x6f8 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_D5__SD2_DATA5 0x310 0x6f8 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_D5__GPIO2_IO05 0x310 0x6f8 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_D6__NAND_DATA06 0x314 0x6fc 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_D6__SD2_DATA6 0x314 0x6fc 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_D6__GPIO2_IO06 0x314 0x6fc 0x000 0x5 0x0 -#define MX6Q_PAD_NANDF_D7__NAND_DATA07 0x318 0x700 0x000 0x0 0x0 -#define MX6Q_PAD_NANDF_D7__SD2_DATA7 0x318 0x700 0x000 0x1 0x0 -#define MX6Q_PAD_NANDF_D7__GPIO2_IO07 0x318 0x700 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_DAT0__SD4_DATA0 0x31c 0x704 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_DAT0__NAND_DQS 0x31c 0x704 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_DAT0__GPIO2_IO08 0x31c 0x704 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_DAT1__SD4_DATA1 0x320 0x708 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_DAT1__PWM3_OUT 0x320 0x708 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_DAT1__GPIO2_IO09 0x320 0x708 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_DAT2__SD4_DATA2 0x324 0x70c 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_DAT2__PWM4_OUT 0x324 0x70c 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_DAT2__GPIO2_IO10 0x324 0x70c 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_DAT3__SD4_DATA3 0x328 0x710 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_DAT3__GPIO2_IO11 0x328 0x710 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_DAT4__SD4_DATA4 0x32c 0x714 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_DAT4__UART2_RX_DATA 0x32c 0x714 0x928 0x2 0x6 -#define MX6Q_PAD_SD4_DAT4__UART2_TX_DATA 0x32c 0x714 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_DAT4__GPIO2_IO12 0x32c 0x714 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_DAT5__SD4_DATA5 0x330 0x718 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_DAT5__UART2_RTS_B 0x330 0x718 0x924 0x2 0x4 -#define MX6Q_PAD_SD4_DAT5__UART2_CTS_B 0x330 0x718 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_DAT5__GPIO2_IO13 0x330 0x718 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_DAT6__SD4_DATA6 0x334 0x71c 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_DAT6__UART2_CTS_B 0x334 0x71c 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_DAT6__UART2_RTS_B 0x334 0x71c 0x924 0x2 0x5 -#define MX6Q_PAD_SD4_DAT6__GPIO2_IO14 0x334 0x71c 0x000 0x5 0x0 -#define MX6Q_PAD_SD4_DAT7__SD4_DATA7 0x338 0x720 0x000 0x1 0x0 -#define MX6Q_PAD_SD4_DAT7__UART2_TX_DATA 0x338 0x720 0x000 0x2 0x0 -#define MX6Q_PAD_SD4_DAT7__UART2_RX_DATA 0x338 0x720 0x928 0x2 0x7 -#define MX6Q_PAD_SD4_DAT7__GPIO2_IO15 0x338 0x720 0x000 0x5 0x0 -#define MX6Q_PAD_SD1_DAT1__SD1_DATA1 0x33c 0x724 0x000 0x0 0x0 -#define MX6Q_PAD_SD1_DAT1__ECSPI5_SS0 0x33c 0x724 0x834 0x1 0x1 -#define MX6Q_PAD_SD1_DAT1__PWM3_OUT 0x33c 0x724 0x000 0x2 0x0 -#define MX6Q_PAD_SD1_DAT1__GPT_CAPTURE2 0x33c 0x724 0x000 0x3 0x0 -#define MX6Q_PAD_SD1_DAT1__GPIO1_IO17 0x33c 0x724 0x000 0x5 0x0 -#define MX6Q_PAD_SD1_DAT0__SD1_DATA0 0x340 0x728 0x000 0x0 0x0 -#define MX6Q_PAD_SD1_DAT0__ECSPI5_MISO 0x340 0x728 0x82c 0x1 0x1 -#define MX6Q_PAD_SD1_DAT0__GPT_CAPTURE1 0x340 0x728 0x000 0x3 0x0 -#define MX6Q_PAD_SD1_DAT0__GPIO1_IO16 0x340 0x728 0x000 0x5 0x0 -#define MX6Q_PAD_SD1_DAT3__SD1_DATA3 0x344 0x72c 0x000 0x0 0x0 -#define MX6Q_PAD_SD1_DAT3__ECSPI5_SS2 0x344 0x72c 0x000 0x1 0x0 -#define MX6Q_PAD_SD1_DAT3__GPT_COMPARE3 0x344 0x72c 0x000 0x2 0x0 -#define MX6Q_PAD_SD1_DAT3__PWM1_OUT 0x344 0x72c 0x000 0x3 0x0 -#define MX6Q_PAD_SD1_DAT3__WDOG2_B 0x344 0x72c 0x000 0x4 0x0 -#define MX6Q_PAD_SD1_DAT3__GPIO1_IO21 0x344 0x72c 0x000 0x5 0x0 -#define MX6Q_PAD_SD1_DAT3__WDOG2_RESET_B_DEB 0x344 0x72c 0x000 0x6 0x0 -#define MX6Q_PAD_SD1_CMD__SD1_CMD 0x348 0x730 0x000 0x0 0x0 -#define MX6Q_PAD_SD1_CMD__ECSPI5_MOSI 0x348 0x730 0x830 0x1 0x0 -#define MX6Q_PAD_SD1_CMD__PWM4_OUT 0x348 0x730 0x000 0x2 0x0 -#define MX6Q_PAD_SD1_CMD__GPT_COMPARE1 0x348 0x730 0x000 0x3 0x0 -#define MX6Q_PAD_SD1_CMD__GPIO1_IO18 0x348 0x730 0x000 0x5 0x0 -#define MX6Q_PAD_SD1_DAT2__SD1_DATA2 0x34c 0x734 0x000 0x0 0x0 -#define MX6Q_PAD_SD1_DAT2__ECSPI5_SS1 0x34c 0x734 0x838 0x1 0x1 -#define MX6Q_PAD_SD1_DAT2__GPT_COMPARE2 0x34c 0x734 0x000 0x2 0x0 -#define MX6Q_PAD_SD1_DAT2__PWM2_OUT 0x34c 0x734 0x000 0x3 0x0 -#define MX6Q_PAD_SD1_DAT2__WDOG1_B 0x34c 0x734 0x000 0x4 0x0 -#define MX6Q_PAD_SD1_DAT2__GPIO1_IO19 0x34c 0x734 0x000 0x5 0x0 -#define MX6Q_PAD_SD1_DAT2__WDOG1_RESET_B_DEB 0x34c 0x734 0x000 0x6 0x0 -#define MX6Q_PAD_SD1_CLK__SD1_CLK 0x350 0x738 0x000 0x0 0x0 -#define MX6Q_PAD_SD1_CLK__ECSPI5_SCLK 0x350 0x738 0x828 0x1 0x0 -#define MX6Q_PAD_SD1_CLK__GPT_CLKIN 0x350 0x738 0x000 0x3 0x0 -#define MX6Q_PAD_SD1_CLK__GPIO1_IO20 0x350 0x738 0x000 0x5 0x0 -#define MX6Q_PAD_SD2_CLK__SD2_CLK 0x354 0x73c 0x000 0x0 0x0 -#define MX6Q_PAD_SD2_CLK__ECSPI5_SCLK 0x354 0x73c 0x828 0x1 0x1 -#define MX6Q_PAD_SD2_CLK__KEY_COL5 0x354 0x73c 0x8e8 0x2 0x3 -#define MX6Q_PAD_SD2_CLK__AUD4_RXFS 0x354 0x73c 0x7c0 0x3 0x1 -#define MX6Q_PAD_SD2_CLK__GPIO1_IO10 0x354 0x73c 0x000 0x5 0x0 -#define MX6Q_PAD_SD2_CMD__SD2_CMD 0x358 0x740 0x000 0x0 0x0 -#define MX6Q_PAD_SD2_CMD__ECSPI5_MOSI 0x358 0x740 0x830 0x1 0x1 -#define MX6Q_PAD_SD2_CMD__KEY_ROW5 0x358 0x740 0x8f4 0x2 0x2 -#define MX6Q_PAD_SD2_CMD__AUD4_RXC 0x358 0x740 0x7bc 0x3 0x1 -#define MX6Q_PAD_SD2_CMD__GPIO1_IO11 0x358 0x740 0x000 0x5 0x0 -#define MX6Q_PAD_SD2_DAT3__SD2_DATA3 0x35c 0x744 0x000 0x0 0x0 -#define MX6Q_PAD_SD2_DAT3__ECSPI5_SS3 0x35c 0x744 0x000 0x1 0x0 -#define MX6Q_PAD_SD2_DAT3__KEY_COL6 0x35c 0x744 0x8ec 0x2 0x2 -#define MX6Q_PAD_SD2_DAT3__AUD4_TXC 0x35c 0x744 0x7c4 0x3 0x1 -#define MX6Q_PAD_SD2_DAT3__GPIO1_IO12 0x35c 0x744 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x04c 0x360 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_DAT1__ECSPI5_SS0 0x04c 0x360 0x834 0x1 0x0 +#define MX6QDL_PAD_SD2_DAT1__EIM_CS2_B 0x04c 0x360 0x000 0x2 0x0 +#define MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x04c 0x360 0x7c8 0x3 0x0 +#define MX6QDL_PAD_SD2_DAT1__KEY_COL7 0x04c 0x360 0x8f0 0x4 0x0 +#define MX6QDL_PAD_SD2_DAT1__GPIO1_IO14 0x04c 0x360 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x050 0x364 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_DAT2__ECSPI5_SS1 0x050 0x364 0x838 0x1 0x0 +#define MX6QDL_PAD_SD2_DAT2__EIM_CS3_B 0x050 0x364 0x000 0x2 0x0 +#define MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x050 0x364 0x7b8 0x3 0x0 +#define MX6QDL_PAD_SD2_DAT2__KEY_ROW6 0x050 0x364 0x8f8 0x4 0x0 +#define MX6QDL_PAD_SD2_DAT2__GPIO1_IO13 0x050 0x364 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x054 0x368 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_DAT0__ECSPI5_MISO 0x054 0x368 0x82c 0x1 0x0 +#define MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x054 0x368 0x7b4 0x3 0x0 +#define MX6QDL_PAD_SD2_DAT0__KEY_ROW7 0x054 0x368 0x8fc 0x4 0x0 +#define MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x054 0x368 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT0__DCIC2_OUT 0x054 0x368 0x000 0x6 0x0 +#define MX6QDL_PAD_RGMII_TXC__USB_H2_DATA 0x058 0x36c 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x058 0x36c 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TXC__SPDIF_EXT_CLK 0x058 0x36c 0x918 0x2 0x0 +#define MX6QDL_PAD_RGMII_TXC__GPIO6_IO19 0x058 0x36c 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TXC__XTALOSC_REF_CLK_24M 0x058 0x36c 0x000 0x7 0x0 +#define MX6QDL_PAD_RGMII_TD0__HSI_TX_READY 0x05c 0x370 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x05c 0x370 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TD0__GPIO6_IO20 0x05c 0x370 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TD1__HSI_RX_FLAG 0x060 0x374 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x060 0x374 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TD1__GPIO6_IO21 0x060 0x374 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TD2__HSI_RX_DATA 0x064 0x378 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x064 0x378 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TD2__GPIO6_IO22 0x064 0x378 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TD3__HSI_RX_WAKE 0x068 0x37c 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x068 0x37c 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TD3__GPIO6_IO23 0x068 0x37c 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RX_CTL__USB_H3_DATA 0x06c 0x380 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x06c 0x380 0x858 0x1 0x0 +#define MX6QDL_PAD_RGMII_RX_CTL__GPIO6_IO24 0x06c 0x380 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RD0__HSI_RX_READY 0x070 0x384 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x070 0x384 0x848 0x1 0x0 +#define MX6QDL_PAD_RGMII_RD0__GPIO6_IO25 0x070 0x384 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x074 0x388 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x074 0x388 0x000 0x1 0x0 +#define MX6QDL_PAD_RGMII_TX_CTL__GPIO6_IO26 0x074 0x388 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_TX_CTL__ENET_REF_CLK 0x074 0x388 0x83c 0x7 0x0 +#define MX6QDL_PAD_RGMII_RD1__HSI_TX_FLAG 0x078 0x38c 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x078 0x38c 0x84c 0x1 0x0 +#define MX6QDL_PAD_RGMII_RD1__GPIO6_IO27 0x078 0x38c 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RD2__HSI_TX_DATA 0x07c 0x390 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x07c 0x390 0x850 0x1 0x0 +#define MX6QDL_PAD_RGMII_RD2__GPIO6_IO28 0x07c 0x390 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RD3__HSI_TX_WAKE 0x080 0x394 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x080 0x394 0x854 0x1 0x0 +#define MX6QDL_PAD_RGMII_RD3__GPIO6_IO29 0x080 0x394 0x000 0x5 0x0 +#define MX6QDL_PAD_RGMII_RXC__USB_H3_STROBE 0x084 0x398 0x000 0x0 0x0 +#define MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x084 0x398 0x844 0x1 0x0 +#define MX6QDL_PAD_RGMII_RXC__GPIO6_IO30 0x084 0x398 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A25__EIM_ADDR25 0x088 0x39c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A25__ECSPI4_SS1 0x088 0x39c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A25__ECSPI2_RDY 0x088 0x39c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_A25__IPU1_DI1_PIN12 0x088 0x39c 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_A25__IPU1_DI0_D1_CS 0x088 0x39c 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x088 0x39c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A25__HDMI_TX_CEC_LINE 0x088 0x39c 0x88c 0x6 0x0 +#define MX6QDL_PAD_EIM_EB2__EIM_EB2_B 0x08c 0x3a0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_EB2__ECSPI1_SS0 0x08c 0x3a0 0x800 0x1 0x0 +#define MX6QDL_PAD_EIM_EB2__IPU2_CSI1_DATA19 0x08c 0x3a0 0x8d4 0x3 0x0 +#define MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x08c 0x3a0 0x890 0x4 0x0 +#define MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x08c 0x3a0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x08c 0x3a0 0x8a0 0x6 0x0 +#define MX6QDL_PAD_EIM_EB2__SRC_BOOT_CFG30 0x08c 0x3a0 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D16__EIM_DATA16 0x090 0x3a4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x090 0x3a4 0x7f4 0x1 0x0 +#define MX6QDL_PAD_EIM_D16__IPU1_DI0_PIN05 0x090 0x3a4 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D16__IPU2_CSI1_DATA18 0x090 0x3a4 0x8d0 0x3 0x0 +#define MX6QDL_PAD_EIM_D16__HDMI_TX_DDC_SDA 0x090 0x3a4 0x894 0x4 0x0 +#define MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x090 0x3a4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D16__I2C2_SDA 0x090 0x3a4 0x8a4 0x6 0x0 +#define MX6QDL_PAD_EIM_D17__EIM_DATA17 0x094 0x3a8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x094 0x3a8 0x7f8 0x1 0x0 +#define MX6QDL_PAD_EIM_D17__IPU1_DI0_PIN06 0x094 0x3a8 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D17__IPU2_CSI1_PIXCLK 0x094 0x3a8 0x8e0 0x3 0x0 +#define MX6QDL_PAD_EIM_D17__DCIC1_OUT 0x094 0x3a8 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D17__GPIO3_IO17 0x094 0x3a8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D17__I2C3_SCL 0x094 0x3a8 0x8a8 0x6 0x0 +#define MX6QDL_PAD_EIM_D18__EIM_DATA18 0x098 0x3ac 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x098 0x3ac 0x7fc 0x1 0x0 +#define MX6QDL_PAD_EIM_D18__IPU1_DI0_PIN07 0x098 0x3ac 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D18__IPU2_CSI1_DATA17 0x098 0x3ac 0x8cc 0x3 0x0 +#define MX6QDL_PAD_EIM_D18__IPU1_DI1_D0_CS 0x098 0x3ac 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D18__GPIO3_IO18 0x098 0x3ac 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D18__I2C3_SDA 0x098 0x3ac 0x8ac 0x6 0x0 +#define MX6QDL_PAD_EIM_D19__EIM_DATA19 0x09c 0x3b0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D19__ECSPI1_SS1 0x09c 0x3b0 0x804 0x1 0x0 +#define MX6QDL_PAD_EIM_D19__IPU1_DI0_PIN08 0x09c 0x3b0 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D19__IPU2_CSI1_DATA16 0x09c 0x3b0 0x8c8 0x3 0x0 +#define MX6QDL_PAD_EIM_D19__UART1_CTS_B 0x09c 0x3b0 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D19__UART1_RTS_B 0x09c 0x3b0 0x91c 0x4 0x0 +#define MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x09c 0x3b0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D19__EPIT1_OUT 0x09c 0x3b0 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D20__EIM_DATA20 0x0a0 0x3b4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D20__ECSPI4_SS0 0x0a0 0x3b4 0x824 0x1 0x0 +#define MX6QDL_PAD_EIM_D20__IPU1_DI0_PIN16 0x0a0 0x3b4 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D20__IPU2_CSI1_DATA15 0x0a0 0x3b4 0x8c4 0x3 0x0 +#define MX6QDL_PAD_EIM_D20__UART1_RTS_B 0x0a0 0x3b4 0x91c 0x4 0x1 +#define MX6QDL_PAD_EIM_D20__UART1_CTS_B 0x0a0 0x3b4 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x0a0 0x3b4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D20__EPIT2_OUT 0x0a0 0x3b4 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D21__EIM_DATA21 0x0a4 0x3b8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D21__ECSPI4_SCLK 0x0a4 0x3b8 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D21__IPU1_DI0_PIN17 0x0a4 0x3b8 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D21__IPU2_CSI1_DATA11 0x0a4 0x3b8 0x8b4 0x3 0x0 +#define MX6QDL_PAD_EIM_D21__USB_OTG_OC 0x0a4 0x3b8 0x944 0x4 0x0 +#define MX6QDL_PAD_EIM_D21__GPIO3_IO21 0x0a4 0x3b8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D21__I2C1_SCL 0x0a4 0x3b8 0x898 0x6 0x0 +#define MX6QDL_PAD_EIM_D21__SPDIF_IN 0x0a4 0x3b8 0x914 0x7 0x0 +#define MX6QDL_PAD_EIM_D22__EIM_DATA22 0x0a8 0x3bc 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D22__ECSPI4_MISO 0x0a8 0x3bc 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D22__IPU1_DI0_PIN01 0x0a8 0x3bc 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D22__IPU2_CSI1_DATA10 0x0a8 0x3bc 0x8b0 0x3 0x0 +#define MX6QDL_PAD_EIM_D22__USB_OTG_PWR 0x0a8 0x3bc 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x0a8 0x3bc 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D22__SPDIF_OUT 0x0a8 0x3bc 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D23__EIM_DATA23 0x0ac 0x3c0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D23__IPU1_DI0_D0_CS 0x0ac 0x3c0 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D23__UART3_CTS_B 0x0ac 0x3c0 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D23__UART3_RTS_B 0x0ac 0x3c0 0x92c 0x2 0x0 +#define MX6QDL_PAD_EIM_D23__UART1_DCD_B 0x0ac 0x3c0 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_D23__IPU2_CSI1_DATA_EN 0x0ac 0x3c0 0x8d8 0x4 0x0 +#define MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x0ac 0x3c0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D23__IPU1_DI1_PIN02 0x0ac 0x3c0 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D23__IPU1_DI1_PIN14 0x0ac 0x3c0 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_EB3__EIM_EB3_B 0x0b0 0x3c4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_EB3__ECSPI4_RDY 0x0b0 0x3c4 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x0b0 0x3c4 0x92c 0x2 0x1 +#define MX6QDL_PAD_EIM_EB3__UART3_CTS_B 0x0b0 0x3c4 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_EB3__UART1_RI_B 0x0b0 0x3c4 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_EB3__IPU2_CSI1_HSYNC 0x0b0 0x3c4 0x8dc 0x4 0x0 +#define MX6QDL_PAD_EIM_EB3__GPIO2_IO31 0x0b0 0x3c4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_EB3__IPU1_DI1_PIN03 0x0b0 0x3c4 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_EB3__SRC_BOOT_CFG31 0x0b0 0x3c4 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D24__EIM_DATA24 0x0b4 0x3c8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D24__ECSPI4_SS2 0x0b4 0x3c8 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x0b4 0x3c8 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D24__UART3_RX_DATA 0x0b4 0x3c8 0x930 0x2 0x0 +#define MX6QDL_PAD_EIM_D24__ECSPI1_SS2 0x0b4 0x3c8 0x808 0x3 0x0 +#define MX6QDL_PAD_EIM_D24__ECSPI2_SS2 0x0b4 0x3c8 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D24__GPIO3_IO24 0x0b4 0x3c8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D24__AUD5_RXFS 0x0b4 0x3c8 0x7d8 0x6 0x0 +#define MX6QDL_PAD_EIM_D24__UART1_DTR_B 0x0b4 0x3c8 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D25__EIM_DATA25 0x0b8 0x3cc 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D25__ECSPI4_SS3 0x0b8 0x3cc 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x0b8 0x3cc 0x930 0x2 0x1 +#define MX6QDL_PAD_EIM_D25__UART3_TX_DATA 0x0b8 0x3cc 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D25__ECSPI1_SS3 0x0b8 0x3cc 0x80c 0x3 0x0 +#define MX6QDL_PAD_EIM_D25__ECSPI2_SS3 0x0b8 0x3cc 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D25__GPIO3_IO25 0x0b8 0x3cc 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D25__AUD5_RXC 0x0b8 0x3cc 0x7d4 0x6 0x0 +#define MX6QDL_PAD_EIM_D25__UART1_DSR_B 0x0b8 0x3cc 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D26__EIM_DATA26 0x0bc 0x3d0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_DI1_PIN11 0x0bc 0x3d0 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_CSI0_DATA01 0x0bc 0x3d0 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D26__IPU2_CSI1_DATA14 0x0bc 0x3d0 0x8c0 0x3 0x0 +#define MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x0bc 0x3d0 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D26__UART2_RX_DATA 0x0bc 0x3d0 0x928 0x4 0x0 +#define MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x0bc 0x3d0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_SISG2 0x0bc 0x3d0 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D26__IPU1_DISP1_DATA22 0x0bc 0x3d0 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D27__EIM_DATA27 0x0c0 0x3d4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_DI1_PIN13 0x0c0 0x3d4 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_CSI0_DATA00 0x0c0 0x3d4 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D27__IPU2_CSI1_DATA13 0x0c0 0x3d4 0x8bc 0x3 0x0 +#define MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x0c0 0x3d4 0x928 0x4 0x1 +#define MX6QDL_PAD_EIM_D27__UART2_TX_DATA 0x0c0 0x3d4 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D27__GPIO3_IO27 0x0c0 0x3d4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_SISG3 0x0c0 0x3d4 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D27__IPU1_DISP1_DATA23 0x0c0 0x3d4 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D28__EIM_DATA28 0x0c4 0x3d8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D28__I2C1_SDA 0x0c4 0x3d8 0x89c 0x1 0x0 +#define MX6QDL_PAD_EIM_D28__ECSPI4_MOSI 0x0c4 0x3d8 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D28__IPU2_CSI1_DATA12 0x0c4 0x3d8 0x8b8 0x3 0x0 +#define MX6QDL_PAD_EIM_D28__UART2_CTS_B 0x0c4 0x3d8 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D28__UART2_RTS_B 0x0c4 0x3d8 0x924 0x4 0x0 +#define MX6QDL_PAD_EIM_D28__UART2_DTE_CTS_B 0x0c4 0x3d8 0x924 0x4 0x0 +#define MX6QDL_PAD_EIM_D28__UART2_DTE_RTS_B 0x0c4 0x3d8 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D28__GPIO3_IO28 0x0c4 0x3d8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D28__IPU1_EXT_TRIG 0x0c4 0x3d8 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_D28__IPU1_DI0_PIN13 0x0c4 0x3d8 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D29__EIM_DATA29 0x0c8 0x3dc 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D29__IPU1_DI1_PIN15 0x0c8 0x3dc 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D29__ECSPI4_SS0 0x0c8 0x3dc 0x824 0x2 0x1 +#define MX6QDL_PAD_EIM_D29__UART2_RTS_B 0x0c8 0x3dc 0x924 0x4 0x1 +#define MX6QDL_PAD_EIM_D29__UART2_CTS_B 0x0c8 0x3dc 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B 0x0c8 0x3dc 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D29__UART2_DTE_CTS_B 0x0c8 0x3dc 0x924 0x4 0x1 +#define MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x0c8 0x3dc 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D29__IPU2_CSI1_VSYNC 0x0c8 0x3dc 0x8e4 0x6 0x0 +#define MX6QDL_PAD_EIM_D29__IPU1_DI0_PIN14 0x0c8 0x3dc 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_D30__EIM_DATA30 0x0cc 0x3e0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D30__IPU1_DISP1_DATA21 0x0cc 0x3e0 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D30__IPU1_DI0_PIN11 0x0cc 0x3e0 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D30__IPU1_CSI0_DATA03 0x0cc 0x3e0 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_D30__UART3_CTS_B 0x0cc 0x3e0 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D30__UART3_RTS_B 0x0cc 0x3e0 0x92c 0x4 0x2 +#define MX6QDL_PAD_EIM_D30__GPIO3_IO30 0x0cc 0x3e0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D30__USB_H1_OC 0x0cc 0x3e0 0x948 0x6 0x0 +#define MX6QDL_PAD_EIM_D31__EIM_DATA31 0x0d0 0x3e4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_D31__IPU1_DISP1_DATA20 0x0d0 0x3e4 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_D31__IPU1_DI0_PIN12 0x0d0 0x3e4 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_D31__IPU1_CSI0_DATA02 0x0d0 0x3e4 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_D31__UART3_RTS_B 0x0d0 0x3e4 0x92c 0x4 0x3 +#define MX6QDL_PAD_EIM_D31__UART3_CTS_B 0x0d0 0x3e4 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_D31__GPIO3_IO31 0x0d0 0x3e4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_D31__USB_H1_PWR 0x0d0 0x3e4 0x000 0x6 0x0 +#define MX6QDL_PAD_EIM_A24__EIM_ADDR24 0x0d4 0x3e8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A24__IPU1_DISP1_DATA19 0x0d4 0x3e8 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A24__IPU2_CSI1_DATA19 0x0d4 0x3e8 0x8d4 0x2 0x1 +#define MX6QDL_PAD_EIM_A24__IPU2_SISG2 0x0d4 0x3e8 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_A24__IPU1_SISG2 0x0d4 0x3e8 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_A24__GPIO5_IO04 0x0d4 0x3e8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A24__SRC_BOOT_CFG24 0x0d4 0x3e8 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A23__EIM_ADDR23 0x0d8 0x3ec 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A23__IPU1_DISP1_DATA18 0x0d8 0x3ec 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A23__IPU2_CSI1_DATA18 0x0d8 0x3ec 0x8d0 0x2 0x1 +#define MX6QDL_PAD_EIM_A23__IPU2_SISG3 0x0d8 0x3ec 0x000 0x3 0x0 +#define MX6QDL_PAD_EIM_A23__IPU1_SISG3 0x0d8 0x3ec 0x000 0x4 0x0 +#define MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x0d8 0x3ec 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A23__SRC_BOOT_CFG23 0x0d8 0x3ec 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A22__EIM_ADDR22 0x0dc 0x3f0 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A22__IPU1_DISP1_DATA17 0x0dc 0x3f0 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A22__IPU2_CSI1_DATA17 0x0dc 0x3f0 0x8cc 0x2 0x1 +#define MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x0dc 0x3f0 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A22__SRC_BOOT_CFG22 0x0dc 0x3f0 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A21__EIM_ADDR21 0x0e0 0x3f4 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A21__IPU1_DISP1_DATA16 0x0e0 0x3f4 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A21__IPU2_CSI1_DATA16 0x0e0 0x3f4 0x8c8 0x2 0x1 +#define MX6QDL_PAD_EIM_A21__GPIO2_IO17 0x0e0 0x3f4 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A21__SRC_BOOT_CFG21 0x0e0 0x3f4 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A20__EIM_ADDR20 0x0e4 0x3f8 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A20__IPU1_DISP1_DATA15 0x0e4 0x3f8 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A20__IPU2_CSI1_DATA15 0x0e4 0x3f8 0x8c4 0x2 0x1 +#define MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x0e4 0x3f8 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A20__SRC_BOOT_CFG20 0x0e4 0x3f8 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A19__EIM_ADDR19 0x0e8 0x3fc 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A19__IPU1_DISP1_DATA14 0x0e8 0x3fc 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A19__IPU2_CSI1_DATA14 0x0e8 0x3fc 0x8c0 0x2 0x1 +#define MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x0e8 0x3fc 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A19__SRC_BOOT_CFG19 0x0e8 0x3fc 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A18__EIM_ADDR18 0x0ec 0x400 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A18__IPU1_DISP1_DATA13 0x0ec 0x400 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A18__IPU2_CSI1_DATA13 0x0ec 0x400 0x8bc 0x2 0x1 +#define MX6QDL_PAD_EIM_A18__GPIO2_IO20 0x0ec 0x400 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A18__SRC_BOOT_CFG18 0x0ec 0x400 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A17__EIM_ADDR17 0x0f0 0x404 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A17__IPU1_DISP1_DATA12 0x0f0 0x404 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A17__IPU2_CSI1_DATA12 0x0f0 0x404 0x8b8 0x2 0x1 +#define MX6QDL_PAD_EIM_A17__GPIO2_IO21 0x0f0 0x404 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A17__SRC_BOOT_CFG17 0x0f0 0x404 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_A16__EIM_ADDR16 0x0f4 0x408 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_A16__IPU1_DI1_DISP_CLK 0x0f4 0x408 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_A16__IPU2_CSI1_PIXCLK 0x0f4 0x408 0x8e0 0x2 0x1 +#define MX6QDL_PAD_EIM_A16__GPIO2_IO22 0x0f4 0x408 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_A16__SRC_BOOT_CFG16 0x0f4 0x408 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_CS0__EIM_CS0_B 0x0f8 0x40c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_CS0__IPU1_DI1_PIN05 0x0f8 0x40c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_CS0__ECSPI2_SCLK 0x0f8 0x40c 0x810 0x2 0x0 +#define MX6QDL_PAD_EIM_CS0__GPIO2_IO23 0x0f8 0x40c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_CS1__EIM_CS1_B 0x0fc 0x410 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_CS1__IPU1_DI1_PIN06 0x0fc 0x410 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_CS1__ECSPI2_MOSI 0x0fc 0x410 0x818 0x2 0x0 +#define MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x0fc 0x410 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_OE__EIM_OE_B 0x100 0x414 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_OE__IPU1_DI1_PIN07 0x100 0x414 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_OE__ECSPI2_MISO 0x100 0x414 0x814 0x2 0x0 +#define MX6QDL_PAD_EIM_OE__GPIO2_IO25 0x100 0x414 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_RW__EIM_RW 0x104 0x418 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_RW__IPU1_DI1_PIN08 0x104 0x418 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_RW__ECSPI2_SS0 0x104 0x418 0x81c 0x2 0x0 +#define MX6QDL_PAD_EIM_RW__GPIO2_IO26 0x104 0x418 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_RW__SRC_BOOT_CFG29 0x104 0x418 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_LBA__EIM_LBA_B 0x108 0x41c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_LBA__IPU1_DI1_PIN17 0x108 0x41c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_LBA__ECSPI2_SS1 0x108 0x41c 0x820 0x2 0x0 +#define MX6QDL_PAD_EIM_LBA__GPIO2_IO27 0x108 0x41c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_LBA__SRC_BOOT_CFG26 0x108 0x41c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_EB0__EIM_EB0_B 0x10c 0x420 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_EB0__IPU1_DISP1_DATA11 0x10c 0x420 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_EB0__IPU2_CSI1_DATA11 0x10c 0x420 0x8b4 0x2 0x1 +#define MX6QDL_PAD_EIM_EB0__CCM_PMIC_READY 0x10c 0x420 0x7f0 0x4 0x0 +#define MX6QDL_PAD_EIM_EB0__GPIO2_IO28 0x10c 0x420 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_EB0__SRC_BOOT_CFG27 0x10c 0x420 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_EB1__EIM_EB1_B 0x110 0x424 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_EB1__IPU1_DISP1_DATA10 0x110 0x424 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_EB1__IPU2_CSI1_DATA10 0x110 0x424 0x8b0 0x2 0x1 +#define MX6QDL_PAD_EIM_EB1__GPIO2_IO29 0x110 0x424 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_EB1__SRC_BOOT_CFG28 0x110 0x424 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA0__EIM_AD00 0x114 0x428 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA0__IPU1_DISP1_DATA09 0x114 0x428 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA0__IPU2_CSI1_DATA09 0x114 0x428 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA0__GPIO3_IO00 0x114 0x428 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA0__SRC_BOOT_CFG00 0x114 0x428 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA1__EIM_AD01 0x118 0x42c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA1__IPU1_DISP1_DATA08 0x118 0x42c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA1__IPU2_CSI1_DATA08 0x118 0x42c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA1__GPIO3_IO01 0x118 0x42c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA1__SRC_BOOT_CFG01 0x118 0x42c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA2__EIM_AD02 0x11c 0x430 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA2__IPU1_DISP1_DATA07 0x11c 0x430 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA2__IPU2_CSI1_DATA07 0x11c 0x430 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA2__GPIO3_IO02 0x11c 0x430 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA2__SRC_BOOT_CFG02 0x11c 0x430 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA3__EIM_AD03 0x120 0x434 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA3__IPU1_DISP1_DATA06 0x120 0x434 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA3__IPU2_CSI1_DATA06 0x120 0x434 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA3__GPIO3_IO03 0x120 0x434 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA3__SRC_BOOT_CFG03 0x120 0x434 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA4__EIM_AD04 0x124 0x438 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA4__IPU1_DISP1_DATA05 0x124 0x438 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA4__IPU2_CSI1_DATA05 0x124 0x438 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA4__GPIO3_IO04 0x124 0x438 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA4__SRC_BOOT_CFG04 0x124 0x438 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA5__EIM_AD05 0x128 0x43c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA5__IPU1_DISP1_DATA04 0x128 0x43c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA5__IPU2_CSI1_DATA04 0x128 0x43c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA5__GPIO3_IO05 0x128 0x43c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA5__SRC_BOOT_CFG05 0x128 0x43c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA6__EIM_AD06 0x12c 0x440 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA6__IPU1_DISP1_DATA03 0x12c 0x440 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA6__IPU2_CSI1_DATA03 0x12c 0x440 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA6__GPIO3_IO06 0x12c 0x440 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA6__SRC_BOOT_CFG06 0x12c 0x440 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA7__EIM_AD07 0x130 0x444 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA7__IPU1_DISP1_DATA02 0x130 0x444 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA7__IPU2_CSI1_DATA02 0x130 0x444 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA7__GPIO3_IO07 0x130 0x444 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA7__SRC_BOOT_CFG07 0x130 0x444 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA8__EIM_AD08 0x134 0x448 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA8__IPU1_DISP1_DATA01 0x134 0x448 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA8__IPU2_CSI1_DATA01 0x134 0x448 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA8__GPIO3_IO08 0x134 0x448 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA8__SRC_BOOT_CFG08 0x134 0x448 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA9__EIM_AD09 0x138 0x44c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA9__IPU1_DISP1_DATA00 0x138 0x44c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA9__IPU2_CSI1_DATA00 0x138 0x44c 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x138 0x44c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA9__SRC_BOOT_CFG09 0x138 0x44c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA10__EIM_AD10 0x13c 0x450 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA10__IPU1_DI1_PIN15 0x13c 0x450 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA10__IPU2_CSI1_DATA_EN 0x13c 0x450 0x8d8 0x2 0x1 +#define MX6QDL_PAD_EIM_DA10__GPIO3_IO10 0x13c 0x450 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA10__SRC_BOOT_CFG10 0x13c 0x450 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA11__EIM_AD11 0x140 0x454 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA11__IPU1_DI1_PIN02 0x140 0x454 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA11__IPU2_CSI1_HSYNC 0x140 0x454 0x8dc 0x2 0x1 +#define MX6QDL_PAD_EIM_DA11__GPIO3_IO11 0x140 0x454 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA11__SRC_BOOT_CFG11 0x140 0x454 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA12__EIM_AD12 0x144 0x458 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA12__IPU1_DI1_PIN03 0x144 0x458 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA12__IPU2_CSI1_VSYNC 0x144 0x458 0x8e4 0x2 0x1 +#define MX6QDL_PAD_EIM_DA12__GPIO3_IO12 0x144 0x458 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA12__SRC_BOOT_CFG12 0x144 0x458 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA13__EIM_AD13 0x148 0x45c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA13__IPU1_DI1_D0_CS 0x148 0x45c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA13__GPIO3_IO13 0x148 0x45c 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA13__SRC_BOOT_CFG13 0x148 0x45c 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA14__EIM_AD14 0x14c 0x460 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA14__IPU1_DI1_D1_CS 0x14c 0x460 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA14__GPIO3_IO14 0x14c 0x460 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA14__SRC_BOOT_CFG14 0x14c 0x460 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_DA15__EIM_AD15 0x150 0x464 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_DA15__IPU1_DI1_PIN01 0x150 0x464 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_DA15__IPU1_DI1_PIN04 0x150 0x464 0x000 0x2 0x0 +#define MX6QDL_PAD_EIM_DA15__GPIO3_IO15 0x150 0x464 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_DA15__SRC_BOOT_CFG15 0x150 0x464 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_WAIT__EIM_WAIT_B 0x154 0x468 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_WAIT__EIM_DTACK_B 0x154 0x468 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_WAIT__GPIO5_IO00 0x154 0x468 0x000 0x5 0x0 +#define MX6QDL_PAD_EIM_WAIT__SRC_BOOT_CFG25 0x154 0x468 0x000 0x7 0x0 +#define MX6QDL_PAD_EIM_BCLK__EIM_BCLK 0x158 0x46c 0x000 0x0 0x0 +#define MX6QDL_PAD_EIM_BCLK__IPU1_DI1_PIN16 0x158 0x46c 0x000 0x1 0x0 +#define MX6QDL_PAD_EIM_BCLK__GPIO6_IO31 0x158 0x46c 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x15c 0x470 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_DISP_CLK__IPU2_DI0_DISP_CLK 0x15c 0x470 0x000 0x1 0x0 +#define MX6QDL_PAD_DI0_DISP_CLK__GPIO4_IO16 0x15c 0x470 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x160 0x474 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_PIN15__IPU2_DI0_PIN15 0x160 0x474 0x000 0x1 0x0 +#define MX6QDL_PAD_DI0_PIN15__AUD6_TXC 0x160 0x474 0x000 0x2 0x0 +#define MX6QDL_PAD_DI0_PIN15__GPIO4_IO17 0x160 0x474 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x164 0x478 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_PIN2__IPU2_DI0_PIN02 0x164 0x478 0x000 0x1 0x0 +#define MX6QDL_PAD_DI0_PIN2__AUD6_TXD 0x164 0x478 0x000 0x2 0x0 +#define MX6QDL_PAD_DI0_PIN2__GPIO4_IO18 0x164 0x478 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x168 0x47c 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_PIN3__IPU2_DI0_PIN03 0x168 0x47c 0x000 0x1 0x0 +#define MX6QDL_PAD_DI0_PIN3__AUD6_TXFS 0x168 0x47c 0x000 0x2 0x0 +#define MX6QDL_PAD_DI0_PIN3__GPIO4_IO19 0x168 0x47c 0x000 0x5 0x0 +#define MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x16c 0x480 0x000 0x0 0x0 +#define MX6QDL_PAD_DI0_PIN4__IPU2_DI0_PIN04 0x16c 0x480 0x000 0x1 0x0 +#define MX6QDL_PAD_DI0_PIN4__AUD6_RXD 0x16c 0x480 0x000 0x2 0x0 +#define MX6QDL_PAD_DI0_PIN4__SD1_WP 0x16c 0x480 0x94c 0x3 0x0 +#define MX6QDL_PAD_DI0_PIN4__GPIO4_IO20 0x16c 0x480 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x170 0x484 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT0__IPU2_DISP0_DATA00 0x170 0x484 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x170 0x484 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT0__GPIO4_IO21 0x170 0x484 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x174 0x488 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT1__IPU2_DISP0_DATA01 0x174 0x488 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x174 0x488 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT1__GPIO4_IO22 0x174 0x488 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x178 0x48c 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT2__IPU2_DISP0_DATA02 0x178 0x48c 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x178 0x48c 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT2__GPIO4_IO23 0x178 0x48c 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x17c 0x490 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT3__IPU2_DISP0_DATA03 0x17c 0x490 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT3__ECSPI3_SS0 0x17c 0x490 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24 0x17c 0x490 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x180 0x494 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT4__IPU2_DISP0_DATA04 0x180 0x494 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT4__ECSPI3_SS1 0x180 0x494 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT4__GPIO4_IO25 0x180 0x494 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x184 0x498 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT5__IPU2_DISP0_DATA05 0x184 0x498 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT5__ECSPI3_SS2 0x184 0x498 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT5__AUD6_RXFS 0x184 0x498 0x000 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT5__GPIO4_IO26 0x184 0x498 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x188 0x49c 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT6__IPU2_DISP0_DATA06 0x188 0x49c 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT6__ECSPI3_SS3 0x188 0x49c 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT6__AUD6_RXC 0x188 0x49c 0x000 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT6__GPIO4_IO27 0x188 0x49c 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x18c 0x4a0 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT7__IPU2_DISP0_DATA07 0x18c 0x4a0 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT7__ECSPI3_RDY 0x18c 0x4a0 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT7__GPIO4_IO28 0x18c 0x4a0 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x190 0x4a4 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT8__IPU2_DISP0_DATA08 0x190 0x4a4 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT8__PWM1_OUT 0x190 0x4a4 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT8__WDOG1_B 0x190 0x4a4 0x000 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT8__GPIO4_IO29 0x190 0x4a4 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x194 0x4a8 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT9__IPU2_DISP0_DATA09 0x194 0x4a8 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT9__PWM2_OUT 0x194 0x4a8 0x000 0x2 0x0 +#define MX6QDL_PAD_DISP0_DAT9__WDOG2_B 0x194 0x4a8 0x000 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT9__GPIO4_IO30 0x194 0x4a8 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x198 0x4ac 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT10__IPU2_DISP0_DATA10 0x198 0x4ac 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT10__GPIO4_IO31 0x198 0x4ac 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x19c 0x4b0 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT11__IPU2_DISP0_DATA11 0x19c 0x4b0 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x19c 0x4b0 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x1a0 0x4b4 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT12__IPU2_DISP0_DATA12 0x1a0 0x4b4 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT12__GPIO5_IO06 0x1a0 0x4b4 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x1a4 0x4b8 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT13__IPU2_DISP0_DATA13 0x1a4 0x4b8 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT13__AUD5_RXFS 0x1a4 0x4b8 0x7d8 0x3 0x1 +#define MX6QDL_PAD_DISP0_DAT13__GPIO5_IO07 0x1a4 0x4b8 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x1a8 0x4bc 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT14__IPU2_DISP0_DATA14 0x1a8 0x4bc 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT14__AUD5_RXC 0x1a8 0x4bc 0x7d4 0x3 0x1 +#define MX6QDL_PAD_DISP0_DAT14__GPIO5_IO08 0x1a8 0x4bc 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x1ac 0x4c0 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT15__IPU2_DISP0_DATA15 0x1ac 0x4c0 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT15__ECSPI1_SS1 0x1ac 0x4c0 0x804 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT15__ECSPI2_SS1 0x1ac 0x4c0 0x820 0x3 0x1 +#define MX6QDL_PAD_DISP0_DAT15__GPIO5_IO09 0x1ac 0x4c0 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x1b0 0x4c4 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT16__IPU2_DISP0_DATA16 0x1b0 0x4c4 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT16__ECSPI2_MOSI 0x1b0 0x4c4 0x818 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT16__AUD5_TXC 0x1b0 0x4c4 0x7dc 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT16__SDMA_EXT_EVENT0 0x1b0 0x4c4 0x90c 0x4 0x0 +#define MX6QDL_PAD_DISP0_DAT16__GPIO5_IO10 0x1b0 0x4c4 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x1b4 0x4c8 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT17__IPU2_DISP0_DATA17 0x1b4 0x4c8 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT17__ECSPI2_MISO 0x1b4 0x4c8 0x814 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT17__AUD5_TXD 0x1b4 0x4c8 0x7d0 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT17__SDMA_EXT_EVENT1 0x1b4 0x4c8 0x910 0x4 0x0 +#define MX6QDL_PAD_DISP0_DAT17__GPIO5_IO11 0x1b4 0x4c8 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x1b8 0x4cc 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT18__IPU2_DISP0_DATA18 0x1b8 0x4cc 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT18__ECSPI2_SS0 0x1b8 0x4cc 0x81c 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT18__AUD5_TXFS 0x1b8 0x4cc 0x7e0 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT18__AUD4_RXFS 0x1b8 0x4cc 0x7c0 0x4 0x0 +#define MX6QDL_PAD_DISP0_DAT18__GPIO5_IO12 0x1b8 0x4cc 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT18__EIM_CS2_B 0x1b8 0x4cc 0x000 0x7 0x0 +#define MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x1bc 0x4d0 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT19__IPU2_DISP0_DATA19 0x1bc 0x4d0 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT19__ECSPI2_SCLK 0x1bc 0x4d0 0x810 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x1bc 0x4d0 0x7cc 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT19__AUD4_RXC 0x1bc 0x4d0 0x7bc 0x4 0x0 +#define MX6QDL_PAD_DISP0_DAT19__GPIO5_IO13 0x1bc 0x4d0 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT19__EIM_CS3_B 0x1bc 0x4d0 0x000 0x7 0x0 +#define MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x1c0 0x4d4 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT20__IPU2_DISP0_DATA20 0x1c0 0x4d4 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT20__ECSPI1_SCLK 0x1c0 0x4d4 0x7f4 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT20__AUD4_TXC 0x1c0 0x4d4 0x7c4 0x3 0x0 +#define MX6QDL_PAD_DISP0_DAT20__GPIO5_IO14 0x1c0 0x4d4 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x1c4 0x4d8 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT21__IPU2_DISP0_DATA21 0x1c4 0x4d8 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT21__ECSPI1_MOSI 0x1c4 0x4d8 0x7fc 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT21__AUD4_TXD 0x1c4 0x4d8 0x7b8 0x3 0x1 +#define MX6QDL_PAD_DISP0_DAT21__GPIO5_IO15 0x1c4 0x4d8 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x1c8 0x4dc 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT22__IPU2_DISP0_DATA22 0x1c8 0x4dc 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT22__ECSPI1_MISO 0x1c8 0x4dc 0x7f8 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT22__AUD4_TXFS 0x1c8 0x4dc 0x7c8 0x3 0x1 +#define MX6QDL_PAD_DISP0_DAT22__GPIO5_IO16 0x1c8 0x4dc 0x000 0x5 0x0 +#define MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x1cc 0x4e0 0x000 0x0 0x0 +#define MX6QDL_PAD_DISP0_DAT23__IPU2_DISP0_DATA23 0x1cc 0x4e0 0x000 0x1 0x0 +#define MX6QDL_PAD_DISP0_DAT23__ECSPI1_SS0 0x1cc 0x4e0 0x800 0x2 0x1 +#define MX6QDL_PAD_DISP0_DAT23__AUD4_RXD 0x1cc 0x4e0 0x7b4 0x3 0x1 +#define MX6QDL_PAD_DISP0_DAT23__GPIO5_IO17 0x1cc 0x4e0 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1d0 0x4e4 0x840 0x1 0x0 +#define MX6QDL_PAD_ENET_MDIO__ESAI_RX_CLK 0x1d0 0x4e4 0x86c 0x2 0x0 +#define MX6QDL_PAD_ENET_MDIO__ENET_1588_EVENT1_OUT 0x1d0 0x4e4 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_MDIO__GPIO1_IO22 0x1d0 0x4e4 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_MDIO__SPDIF_LOCK 0x1d0 0x4e4 0x000 0x6 0x0 +#define MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1d4 0x4e8 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_REF_CLK__ESAI_RX_FS 0x1d4 0x4e8 0x85c 0x2 0x0 +#define MX6QDL_PAD_ENET_REF_CLK__GPIO1_IO23 0x1d4 0x4e8 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_REF_CLK__SPDIF_SR_CLK 0x1d4 0x4e8 0x000 0x6 0x0 +#define MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x1d8 0x4ec 0x004 0x0 0xff0d0100 +#define MX6QDL_PAD_ENET_RX_ER__ENET_RX_ER 0x1d8 0x4ec 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_RX_ER__ESAI_RX_HF_CLK 0x1d8 0x4ec 0x864 0x2 0x0 +#define MX6QDL_PAD_ENET_RX_ER__SPDIF_IN 0x1d8 0x4ec 0x914 0x3 0x1 +#define MX6QDL_PAD_ENET_RX_ER__ENET_1588_EVENT2_OUT 0x1d8 0x4ec 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_RX_ER__GPIO1_IO24 0x1d8 0x4ec 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_CRS_DV__ENET_RX_EN 0x1dc 0x4f0 0x858 0x1 0x1 +#define MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1dc 0x4f0 0x870 0x2 0x0 +#define MX6QDL_PAD_ENET_CRS_DV__SPDIF_EXT_CLK 0x1dc 0x4f0 0x918 0x3 0x1 +#define MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x1dc 0x4f0 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_RXD1__MLB_SIG 0x1e0 0x4f4 0x908 0x0 0x0 +#define MX6QDL_PAD_ENET_RXD1__ENET_RX_DATA1 0x1e0 0x4f4 0x84c 0x1 0x1 +#define MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1e0 0x4f4 0x860 0x2 0x0 +#define MX6QDL_PAD_ENET_RXD1__ENET_1588_EVENT3_OUT 0x1e0 0x4f4 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x1e0 0x4f4 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_RXD0__ENET_RX_DATA0 0x1e4 0x4f8 0x848 0x1 0x1 +#define MX6QDL_PAD_ENET_RXD0__ESAI_TX_HF_CLK 0x1e4 0x4f8 0x868 0x2 0x0 +#define MX6QDL_PAD_ENET_RXD0__SPDIF_OUT 0x1e4 0x4f8 0x000 0x3 0x0 +#define MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x1e4 0x4f8 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1e8 0x4fc 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1e8 0x4fc 0x880 0x2 0x0 +#define MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x1e8 0x4fc 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_TXD1__MLB_CLK 0x1ec 0x500 0x900 0x0 0x0 +#define MX6QDL_PAD_ENET_TXD1__ENET_TX_DATA1 0x1ec 0x500 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_TXD1__ESAI_TX2_RX3 0x1ec 0x500 0x87c 0x2 0x0 +#define MX6QDL_PAD_ENET_TXD1__ENET_1588_EVENT0_IN 0x1ec 0x500 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x1ec 0x500 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_TXD0__ENET_TX_DATA0 0x1f0 0x504 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1f0 0x504 0x884 0x2 0x0 +#define MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x1f0 0x504 0x000 0x5 0x0 +#define MX6QDL_PAD_ENET_MDC__MLB_DATA 0x1f4 0x508 0x904 0x0 0x0 +#define MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1f4 0x508 0x000 0x1 0x0 +#define MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1f4 0x508 0x888 0x2 0x0 +#define MX6QDL_PAD_ENET_MDC__ENET_1588_EVENT1_IN 0x1f4 0x508 0x000 0x4 0x0 +#define MX6QDL_PAD_ENET_MDC__GPIO1_IO31 0x1f4 0x508 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x1f8 0x5c8 0x7f4 0x0 0x2 +#define MX6QDL_PAD_KEY_COL0__ENET_RX_DATA3 0x1f8 0x5c8 0x854 0x1 0x1 +#define MX6QDL_PAD_KEY_COL0__AUD5_TXC 0x1f8 0x5c8 0x7dc 0x2 0x1 +#define MX6QDL_PAD_KEY_COL0__KEY_COL0 0x1f8 0x5c8 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1f8 0x5c8 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_COL0__UART4_RX_DATA 0x1f8 0x5c8 0x938 0x4 0x0 +#define MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x1f8 0x5c8 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL0__DCIC1_OUT 0x1f8 0x5c8 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x1fc 0x5cc 0x7fc 0x0 0x2 +#define MX6QDL_PAD_KEY_ROW0__ENET_TX_DATA3 0x1fc 0x5cc 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW0__AUD5_TXD 0x1fc 0x5cc 0x7d0 0x2 0x1 +#define MX6QDL_PAD_KEY_ROW0__KEY_ROW0 0x1fc 0x5cc 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1fc 0x5cc 0x938 0x4 0x1 +#define MX6QDL_PAD_KEY_ROW0__UART4_TX_DATA 0x1fc 0x5cc 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x1fc 0x5cc 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW0__DCIC2_OUT 0x1fc 0x5cc 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x200 0x5d0 0x7f8 0x0 0x2 +#define MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x200 0x5d0 0x840 0x1 0x1 +#define MX6QDL_PAD_KEY_COL1__AUD5_TXFS 0x200 0x5d0 0x7e0 0x2 0x1 +#define MX6QDL_PAD_KEY_COL1__KEY_COL1 0x200 0x5d0 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x200 0x5d0 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_COL1__UART5_RX_DATA 0x200 0x5d0 0x940 0x4 0x0 +#define MX6QDL_PAD_KEY_COL1__GPIO4_IO08 0x200 0x5d0 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL1__SD1_VSELECT 0x200 0x5d0 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_ROW1__ECSPI1_SS0 0x204 0x5d4 0x800 0x0 0x2 +#define MX6QDL_PAD_KEY_ROW1__ENET_COL 0x204 0x5d4 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW1__AUD5_RXD 0x204 0x5d4 0x7cc 0x2 0x1 +#define MX6QDL_PAD_KEY_ROW1__KEY_ROW1 0x204 0x5d4 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x204 0x5d4 0x940 0x4 0x1 +#define MX6QDL_PAD_KEY_ROW1__UART5_TX_DATA 0x204 0x5d4 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_ROW1__GPIO4_IO09 0x204 0x5d4 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW1__SD2_VSELECT 0x204 0x5d4 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_COL2__ECSPI1_SS1 0x208 0x5d8 0x804 0x0 0x2 +#define MX6QDL_PAD_KEY_COL2__ENET_RX_DATA2 0x208 0x5d8 0x850 0x1 0x1 +#define MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x208 0x5d8 0x000 0x2 0x0 +#define MX6QDL_PAD_KEY_COL2__KEY_COL2 0x208 0x5d8 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL2__ENET_MDC 0x208 0x5d8 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_COL2__GPIO4_IO10 0x208 0x5d8 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL2__USB_H1_PWR_CTL_WAKE 0x208 0x5d8 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_ROW2__ECSPI1_SS2 0x20c 0x5dc 0x808 0x0 0x1 +#define MX6QDL_PAD_KEY_ROW2__ENET_TX_DATA2 0x20c 0x5dc 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x20c 0x5dc 0x7e4 0x2 0x0 +#define MX6QDL_PAD_KEY_ROW2__KEY_ROW2 0x20c 0x5dc 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW2__SD2_VSELECT 0x20c 0x5dc 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_ROW2__GPIO4_IO11 0x20c 0x5dc 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x20c 0x5dc 0x88c 0x6 0x1 +#define MX6QDL_PAD_KEY_COL3__ECSPI1_SS3 0x210 0x5e0 0x80c 0x0 0x1 +#define MX6QDL_PAD_KEY_COL3__ENET_CRS 0x210 0x5e0 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x210 0x5e0 0x890 0x2 0x1 +#define MX6QDL_PAD_KEY_COL3__KEY_COL3 0x210 0x5e0 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x210 0x5e0 0x8a0 0x4 0x1 +#define MX6QDL_PAD_KEY_COL3__GPIO4_IO12 0x210 0x5e0 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x210 0x5e0 0x914 0x6 0x2 +#define MX6QDL_PAD_KEY_ROW3__ASRC_EXT_CLK 0x214 0x5e4 0x7b0 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x214 0x5e4 0x894 0x2 0x1 +#define MX6QDL_PAD_KEY_ROW3__KEY_ROW3 0x214 0x5e4 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x214 0x5e4 0x8a4 0x4 0x1 +#define MX6QDL_PAD_KEY_ROW3__GPIO4_IO13 0x214 0x5e4 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW3__SD1_VSELECT 0x214 0x5e4 0x000 0x6 0x0 +#define MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x218 0x5e8 0x000 0x0 0x0 +#define MX6QDL_PAD_KEY_COL4__IPU1_SISG4 0x218 0x5e8 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x218 0x5e8 0x944 0x2 0x1 +#define MX6QDL_PAD_KEY_COL4__KEY_COL4 0x218 0x5e8 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_COL4__UART5_RTS_B 0x218 0x5e8 0x93c 0x4 0x0 +#define MX6QDL_PAD_KEY_COL4__UART5_CTS_B 0x218 0x5e8 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_COL4__GPIO4_IO14 0x218 0x5e8 0x000 0x5 0x0 +#define MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x21c 0x5ec 0x7e8 0x0 0x0 +#define MX6QDL_PAD_KEY_ROW4__IPU1_SISG5 0x21c 0x5ec 0x000 0x1 0x0 +#define MX6QDL_PAD_KEY_ROW4__USB_OTG_PWR 0x21c 0x5ec 0x000 0x2 0x0 +#define MX6QDL_PAD_KEY_ROW4__KEY_ROW4 0x21c 0x5ec 0x000 0x3 0x0 +#define MX6QDL_PAD_KEY_ROW4__UART5_CTS_B 0x21c 0x5ec 0x000 0x4 0x0 +#define MX6QDL_PAD_KEY_ROW4__UART5_RTS_B 0x21c 0x5ec 0x93c 0x4 0x1 +#define MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x21c 0x5ec 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x220 0x5f0 0x000 0x0 0x0 +#define MX6QDL_PAD_GPIO_0__KEY_COL5 0x220 0x5f0 0x8e8 0x2 0x0 +#define MX6QDL_PAD_GPIO_0__ASRC_EXT_CLK 0x220 0x5f0 0x7b0 0x3 0x1 +#define MX6QDL_PAD_GPIO_0__EPIT1_OUT 0x220 0x5f0 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x220 0x5f0 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_0__USB_H1_PWR 0x220 0x5f0 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_0__SNVS_VIO_5 0x220 0x5f0 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_1__ESAI_RX_CLK 0x224 0x5f4 0x86c 0x0 0x1 +#define MX6QDL_PAD_GPIO_1__WDOG2_B 0x224 0x5f4 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_1__KEY_ROW5 0x224 0x5f4 0x8f4 0x2 0x0 +#define MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x224 0x5f4 0x004 0x3 0xff0d0101 +#define MX6QDL_PAD_GPIO_1__PWM2_OUT 0x224 0x5f4 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_1__GPIO1_IO01 0x224 0x5f4 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_1__SD1_CD_B 0x224 0x5f4 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_9__ESAI_RX_FS 0x228 0x5f8 0x85c 0x0 0x1 +#define MX6QDL_PAD_GPIO_9__WDOG1_B 0x228 0x5f8 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_9__KEY_COL6 0x228 0x5f8 0x8ec 0x2 0x0 +#define MX6QDL_PAD_GPIO_9__CCM_REF_EN_B 0x228 0x5f8 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_9__PWM1_OUT 0x228 0x5f8 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x228 0x5f8 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_9__SD1_WP 0x228 0x5f8 0x94c 0x6 0x1 +#define MX6QDL_PAD_GPIO_3__ESAI_RX_HF_CLK 0x22c 0x5fc 0x864 0x0 0x1 +#define MX6QDL_PAD_GPIO_3__I2C3_SCL 0x22c 0x5fc 0x8a8 0x2 0x1 +#define MX6QDL_PAD_GPIO_3__XTALOSC_REF_CLK_24M 0x22c 0x5fc 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_3__CCM_CLKO2 0x22c 0x5fc 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_3__GPIO1_IO03 0x22c 0x5fc 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_3__USB_H1_OC 0x22c 0x5fc 0x948 0x6 0x1 +#define MX6QDL_PAD_GPIO_3__MLB_CLK 0x22c 0x5fc 0x900 0x7 0x1 +#define MX6QDL_PAD_GPIO_6__ESAI_TX_CLK 0x230 0x600 0x870 0x0 0x1 +#define MX6QDL_PAD_GPIO_6__I2C3_SDA 0x230 0x600 0x8ac 0x2 0x1 +#define MX6QDL_PAD_GPIO_6__GPIO1_IO06 0x230 0x600 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_6__SD2_LCTL 0x230 0x600 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_6__MLB_SIG 0x230 0x600 0x908 0x7 0x1 +#define MX6QDL_PAD_GPIO_2__ESAI_TX_FS 0x234 0x604 0x860 0x0 0x1 +#define MX6QDL_PAD_GPIO_2__KEY_ROW6 0x234 0x604 0x8f8 0x2 0x1 +#define MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x234 0x604 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_2__SD2_WP 0x234 0x604 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_2__MLB_DATA 0x234 0x604 0x904 0x7 0x1 +#define MX6QDL_PAD_GPIO_4__ESAI_TX_HF_CLK 0x238 0x608 0x868 0x0 0x1 +#define MX6QDL_PAD_GPIO_4__KEY_COL7 0x238 0x608 0x8f0 0x2 0x1 +#define MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x238 0x608 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_4__SD2_CD_B 0x238 0x608 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_5__ESAI_TX2_RX3 0x23c 0x60c 0x87c 0x0 0x1 +#define MX6QDL_PAD_GPIO_5__KEY_ROW7 0x23c 0x60c 0x8fc 0x2 0x1 +#define MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x23c 0x60c 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x23c 0x60c 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_5__I2C3_SCL 0x23c 0x60c 0x8a8 0x6 0x2 +#define MX6QDL_PAD_GPIO_5__ARM_EVENTI 0x23c 0x60c 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_7__ESAI_TX4_RX1 0x240 0x610 0x884 0x0 0x1 +#define MX6QDL_PAD_GPIO_7__ECSPI5_RDY 0x240 0x610 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_7__EPIT1_OUT 0x240 0x610 0x000 0x2 0x0 +#define MX6QDL_PAD_GPIO_7__FLEXCAN1_TX 0x240 0x610 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x240 0x610 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_7__UART2_RX_DATA 0x240 0x610 0x928 0x4 0x2 +#define MX6QDL_PAD_GPIO_7__GPIO1_IO07 0x240 0x610 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_7__SPDIF_LOCK 0x240 0x610 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_7__USB_OTG_HOST_MODE 0x240 0x610 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_8__ESAI_TX5_RX0 0x244 0x614 0x888 0x0 0x1 +#define MX6QDL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x244 0x614 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_8__EPIT2_OUT 0x244 0x614 0x000 0x2 0x0 +#define MX6QDL_PAD_GPIO_8__FLEXCAN1_RX 0x244 0x614 0x7e4 0x3 0x1 +#define MX6QDL_PAD_GPIO_8__UART2_RX_DATA 0x244 0x614 0x928 0x4 0x3 +#define MX6QDL_PAD_GPIO_8__UART2_TX_DATA 0x244 0x614 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_8__GPIO1_IO08 0x244 0x614 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_8__SPDIF_SR_CLK 0x244 0x614 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_8__USB_OTG_PWR_CTL_WAKE 0x244 0x614 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_16__ESAI_TX3_RX2 0x248 0x618 0x880 0x0 0x1 +#define MX6QDL_PAD_GPIO_16__ENET_1588_EVENT2_IN 0x248 0x618 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x248 0x618 0x83c 0x2 0x1 +#define MX6QDL_PAD_GPIO_16__SD1_LCTL 0x248 0x618 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_16__SPDIF_IN 0x248 0x618 0x914 0x4 0x3 +#define MX6QDL_PAD_GPIO_16__GPIO7_IO11 0x248 0x618 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_16__I2C3_SDA 0x248 0x618 0x8ac 0x6 0x2 +#define MX6QDL_PAD_GPIO_16__JTAG_DE_B 0x248 0x618 0x000 0x7 0x0 +#define MX6QDL_PAD_GPIO_17__ESAI_TX0 0x24c 0x61c 0x874 0x0 0x0 +#define MX6QDL_PAD_GPIO_17__ENET_1588_EVENT3_IN 0x24c 0x61c 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_17__CCM_PMIC_READY 0x24c 0x61c 0x7f0 0x2 0x1 +#define MX6QDL_PAD_GPIO_17__SDMA_EXT_EVENT0 0x24c 0x61c 0x90c 0x3 0x1 +#define MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x24c 0x61c 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x24c 0x61c 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_18__ESAI_TX1 0x250 0x620 0x878 0x0 0x0 +#define MX6QDL_PAD_GPIO_18__ENET_RX_CLK 0x250 0x620 0x844 0x1 0x1 +#define MX6QDL_PAD_GPIO_18__SD3_VSELECT 0x250 0x620 0x000 0x2 0x0 +#define MX6QDL_PAD_GPIO_18__SDMA_EXT_EVENT1 0x250 0x620 0x910 0x3 0x1 +#define MX6QDL_PAD_GPIO_18__ASRC_EXT_CLK 0x250 0x620 0x7b0 0x4 0x2 +#define MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x250 0x620 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_18__SNVS_VIO_5_CTL 0x250 0x620 0x000 0x6 0x0 +#define MX6QDL_PAD_GPIO_19__KEY_COL5 0x254 0x624 0x8e8 0x0 0x1 +#define MX6QDL_PAD_GPIO_19__ENET_1588_EVENT0_OUT 0x254 0x624 0x000 0x1 0x0 +#define MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x254 0x624 0x000 0x2 0x0 +#define MX6QDL_PAD_GPIO_19__CCM_CLKO1 0x254 0x624 0x000 0x3 0x0 +#define MX6QDL_PAD_GPIO_19__ECSPI1_RDY 0x254 0x624 0x000 0x4 0x0 +#define MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x254 0x624 0x000 0x5 0x0 +#define MX6QDL_PAD_GPIO_19__ENET_TX_ER 0x254 0x624 0x000 0x6 0x0 +#define MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x258 0x628 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18 0x258 0x628 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_PIXCLK__ARM_EVENTO 0x258 0x628 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x25c 0x62c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_MCLK__CCM_CLKO1 0x25c 0x62c 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_MCLK__GPIO5_IO19 0x25c 0x62c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_MCLK__ARM_TRACE_CTL 0x25c 0x62c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x260 0x630 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DATA_EN__EIM_DATA00 0x260 0x630 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x260 0x630 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DATA_EN__ARM_TRACE_CLK 0x260 0x630 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x264 0x634 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_VSYNC__EIM_DATA01 0x264 0x634 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_VSYNC__GPIO5_IO21 0x264 0x634 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_VSYNC__ARM_TRACE00 0x264 0x634 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04 0x268 0x638 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT4__EIM_DATA02 0x268 0x638 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT4__ECSPI1_SCLK 0x268 0x638 0x7f4 0x2 0x3 +#define MX6QDL_PAD_CSI0_DAT4__KEY_COL5 0x268 0x638 0x8e8 0x3 0x2 +#define MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x268 0x638 0x000 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT4__GPIO5_IO22 0x268 0x638 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT4__ARM_TRACE01 0x268 0x638 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05 0x26c 0x63c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT5__EIM_DATA03 0x26c 0x63c 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT5__ECSPI1_MOSI 0x26c 0x63c 0x7fc 0x2 0x3 +#define MX6QDL_PAD_CSI0_DAT5__KEY_ROW5 0x26c 0x63c 0x8f4 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x26c 0x63c 0x000 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT5__GPIO5_IO23 0x26c 0x63c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT5__ARM_TRACE02 0x26c 0x63c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06 0x270 0x640 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT6__EIM_DATA04 0x270 0x640 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT6__ECSPI1_MISO 0x270 0x640 0x7f8 0x2 0x3 +#define MX6QDL_PAD_CSI0_DAT6__KEY_COL6 0x270 0x640 0x8ec 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x270 0x640 0x000 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT6__GPIO5_IO24 0x270 0x640 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT6__ARM_TRACE03 0x270 0x640 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07 0x274 0x644 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT7__EIM_DATA05 0x274 0x644 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT7__ECSPI1_SS0 0x274 0x644 0x800 0x2 0x3 +#define MX6QDL_PAD_CSI0_DAT7__KEY_ROW6 0x274 0x644 0x8f8 0x3 0x2 +#define MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x274 0x644 0x000 0x4 0x0 +#define MX6QDL_PAD_CSI0_DAT7__GPIO5_IO25 0x274 0x644 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT7__ARM_TRACE04 0x274 0x644 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08 0x278 0x648 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT8__EIM_DATA06 0x278 0x648 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT8__ECSPI2_SCLK 0x278 0x648 0x810 0x2 0x2 +#define MX6QDL_PAD_CSI0_DAT8__KEY_COL7 0x278 0x648 0x8f0 0x3 0x2 +#define MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x278 0x648 0x89c 0x4 0x1 +#define MX6QDL_PAD_CSI0_DAT8__GPIO5_IO26 0x278 0x648 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT8__ARM_TRACE05 0x278 0x648 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09 0x27c 0x64c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT9__EIM_DATA07 0x27c 0x64c 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT9__ECSPI2_MOSI 0x27c 0x64c 0x818 0x2 0x2 +#define MX6QDL_PAD_CSI0_DAT9__KEY_ROW7 0x27c 0x64c 0x8fc 0x3 0x2 +#define MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x27c 0x64c 0x898 0x4 0x1 +#define MX6QDL_PAD_CSI0_DAT9__GPIO5_IO27 0x27c 0x64c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT9__ARM_TRACE06 0x27c 0x64c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10 0x280 0x650 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT10__AUD3_RXC 0x280 0x650 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT10__ECSPI2_MISO 0x280 0x650 0x814 0x2 0x2 +#define MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x280 0x650 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT10__UART1_RX_DATA 0x280 0x650 0x920 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT10__GPIO5_IO28 0x280 0x650 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT10__ARM_TRACE07 0x280 0x650 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11 0x284 0x654 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT11__AUD3_RXFS 0x284 0x654 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT11__ECSPI2_SS0 0x284 0x654 0x81c 0x2 0x2 +#define MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x284 0x654 0x920 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT11__UART1_TX_DATA 0x284 0x654 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT11__GPIO5_IO29 0x284 0x654 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT11__ARM_TRACE08 0x284 0x654 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x288 0x658 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT12__EIM_DATA08 0x288 0x658 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x288 0x658 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT12__UART4_RX_DATA 0x288 0x658 0x938 0x3 0x2 +#define MX6QDL_PAD_CSI0_DAT12__GPIO5_IO30 0x288 0x658 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT12__ARM_TRACE09 0x288 0x658 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x28c 0x65c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT13__EIM_DATA09 0x28c 0x65c 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x28c 0x65c 0x938 0x3 0x3 +#define MX6QDL_PAD_CSI0_DAT13__UART4_TX_DATA 0x28c 0x65c 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT13__GPIO5_IO31 0x28c 0x65c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT13__ARM_TRACE10 0x28c 0x65c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x290 0x660 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT14__EIM_DATA10 0x290 0x660 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT14__UART5_TX_DATA 0x290 0x660 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT14__UART5_RX_DATA 0x290 0x660 0x940 0x3 0x2 +#define MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x290 0x660 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT14__ARM_TRACE11 0x290 0x660 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x294 0x664 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT15__EIM_DATA11 0x294 0x664 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT15__UART5_RX_DATA 0x294 0x664 0x940 0x3 0x3 +#define MX6QDL_PAD_CSI0_DAT15__UART5_TX_DATA 0x294 0x664 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x294 0x664 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT15__ARM_TRACE12 0x294 0x664 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x298 0x668 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT16__EIM_DATA12 0x298 0x668 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x298 0x668 0x934 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT16__UART4_CTS_B 0x298 0x668 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT16__GPIO6_IO02 0x298 0x668 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT16__ARM_TRACE13 0x298 0x668 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x29c 0x66c 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT17__EIM_DATA13 0x29c 0x66c 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x29c 0x66c 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT17__UART4_RTS_B 0x29c 0x66c 0x934 0x3 0x1 +#define MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03 0x29c 0x66c 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT17__ARM_TRACE14 0x29c 0x66c 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x2a0 0x670 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT18__EIM_DATA14 0x2a0 0x670 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT18__UART5_RTS_B 0x2a0 0x670 0x93c 0x3 0x2 +#define MX6QDL_PAD_CSI0_DAT18__UART5_CTS_B 0x2a0 0x670 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x2a0 0x670 0x000 0x5 0x0 +#define MX6QDL_PAD_CSI0_DAT18__ARM_TRACE15 0x2a0 0x670 0x000 0x7 0x0 +#define MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x2a4 0x674 0x000 0x0 0x0 +#define MX6QDL_PAD_CSI0_DAT19__EIM_DATA15 0x2a4 0x674 0x000 0x1 0x0 +#define MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x2a4 0x674 0x000 0x3 0x0 +#define MX6QDL_PAD_CSI0_DAT19__UART5_RTS_B 0x2a4 0x674 0x93c 0x3 0x3 +#define MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x2a4 0x674 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x2a8 0x690 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT7__UART1_TX_DATA 0x2a8 0x690 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT7__UART1_RX_DATA 0x2a8 0x690 0x920 0x1 0x2 +#define MX6QDL_PAD_SD3_DAT7__GPIO6_IO17 0x2a8 0x690 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x2ac 0x694 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA 0x2ac 0x694 0x920 0x1 0x3 +#define MX6QDL_PAD_SD3_DAT6__UART1_TX_DATA 0x2ac 0x694 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT6__GPIO6_IO18 0x2ac 0x694 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x2b0 0x698 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT5__UART2_TX_DATA 0x2b0 0x698 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT5__UART2_RX_DATA 0x2b0 0x698 0x928 0x1 0x4 +#define MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x2b0 0x698 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x2b4 0x69c 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT4__UART2_RX_DATA 0x2b4 0x69c 0x928 0x1 0x5 +#define MX6QDL_PAD_SD3_DAT4__UART2_TX_DATA 0x2b4 0x69c 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x2b4 0x69c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_CMD__SD3_CMD 0x2b8 0x6a0 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_CMD__UART2_CTS_B 0x2b8 0x6a0 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_CMD__UART2_RTS_B 0x2b8 0x6a0 0x924 0x1 0x2 +#define MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x2b8 0x6a0 0x000 0x2 0x0 +#define MX6QDL_PAD_SD3_CMD__GPIO7_IO02 0x2b8 0x6a0 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_CLK__SD3_CLK 0x2bc 0x6a4 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_CLK__UART2_RTS_B 0x2bc 0x6a4 0x924 0x1 0x3 +#define MX6QDL_PAD_SD3_CLK__UART2_CTS_B 0x2bc 0x6a4 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x2bc 0x6a4 0x7e4 0x2 0x2 +#define MX6QDL_PAD_SD3_CLK__GPIO7_IO03 0x2bc 0x6a4 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x2c0 0x6a8 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT0__UART1_CTS_B 0x2c0 0x6a8 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT0__UART1_RTS_B 0x2c0 0x6a8 0x91c 0x1 0x2 +#define MX6QDL_PAD_SD3_DAT0__FLEXCAN2_TX 0x2c0 0x6a8 0x000 0x2 0x0 +#define MX6QDL_PAD_SD3_DAT0__GPIO7_IO04 0x2c0 0x6a8 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x2c4 0x6ac 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT1__UART1_RTS_B 0x2c4 0x6ac 0x91c 0x1 0x3 +#define MX6QDL_PAD_SD3_DAT1__UART1_CTS_B 0x2c4 0x6ac 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT1__FLEXCAN2_RX 0x2c4 0x6ac 0x7e8 0x2 0x1 +#define MX6QDL_PAD_SD3_DAT1__GPIO7_IO05 0x2c4 0x6ac 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x2c8 0x6b0 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT2__GPIO7_IO06 0x2c8 0x6b0 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x2cc 0x6b4 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_DAT3__UART3_CTS_B 0x2cc 0x6b4 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_DAT3__UART3_RTS_B 0x2cc 0x6b4 0x92c 0x1 0x4 +#define MX6QDL_PAD_SD3_DAT3__GPIO7_IO07 0x2cc 0x6b4 0x000 0x5 0x0 +#define MX6QDL_PAD_SD3_RST__SD3_RESET 0x2d0 0x6b8 0x000 0x0 0x0 +#define MX6QDL_PAD_SD3_RST__UART3_RTS_B 0x2d0 0x6b8 0x92c 0x1 0x5 +#define MX6QDL_PAD_SD3_RST__UART3_CTS_B 0x2d0 0x6b8 0x000 0x1 0x0 +#define MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x2d0 0x6b8 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CLE__NAND_CLE 0x2d4 0x6bc 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CLE__IPU2_SISG4 0x2d4 0x6bc 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_CLE__GPIO6_IO07 0x2d4 0x6bc 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_ALE__NAND_ALE 0x2d8 0x6c0 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_ALE__SD4_RESET 0x2d8 0x6c0 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_ALE__GPIO6_IO08 0x2d8 0x6c0 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0x2dc 0x6c4 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_WP_B__IPU2_SISG5 0x2dc 0x6c4 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_WP_B__GPIO6_IO09 0x2dc 0x6c4 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0x2e0 0x6c8 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_RB0__IPU2_DI0_PIN01 0x2e0 0x6c8 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_RB0__GPIO6_IO10 0x2e0 0x6c8 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0x2e4 0x6cc 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x2e4 0x6cc 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0x2e8 0x6d0 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CS1__SD4_VSELECT 0x2e8 0x6d0 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_CS1__SD3_VSELECT 0x2e8 0x6d0 0x000 0x2 0x0 +#define MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x2e8 0x6d0 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS2__NAND_CE2_B 0x2ec 0x6d4 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CS2__IPU1_SISG0 0x2ec 0x6d4 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_CS2__ESAI_TX0 0x2ec 0x6d4 0x874 0x2 0x1 +#define MX6QDL_PAD_NANDF_CS2__EIM_CRE 0x2ec 0x6d4 0x000 0x3 0x0 +#define MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x2ec 0x6d4 0x000 0x4 0x0 +#define MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x2ec 0x6d4 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS2__IPU2_SISG0 0x2ec 0x6d4 0x000 0x6 0x0 +#define MX6QDL_PAD_NANDF_CS3__NAND_CE3_B 0x2f0 0x6d8 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_CS3__IPU1_SISG1 0x2f0 0x6d8 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x2f0 0x6d8 0x878 0x2 0x1 +#define MX6QDL_PAD_NANDF_CS3__EIM_ADDR26 0x2f0 0x6d8 0x000 0x3 0x0 +#define MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x2f0 0x6d8 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_CS3__IPU2_SISG1 0x2f0 0x6d8 0x000 0x6 0x0 +#define MX6QDL_PAD_SD4_CMD__SD4_CMD 0x2f4 0x6dc 0x000 0x0 0x0 +#define MX6QDL_PAD_SD4_CMD__NAND_RE_B 0x2f4 0x6dc 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_CMD__UART3_TX_DATA 0x2f4 0x6dc 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_CMD__UART3_RX_DATA 0x2f4 0x6dc 0x930 0x2 0x2 +#define MX6QDL_PAD_SD4_CMD__GPIO7_IO09 0x2f4 0x6dc 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_CLK__SD4_CLK 0x2f8 0x6e0 0x000 0x0 0x0 +#define MX6QDL_PAD_SD4_CLK__NAND_WE_B 0x2f8 0x6e0 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_CLK__UART3_RX_DATA 0x2f8 0x6e0 0x930 0x2 0x3 +#define MX6QDL_PAD_SD4_CLK__UART3_TX_DATA 0x2f8 0x6e0 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_CLK__GPIO7_IO10 0x2f8 0x6e0 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D0__NAND_DATA00 0x2fc 0x6e4 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D0__SD1_DATA4 0x2fc 0x6e4 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x2fc 0x6e4 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D1__NAND_DATA01 0x300 0x6e8 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D1__SD1_DATA5 0x300 0x6e8 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x300 0x6e8 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D2__NAND_DATA02 0x304 0x6ec 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D2__SD1_DATA6 0x304 0x6ec 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x304 0x6ec 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D3__NAND_DATA03 0x308 0x6f0 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D3__SD1_DATA7 0x308 0x6f0 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x308 0x6f0 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D4__NAND_DATA04 0x30c 0x6f4 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x30c 0x6f4 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x30c 0x6f4 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D5__NAND_DATA05 0x310 0x6f8 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x310 0x6f8 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x310 0x6f8 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D6__NAND_DATA06 0x314 0x6fc 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x314 0x6fc 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x314 0x6fc 0x000 0x5 0x0 +#define MX6QDL_PAD_NANDF_D7__NAND_DATA07 0x318 0x700 0x000 0x0 0x0 +#define MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x318 0x700 0x000 0x1 0x0 +#define MX6QDL_PAD_NANDF_D7__GPIO2_IO07 0x318 0x700 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x31c 0x704 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x31c 0x704 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT0__GPIO2_IO08 0x31c 0x704 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x320 0x708 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x320 0x708 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT1__GPIO2_IO09 0x320 0x708 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x324 0x70c 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT2__PWM4_OUT 0x324 0x70c 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT2__GPIO2_IO10 0x324 0x70c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x328 0x710 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT3__GPIO2_IO11 0x328 0x710 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x32c 0x714 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA 0x32c 0x714 0x928 0x2 0x6 +#define MX6QDL_PAD_SD4_DAT4__UART2_TX_DATA 0x32c 0x714 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT4__GPIO2_IO12 0x32c 0x714 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x330 0x718 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT5__UART2_RTS_B 0x330 0x718 0x924 0x2 0x4 +#define MX6QDL_PAD_SD4_DAT5__UART2_CTS_B 0x330 0x718 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT5__GPIO2_IO13 0x330 0x718 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x334 0x71c 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT6__UART2_CTS_B 0x334 0x71c 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT6__UART2_RTS_B 0x334 0x71c 0x924 0x2 0x5 +#define MX6QDL_PAD_SD4_DAT6__GPIO2_IO14 0x334 0x71c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x338 0x720 0x000 0x1 0x0 +#define MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA 0x338 0x720 0x000 0x2 0x0 +#define MX6QDL_PAD_SD4_DAT7__UART2_RX_DATA 0x338 0x720 0x928 0x2 0x7 +#define MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x338 0x720 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x33c 0x724 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_DAT1__ECSPI5_SS0 0x33c 0x724 0x834 0x1 0x1 +#define MX6QDL_PAD_SD1_DAT1__PWM3_OUT 0x33c 0x724 0x000 0x2 0x0 +#define MX6QDL_PAD_SD1_DAT1__GPT_CAPTURE2 0x33c 0x724 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_DAT1__GPIO1_IO17 0x33c 0x724 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x340 0x728 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_DAT0__ECSPI5_MISO 0x340 0x728 0x82c 0x1 0x1 +#define MX6QDL_PAD_SD1_DAT0__GPT_CAPTURE1 0x340 0x728 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x340 0x728 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x344 0x72c 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_DAT3__ECSPI5_SS2 0x344 0x72c 0x000 0x1 0x0 +#define MX6QDL_PAD_SD1_DAT3__GPT_COMPARE3 0x344 0x72c 0x000 0x2 0x0 +#define MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x344 0x72c 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_DAT3__WDOG2_B 0x344 0x72c 0x000 0x4 0x0 +#define MX6QDL_PAD_SD1_DAT3__GPIO1_IO21 0x344 0x72c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT3__WDOG2_RESET_B_DEB 0x344 0x72c 0x000 0x6 0x0 +#define MX6QDL_PAD_SD1_CMD__SD1_CMD 0x348 0x730 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_CMD__ECSPI5_MOSI 0x348 0x730 0x830 0x1 0x0 +#define MX6QDL_PAD_SD1_CMD__PWM4_OUT 0x348 0x730 0x000 0x2 0x0 +#define MX6QDL_PAD_SD1_CMD__GPT_COMPARE1 0x348 0x730 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_CMD__GPIO1_IO18 0x348 0x730 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x34c 0x734 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_DAT2__ECSPI5_SS1 0x34c 0x734 0x838 0x1 0x1 +#define MX6QDL_PAD_SD1_DAT2__GPT_COMPARE2 0x34c 0x734 0x000 0x2 0x0 +#define MX6QDL_PAD_SD1_DAT2__PWM2_OUT 0x34c 0x734 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_DAT2__WDOG1_B 0x34c 0x734 0x000 0x4 0x0 +#define MX6QDL_PAD_SD1_DAT2__GPIO1_IO19 0x34c 0x734 0x000 0x5 0x0 +#define MX6QDL_PAD_SD1_DAT2__WDOG1_RESET_B_DEB 0x34c 0x734 0x000 0x6 0x0 +#define MX6QDL_PAD_SD1_CLK__SD1_CLK 0x350 0x738 0x000 0x0 0x0 +#define MX6QDL_PAD_SD1_CLK__ECSPI5_SCLK 0x350 0x738 0x828 0x1 0x0 +#define MX6QDL_PAD_SD1_CLK__GPT_CLKIN 0x350 0x738 0x000 0x3 0x0 +#define MX6QDL_PAD_SD1_CLK__GPIO1_IO20 0x350 0x738 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_CLK__SD2_CLK 0x354 0x73c 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_CLK__ECSPI5_SCLK 0x354 0x73c 0x828 0x1 0x1 +#define MX6QDL_PAD_SD2_CLK__KEY_COL5 0x354 0x73c 0x8e8 0x2 0x3 +#define MX6QDL_PAD_SD2_CLK__AUD4_RXFS 0x354 0x73c 0x7c0 0x3 0x1 +#define MX6QDL_PAD_SD2_CLK__GPIO1_IO10 0x354 0x73c 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_CMD__SD2_CMD 0x358 0x740 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_CMD__ECSPI5_MOSI 0x358 0x740 0x830 0x1 0x1 +#define MX6QDL_PAD_SD2_CMD__KEY_ROW5 0x358 0x740 0x8f4 0x2 0x2 +#define MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x358 0x740 0x7bc 0x3 0x1 +#define MX6QDL_PAD_SD2_CMD__GPIO1_IO11 0x358 0x740 0x000 0x5 0x0 +#define MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x35c 0x744 0x000 0x0 0x0 +#define MX6QDL_PAD_SD2_DAT3__ECSPI5_SS3 0x35c 0x744 0x000 0x1 0x0 +#define MX6QDL_PAD_SD2_DAT3__KEY_COL6 0x35c 0x744 0x8ec 0x2 0x2 +#define MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x35c 0x744 0x7c4 0x3 0x1 +#define MX6QDL_PAD_SD2_DAT3__GPIO1_IO12 0x35c 0x744 0x000 0x5 0x0 #endif /* __DTS_IMX6Q_PINFUNC_H */ diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-sabreauto-ecspi.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabreauto-ecspi.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-sabreauto-ecspi.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabreauto-ecspi.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6q-sabreauto.dts" + +&ecspi1 { + status = "okay"; +}; + +&flexcan2 { + /* max7310_c on i2c3 is gone */ + status = "disabled"; +}; + +&i2c3 { + /* pin conflict with ecspi1 */ + status = "disabled"; +}; + +&uart3 { + /* the uart3 depends on the i2c3, so disable it too. */ + status = "disabled"; +}; + +&usbh1 { + /* max7310_b on i2c3 is gone */ + status = "disabled"; +}; + +&usbotg { + /* max7310_c on i2c3 is gone */ + status = "okay"; + dr_mode = "peripheral"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-sabreauto-flexcan1.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabreauto-flexcan1.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-sabreauto-flexcan1.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabreauto-flexcan1.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6q-sabreauto.dts" + +&flexcan1{ + status = "okay"; +}; + +&fec { + /* pin conflict with flexcan1 */ + status = "disabled"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-sabreauto-gpmi-weim.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabreauto-gpmi-weim.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-sabreauto-gpmi-weim.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabreauto-gpmi-weim.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6q-sabreauto.dts" + +&ecspi1 { + /* pin conflict with weim */ + status = "disabled"; +}; + +&flexcan2 { + /* max7310_c on i2c3 is gone */ + status = "disabled"; +}; + +&gpmi { + status = "okay"; +}; + +&i2c3 { + /* pin conflict with weim */ + status = "disabled"; +}; + +&uart3 { + /* pin conflict with gpmi and weim */ + status = "disabled"; +}; + +&usbh1 { + /* max7310_b on i2c3 is gone */ + status = "disabled"; +}; + +&usbotg { + /* max7310_c on i2c3 is gone */ + status = "okay"; + dr_mode = "peripheral"; +}; + +&weim { + status = "okay"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-sabreauto.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabreauto.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-sabreauto.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabreauto.dts 2014-03-08 20:32:53.000000000 +0100 @@ -20,16 +20,22 @@ compatible = "fsl,imx6q-sabreauto", "fsl,imx6q"; }; -&iomuxc { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + status = "okay"; +}; + +&mxcfb3 { + status = "okay"; +}; + +&mxcfb4 { + status = "okay"; +}; - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - MX6Q_PAD_NANDF_CS2__GPIO6_IO15 0x80000000 - MX6Q_PAD_SD2_DAT2__GPIO1_IO13 0x80000000 - >; - }; - }; +&sata { + status = "okay"; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-sabrelite.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabrelite.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-sabrelite.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabrelite.dts 2014-03-08 20:32:53.000000000 +0100 @@ -65,6 +65,12 @@ }; }; +&audmux { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux_1>; +}; + &ecspi1 { fsl,spi-num-chipselects = <1>; cs-gpios = <&gpio3 19 0>; @@ -79,9 +85,27 @@ }; }; -&ssi1 { - fsl,mode = "i2s-slave"; +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet_1>; + phy-mode = "rgmii"; + phy-reset-gpios = <&gpio3 23 0>; + status = "okay"; +}; + +&i2c1 { status = "okay"; + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1_1>; + + codec: sgtl5000@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + clocks = <&clks 201>; + VDDA-supply = <®_2p5v>; + VDDIO-supply = <®_3p3v>; + }; }; &iomuxc { @@ -91,36 +115,69 @@ hog { pinctrl_hog: hoggrp { fsl,pins = < - MX6Q_PAD_NANDF_D6__GPIO2_IO06 0x80000000 - MX6Q_PAD_NANDF_D7__GPIO2_IO07 0x80000000 - MX6Q_PAD_EIM_D19__GPIO3_IO19 0x80000000 - MX6Q_PAD_EIM_D22__GPIO3_IO22 0x80000000 - MX6Q_PAD_EIM_D23__GPIO3_IO23 0x80000000 - MX6Q_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 - MX6Q_PAD_SD3_DAT4__GPIO7_IO01 0x1f0b0 - MX6Q_PAD_GPIO_0__CCM_CLKO1 0x80000000 + MX6QDL_PAD_NANDF_D6__GPIO2_IO06 0x80000000 + MX6QDL_PAD_NANDF_D7__GPIO2_IO07 0x80000000 + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000 + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 + MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 + MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 + MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x1f0b0 + MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x80000000 + MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 >; }; }; }; -&usbotg { - vbus-supply = <®_usb_otg_vbus>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbotg_1>; - disable-over-current; +&ldb { + status = "okay"; + + lvds-channel@0 { + fsl,data-mapping = "spwg"; + fsl,data-width = <18>; + status = "okay"; + + display-timings { + native-mode = <&timing0>; + timing0: hsd100pxn1 { + clock-frequency = <65000000>; + hactive = <1024>; + vactive = <768>; + hback-porch = <220>; + hfront-porch = <40>; + vback-porch = <21>; + vfront-porch = <7>; + hsync-len = <60>; + vsync-len = <10>; + }; + }; + }; +}; + +&sata { status = "okay"; }; +&ssi1 { + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_1>; +}; + &usbh1 { status = "okay"; }; -&fec { +&usbotg { + vbus-supply = <®_usb_otg_vbus>; pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet_1>; - phy-mode = "rgmii"; - phy-reset-gpios = <&gpio3 23 0>; + pinctrl-0 = <&pinctrl_usbotg_1>; + disable-over-current; status = "okay"; }; @@ -141,30 +198,3 @@ vmmc-supply = <®_3p3v>; status = "okay"; }; - -&audmux { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_audmux_1>; -}; - -&uart2 { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2_1>; -}; - -&i2c1 { - status = "okay"; - clock-frequency = <100000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c1_1>; - - codec: sgtl5000@0a { - compatible = "fsl,sgtl5000"; - reg = <0x0a>; - clocks = <&clks 169>; - VDDA-supply = <®_2p5v>; - VDDIO-supply = <®_3p3v>; - }; -}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-sabresd-hdcp.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabresd-hdcp.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-sabresd-hdcp.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabresd-hdcp.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,23 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include "imx6q-sabresd.dts" + +&hdmi_video { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; + fsl,hdcp; +}; + +&i2c2 { + status = "disable"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-sabresd-ldo.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabresd-ldo.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-sabresd-ldo.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabresd-ldo.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,34 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + + +#include "imx6q-sabresd.dts" + +&cpu0 { + arm-supply = <®_arm>; + soc-supply = <®_soc>; + pu-supply = <®_pu>; /* use pu_dummy if VDDSOC share with VDDPU */ +}; + +&gpc { + fsl,ldo-bypass = <0>; /* use ldo-bypass, u-boot will check it and configure */ + fsl,wdog-reset = <1>; /* watchdog select of reset source */ + pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; + +&gpu { + pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; + +&vpu { + pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q-sabresd.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabresd.dts --- linux-3.10.30/arch/arm/boot/dts/imx6q-sabresd.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q-sabresd.dts 2014-03-08 20:32:53.000000000 +0100 @@ -20,20 +20,32 @@ compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; }; -&iomuxc { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - MX6Q_PAD_GPIO_4__GPIO1_IO04 0x80000000 - MX6Q_PAD_GPIO_5__GPIO1_IO05 0x80000000 - MX6Q_PAD_NANDF_D0__GPIO2_IO00 0x80000000 - MX6Q_PAD_NANDF_D1__GPIO2_IO01 0x80000000 - MX6Q_PAD_NANDF_D2__GPIO2_IO02 0x80000000 - MX6Q_PAD_NANDF_D3__GPIO2_IO03 0x80000000 - >; - }; - }; +&imx_drm { + crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; +}; + +&sata { + status = "okay"; +}; + +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + status = "okay"; +}; + +&mxcfb3 { + status = "okay"; +}; + +&mxcfb4 { + status = "okay"; +}; + +&battery { + offset-charger = <1900>; + offset-discharger = <1694>; + offset-usb-charger = <1685>; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6q.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6q.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6q.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -8,15 +8,19 @@ * */ -#include "imx6qdl.dtsi" #include "imx6q-pinfunc.h" +#include "imx6qdl.dtsi" / { + aliases { + ipu1 = &ipu2; + }; + cpus { #address-cells = <1>; #size-cells = <0>; - cpu@0 { + cpu0: cpu@0 { compatible = "arm,cortex-a9"; device_type = "cpu"; reg = <0>; @@ -25,8 +29,17 @@ /* kHz uV */ 1200000 1275000 996000 1250000 + 852000 1250000 792000 1150000 - 396000 950000 + 396000 975000 + >; + fsl,soc-operating-points = < + /* ARM kHz SOC-PU uV */ + 1200000 1275000 + 996000 1250000 + 852000 1250000 + 792000 1175000 + 396000 1175000 >; clock-latency = <61036>; /* two CLK32 periods */ clocks = <&clks 104>, <&clks 6>, <&clks 16>, @@ -61,6 +74,77 @@ }; soc { + + busfreq { /* BUSFREQ */ + compatible = "fsl,imx6_busfreq"; + clocks = <&clks 171>, <&clks 6>, <&clks 11>, <&clks 104>, <&clks 172>, <&clks 58>, + <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>; + clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", + "periph_pre", "periph_clk2", "periph_clk2_sel", "osc"; + interrupts = <0 107 0x04>, <0 112 0x4>, <0 113 0x4>, <0 114 0x4>; + interrupt-names = "irq_busfreq_0", "irq_busfreq_1", "irq_busfreq_2", "irq_busfreq_3"; + fsl,max_ddr_freq = <528000000>; + }; + + gpu: gpu@00130000 { + compatible = "fsl,imx6q-gpu"; + reg = <0x00130000 0x4000>, <0x00134000 0x4000>, + <0x02204000 0x4000>, <0x0 0x0>; + reg-names = "iobase_3d", "iobase_2d", + "iobase_vg", "phys_baseaddr"; + interrupts = <0 9 0x04>, <0 10 0x04>,<0 11 0x04>; + interrupt-names = "irq_3d", "irq_2d", "irq_vg"; + clocks = <&clks 26>, <&clks 143>, + <&clks 27>, <&clks 121>, + <&clks 122>, <&clks 74>; + clock-names = "gpu2d_axi_clk", "openvg_axi_clk", + "gpu3d_axi_clk", "gpu2d_clk", + "gpu3d_clk", "gpu3d_shader_clk"; + resets = <&src 0>, <&src 3>, <&src 3>; + reset-names = "gpu3d", "gpu2d", "gpuvg"; + pu-supply = <®_pu>; + }; + + ocram: sram@00900000 { + compatible = "mmio-sram"; + reg = <0x00900000 0x40000>; + clocks = <&clks 142>; + }; + + hdmi_core: hdmi_core@00120000 { + compatible = "fsl,imx6q-hdmi-core"; + reg = <0x00120000 0x9000>; + clocks = <&clks 124>, <&clks 123>; + clock-names = "hdmi_isfr", "hdmi_iahb"; + status = "disabled"; + }; + + hdmi_video: hdmi_video@020e0000 { + compatible = "fsl,imx6q-hdmi-video"; + reg = <0x020e0000 0x1000>; + reg-names = "hdmi_gpr"; + interrupts = <0 115 0x04>; + clocks = <&clks 124>, <&clks 123>; + clock-names = "hdmi_isfr", "hdmi_iahb"; + status = "disabled"; + }; + + hdmi_audio: hdmi_audio@00120000 { + compatible = "fsl,imx6q-hdmi-audio"; + clocks = <&clks 124>, <&clks 123>; + clock-names = "hdmi_isfr", "hdmi_iahb"; + dmas = <&sdma 2 22 0>; + dma-names = "tx"; + status = "disabled"; + }; + + hdmi_cec: hdmi_cec@00120000 { + compatible = "fsl,imx6q-hdmi-cec"; + interrupts = <0 115 0x04>; + status = "disabled"; + }; + + aips-bus@02000000 { /* AIPS1 */ spba-bus@02000000 { ecspi5: ecspi@02018000 { @@ -75,288 +159,91 @@ }; }; + vpu@02040000 { + status = "okay"; + }; + iomuxc: iomuxc@020e0000 { compatible = "fsl,imx6q-iomuxc"; - reg = <0x020e0000 0x4000>; - - /* shared pinctrl settings */ - audmux { - pinctrl_audmux_1: audmux-1 { - fsl,pins = < - MX6Q_PAD_SD2_DAT0__AUD4_RXD 0x80000000 - MX6Q_PAD_SD2_DAT3__AUD4_TXC 0x80000000 - MX6Q_PAD_SD2_DAT2__AUD4_TXD 0x80000000 - MX6Q_PAD_SD2_DAT1__AUD4_TXFS 0x80000000 - >; - }; - - pinctrl_audmux_2: audmux-2 { - fsl,pins = < - MX6Q_PAD_CSI0_DAT7__AUD3_RXD 0x80000000 - MX6Q_PAD_CSI0_DAT4__AUD3_TXC 0x80000000 - MX6Q_PAD_CSI0_DAT5__AUD3_TXD 0x80000000 - MX6Q_PAD_CSI0_DAT6__AUD3_TXFS 0x80000000 - >; - }; - }; - - ecspi1 { - pinctrl_ecspi1_1: ecspi1grp-1 { - fsl,pins = < - MX6Q_PAD_EIM_D17__ECSPI1_MISO 0x100b1 - MX6Q_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 - MX6Q_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 - >; - }; - }; - - ecspi3 { - pinctrl_ecspi3_1: ecspi3grp-1 { - fsl,pins = < - MX6Q_PAD_DISP0_DAT2__ECSPI3_MISO 0x100b1 - MX6Q_PAD_DISP0_DAT1__ECSPI3_MOSI 0x100b1 - MX6Q_PAD_DISP0_DAT0__ECSPI3_SCLK 0x100b1 - >; - }; - }; - - enet { - pinctrl_enet_1: enetgrp-1 { - fsl,pins = < - MX6Q_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 - MX6Q_PAD_ENET_MDC__ENET_MDC 0x1b0b0 - MX6Q_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 - MX6Q_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6Q_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6Q_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6Q_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6Q_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 - MX6Q_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 - MX6Q_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 - MX6Q_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 - MX6Q_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 - MX6Q_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 - MX6Q_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 - MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 - MX6Q_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 - >; - }; - - pinctrl_enet_2: enetgrp-2 { - fsl,pins = < - MX6Q_PAD_KEY_COL1__ENET_MDIO 0x1b0b0 - MX6Q_PAD_KEY_COL2__ENET_MDC 0x1b0b0 - MX6Q_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 - MX6Q_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6Q_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6Q_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6Q_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6Q_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 - MX6Q_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 - MX6Q_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 - MX6Q_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 - MX6Q_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 - MX6Q_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 - MX6Q_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 - MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 - >; - }; - }; - - gpmi-nand { - pinctrl_gpmi_nand_1: gpmi-nand-1 { - fsl,pins = < - MX6Q_PAD_NANDF_CLE__NAND_CLE 0xb0b1 - MX6Q_PAD_NANDF_ALE__NAND_ALE 0xb0b1 - MX6Q_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 - MX6Q_PAD_NANDF_RB0__NAND_READY_B 0xb000 - MX6Q_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 - MX6Q_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 - MX6Q_PAD_NANDF_CS2__NAND_CE2_B 0xb0b1 - MX6Q_PAD_NANDF_CS3__NAND_CE3_B 0xb0b1 - MX6Q_PAD_SD4_CMD__NAND_RE_B 0xb0b1 - MX6Q_PAD_SD4_CLK__NAND_WE_B 0xb0b1 - MX6Q_PAD_NANDF_D0__NAND_DATA00 0xb0b1 - MX6Q_PAD_NANDF_D1__NAND_DATA01 0xb0b1 - MX6Q_PAD_NANDF_D2__NAND_DATA02 0xb0b1 - MX6Q_PAD_NANDF_D3__NAND_DATA03 0xb0b1 - MX6Q_PAD_NANDF_D4__NAND_DATA04 0xb0b1 - MX6Q_PAD_NANDF_D5__NAND_DATA05 0xb0b1 - MX6Q_PAD_NANDF_D6__NAND_DATA06 0xb0b1 - MX6Q_PAD_NANDF_D7__NAND_DATA07 0xb0b1 - MX6Q_PAD_SD4_DAT0__NAND_DQS 0x00b1 - >; - }; - }; - - i2c1 { - pinctrl_i2c1_1: i2c1grp-1 { - fsl,pins = < - MX6Q_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 - MX6Q_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 - >; - }; - }; - - i2c2 { - pinctrl_i2c2_1: i2c2grp-1 { - fsl,pins = < - MX6Q_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 - MX6Q_PAD_EIM_D16__I2C2_SDA 0x4001b8b1 - >; - }; - }; - - i2c3 { - pinctrl_i2c3_1: i2c3grp-1 { - fsl,pins = < - MX6Q_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 - MX6Q_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 - >; - }; - }; - - uart1 { - pinctrl_uart1_1: uart1grp-1 { - fsl,pins = < - MX6Q_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 - MX6Q_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 - >; - }; - }; - - uart2 { - pinctrl_uart2_1: uart2grp-1 { - fsl,pins = < - MX6Q_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 - MX6Q_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 - >; - }; - }; - - uart4 { - pinctrl_uart4_1: uart4grp-1 { - fsl,pins = < - MX6Q_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 - MX6Q_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 - >; - }; - }; - - usbotg { - pinctrl_usbotg_1: usbotggrp-1 { - fsl,pins = < - MX6Q_PAD_GPIO_1__USB_OTG_ID 0x17059 - >; - }; - - pinctrl_usbotg_2: usbotggrp-2 { - fsl,pins = < - MX6Q_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 - >; - }; - }; - - usdhc2 { - pinctrl_usdhc2_1: usdhc2grp-1 { - fsl,pins = < - MX6Q_PAD_SD2_CMD__SD2_CMD 0x17059 - MX6Q_PAD_SD2_CLK__SD2_CLK 0x10059 - MX6Q_PAD_SD2_DAT0__SD2_DATA0 0x17059 - MX6Q_PAD_SD2_DAT1__SD2_DATA1 0x17059 - MX6Q_PAD_SD2_DAT2__SD2_DATA2 0x17059 - MX6Q_PAD_SD2_DAT3__SD2_DATA3 0x17059 - MX6Q_PAD_NANDF_D4__SD2_DATA4 0x17059 - MX6Q_PAD_NANDF_D5__SD2_DATA5 0x17059 - MX6Q_PAD_NANDF_D6__SD2_DATA6 0x17059 - MX6Q_PAD_NANDF_D7__SD2_DATA7 0x17059 - >; - }; - }; - - usdhc3 { - pinctrl_usdhc3_1: usdhc3grp-1 { - fsl,pins = < - MX6Q_PAD_SD3_CMD__SD3_CMD 0x17059 - MX6Q_PAD_SD3_CLK__SD3_CLK 0x10059 - MX6Q_PAD_SD3_DAT0__SD3_DATA0 0x17059 - MX6Q_PAD_SD3_DAT1__SD3_DATA1 0x17059 - MX6Q_PAD_SD3_DAT2__SD3_DATA2 0x17059 - MX6Q_PAD_SD3_DAT3__SD3_DATA3 0x17059 - MX6Q_PAD_SD3_DAT4__SD3_DATA4 0x17059 - MX6Q_PAD_SD3_DAT5__SD3_DATA5 0x17059 - MX6Q_PAD_SD3_DAT6__SD3_DATA6 0x17059 - MX6Q_PAD_SD3_DAT7__SD3_DATA7 0x17059 - >; - }; - - pinctrl_usdhc3_2: usdhc3grp-2 { - fsl,pins = < - MX6Q_PAD_SD3_CMD__SD3_CMD 0x17059 - MX6Q_PAD_SD3_CLK__SD3_CLK 0x10059 - MX6Q_PAD_SD3_DAT0__SD3_DATA0 0x17059 - MX6Q_PAD_SD3_DAT1__SD3_DATA1 0x17059 - MX6Q_PAD_SD3_DAT2__SD3_DATA2 0x17059 - MX6Q_PAD_SD3_DAT3__SD3_DATA3 0x17059 - >; - }; - }; + }; + }; - usdhc4 { - pinctrl_usdhc4_1: usdhc4grp-1 { - fsl,pins = < - MX6Q_PAD_SD4_CMD__SD4_CMD 0x17059 - MX6Q_PAD_SD4_CLK__SD4_CLK 0x10059 - MX6Q_PAD_SD4_DAT0__SD4_DATA0 0x17059 - MX6Q_PAD_SD4_DAT1__SD4_DATA1 0x17059 - MX6Q_PAD_SD4_DAT2__SD4_DATA2 0x17059 - MX6Q_PAD_SD4_DAT3__SD4_DATA3 0x17059 - MX6Q_PAD_SD4_DAT4__SD4_DATA4 0x17059 - MX6Q_PAD_SD4_DAT5__SD4_DATA5 0x17059 - MX6Q_PAD_SD4_DAT6__SD4_DATA6 0x17059 - MX6Q_PAD_SD4_DAT7__SD4_DATA7 0x17059 - >; - }; - - pinctrl_usdhc4_2: usdhc4grp-2 { - fsl,pins = < - MX6Q_PAD_SD4_CMD__SD4_CMD 0x17059 - MX6Q_PAD_SD4_CLK__SD4_CLK 0x10059 - MX6Q_PAD_SD4_DAT0__SD4_DATA0 0x17059 - MX6Q_PAD_SD4_DAT1__SD4_DATA1 0x17059 - MX6Q_PAD_SD4_DAT2__SD4_DATA2 0x17059 - MX6Q_PAD_SD4_DAT3__SD4_DATA3 0x17059 - >; - }; - }; + aips-bus@02100000 { /* AIPS2 */ + mipi_dsi: mipi@021e0000 { + compatible = "fsl,imx6q-mipi-dsi"; + reg = <0x021e0000 0x4000>; + interrupts = <0 102 0x04>; + gpr = <&gpr>; + clocks = <&clks 138>, <&clks 204>; + clock-names = "mipi_pllref_clk", "mipi_cfg_clk"; + status = "disabled"; }; }; + sata: sata@02200000 { + compatible = "fsl,imx6q-ahci"; + reg = <0x02200000 0x4000>; + interrupts = <0 39 0x04>; + clocks = <&clks 154>, <&clks 187>, <&clks 105>; + clock-names = "sata", "sata_ref", "ahb"; + status = "disabled"; + }; + ipu2: ipu@02800000 { - #crtc-cells = <1>; compatible = "fsl,imx6q-ipu"; reg = <0x02800000 0x400000>; interrupts = <0 8 0x4 0 7 0x4>; - clocks = <&clks 133>, <&clks 134>, <&clks 137>; - clock-names = "bus", "di0", "di1"; + clocks = <&clks 133>, <&clks 134>, <&clks 137>, + <&clks 41>, <&clks 42>, + <&clks 135>, <&clks 136>; + clock-names = "bus", "di0", "di1", + "di0_sel", "di1_sel", + "ldb_di0", "ldb_di1"; resets = <&src 4>; + bypass_reset = <0>; }; }; }; -&ldb { - clocks = <&clks 33>, <&clks 34>, - <&clks 39>, <&clks 40>, <&clks 41>, <&clks 42>, - <&clks 135>, <&clks 136>; - clock-names = "di0_pll", "di1_pll", - "di0_sel", "di1_sel", "di2_sel", "di3_sel", - "di0", "di1"; - - lvds-channel@0 { - crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; +&iomuxc { + ipu2 { + pinctrl_ipu2_1: ipu2grp-1 { + fsl,pins = < + MX6QDL_PAD_DI0_DISP_CLK__IPU2_DI0_DISP_CLK 0x10 + MX6QDL_PAD_DI0_PIN15__IPU2_DI0_PIN15 0x10 + MX6QDL_PAD_DI0_PIN2__IPU2_DI0_PIN02 0x10 + MX6QDL_PAD_DI0_PIN3__IPU2_DI0_PIN03 0x10 + MX6QDL_PAD_DI0_PIN4__IPU2_DI0_PIN04 0x80000000 + MX6QDL_PAD_DISP0_DAT0__IPU2_DISP0_DATA00 0x10 + MX6QDL_PAD_DISP0_DAT1__IPU2_DISP0_DATA01 0x10 + MX6QDL_PAD_DISP0_DAT2__IPU2_DISP0_DATA02 0x10 + MX6QDL_PAD_DISP0_DAT3__IPU2_DISP0_DATA03 0x10 + MX6QDL_PAD_DISP0_DAT4__IPU2_DISP0_DATA04 0x10 + MX6QDL_PAD_DISP0_DAT5__IPU2_DISP0_DATA05 0x10 + MX6QDL_PAD_DISP0_DAT6__IPU2_DISP0_DATA06 0x10 + MX6QDL_PAD_DISP0_DAT7__IPU2_DISP0_DATA07 0x10 + MX6QDL_PAD_DISP0_DAT8__IPU2_DISP0_DATA08 0x10 + MX6QDL_PAD_DISP0_DAT9__IPU2_DISP0_DATA09 0x10 + MX6QDL_PAD_DISP0_DAT10__IPU2_DISP0_DATA10 0x10 + MX6QDL_PAD_DISP0_DAT11__IPU2_DISP0_DATA11 0x10 + MX6QDL_PAD_DISP0_DAT12__IPU2_DISP0_DATA12 0x10 + MX6QDL_PAD_DISP0_DAT13__IPU2_DISP0_DATA13 0x10 + MX6QDL_PAD_DISP0_DAT14__IPU2_DISP0_DATA14 0x10 + MX6QDL_PAD_DISP0_DAT15__IPU2_DISP0_DATA15 0x10 + MX6QDL_PAD_DISP0_DAT16__IPU2_DISP0_DATA16 0x10 + MX6QDL_PAD_DISP0_DAT17__IPU2_DISP0_DATA17 0x10 + MX6QDL_PAD_DISP0_DAT18__IPU2_DISP0_DATA18 0x10 + MX6QDL_PAD_DISP0_DAT19__IPU2_DISP0_DATA19 0x10 + MX6QDL_PAD_DISP0_DAT20__IPU2_DISP0_DATA20 0x10 + MX6QDL_PAD_DISP0_DAT21__IPU2_DISP0_DATA21 0x10 + MX6QDL_PAD_DISP0_DAT22__IPU2_DISP0_DATA22 0x10 + MX6QDL_PAD_DISP0_DAT23__IPU2_DISP0_DATA23 0x10 + >; + }; }; +}; - lvds-channel@1 { - crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; - }; +&hdmi { + compatible = "fsl,imx6q-hdmi"; + crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2014 Russell King + */ +#include "imx6qdl-microsom.dtsi" +#include "imx6qdl-microsom-ar8035.dtsi" + +/ { + aliases { + mxcfb0 = &mxcfb1; + }; + + ir_recv: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio3 9 1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_ir>; + }; + + pwmleds { + compatible = "pwm-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_pwm1>; + + front { + active-low; + default-brightness = <128>; + label = "imx6:red:front"; + max-brightness = <248>; + pwms = <&pwm1 0 50000>; + }; + }; + + regulators { + compatible = "simple-bus"; + + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + reg_usbh1_vbus: usb-h1-vbus { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 0 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_usbh1_vbus>; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_usbotg_vbus: usb-otg-vbus { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio3 22 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_usbotg_vbus>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + }; + + codec: spdif-transmitter { + compatible = "linux,spdif-dit"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_spdif>; + }; + + imx-drm { + compatible = "fsl,imx-drm"; + crtcs = <&ipu1 0>, <&ipu1 1>; + connectors = <&hdmi>; + }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + mxcfb1: mxc_sdc_fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1280x720@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; + + v4l2_cap_0 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <0>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_cap_1 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <1>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; +}; + +&hdmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_hdmi>; + ddc = <&i2c2>; + status = "okay"; + crtcs = <&ipu1 0>; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_cec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_hdmi>; + status = "okay"; +}; + +&hdmi_core { + ipu_id = <1>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + fsl,hdcp; + status = "okay"; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_2>; + status = "disable"; +}; + +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3_1>; + + status = "okay"; + + rtc: pcf8523@68 { + compatible = "nxp,pcf8523"; + reg = <0x68>; + }; +}; + +&iomuxc { + cubox_i { + pinctrl_cubox_i_ir: cubox-i-ir { + fsl,pins = < + MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x80000000 + >; + }; + + pinctrl_cubox_i_hdmi: cubox-i-hdmi { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + + pinctrl_cubox_i_pwm1: cubox-i-pwm1-front-led { + fsl,pins = ; + }; + + pinctrl_cubox_i_spdif: cubox-i-spdif { + fsl,pins = ; + }; + + pinctrl_cubox_i_usbh1_vbus: cubox-i-usbh1-vbus { + fsl,pins = ; + }; + + pinctrl_cubox_i_usbotg_vbus: cubox-i-usbotg-vbus { + fsl,pins = ; + }; + + pinctrl_cubox_i_usdhc2_aux: cubox-i-usdhc2-aux { + fsl,pins = < + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1f071 + MX6QDL_PAD_KEY_ROW1__SD2_VSELECT 0x1b071 + >; + }; + + pinctrl_cubox_i_usdhc2: cubox-i-usdhc2 { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 + >; + }; + + pinctrl_cubox_i_usdhc2_100mhz: cubox-i-usdhc2-100mhz { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170b9 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100b9 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130b9 + >; + }; + + pinctrl_cubox_i_usdhc2_200mhz: cubox-i-usdhc2-200mhz { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170f9 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100f9 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130f9 + >; + }; + }; +}; + +&spdif { + status = "okay"; +}; + +&usbh1 { + vbus-supply = <®_usbh1_vbus>; + status = "okay"; +}; + +&usbotg { + vbus-supply = <®_usbotg_vbus>; + status = "okay"; +}; + +&uart4 { + status = "okay"; +}; + +&usdhc1 { + status = "disabled"; +}; + +&uart4 { + status = "okay"; +}; + +&usdhc1 { + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2>; + pinctrl-1 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_100mhz>; + pinctrl-2 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_200mhz>; + vmmc-supply = <®_3p3v>; + cd-gpios = <&gpio1 4 0>; + no-1-8-v; + status = "okay"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,58 @@ +/* + * Copyright 2013 Russell King + * + * The code contained herein is licensed under the GNU General Public + * License version 2. + * + * This describes the hookup for an AR8035 to the IMX6 on the Cubox-i + * MicroSOM. + * + * FIXME: we need to configure PLL_ENET to produce 25MHz, but there + * doesn't seem to be a way to do that yet from DT. (Writing 0x2000 + * to 0x020c80e0 phys will do this.) + */ +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_enet_ar8035>; + phy-mode = "rgmii"; + phy-reset-duration = <2>; + phy-reset-gpios = <&gpio4 15 0>; + status = "okay"; +}; + +&iomuxc { + enet { + pinctrl_microsom_enet_ar8035: microsom-enet-ar8035 { + fsl,pins = < + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + /* AR8035 reset */ + MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x130b0 + /* AR8035 interrupt */ + MX6QDL_PAD_DI0_PIN2__GPIO4_IO18 0x80000000 + /* GPIO16 -> AR8035 25MHz */ + MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0xc0000000 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x80000000 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + /* AR8035 CLK_25M --> ENET_REF_CLK (V22) */ + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x0a0b1 + /* AR8035 pin strapping: IO voltage: pull up */ + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + /* AR8035 pin strapping: PHYADDR#0: pull down */ + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x130b0 + /* AR8035 pin strapping: PHYADDR#1: pull down */ + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x130b0 + /* AR8035 pin strapping: MODE#1: pull up */ + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + /* AR8035 pin strapping: MODE#3: pull up */ + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + /* AR8035 pin strapping: MODE#0: pull down */ + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x130b0 + >; + }; + }; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6qdl-microsom.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-microsom.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6qdl-microsom.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013,2014 Russell King + */ +#include +/ { + regulators { + compatible = "simple-bus"; + + reg_brcm_osc: brcm-osc-reg { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio5 5 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_brcm_osc_reg>; + regulator-name = "brcm_osc_reg"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + reg_brcm: brcm-reg { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_brcm_reg>; + regulator-name = "brcm_reg"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <200000>; + }; + }; +}; + +&iomuxc { + microsom { + pinctrl_microsom_brcm_osc_reg: microsom-brcm-osc-reg { + fsl,pins = < + MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x40013070 + >; + }; + + pinctrl_microsom_brcm_reg: microsom-brcm-reg { + fsl,pins = < + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x40013070 + >; + }; + + pinctrl_microsom_brcm_wifi: microsom-brcm-wifi { + fsl,pins = < + MX6QDL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x1b0b0 + MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x40013070 + MX6QDL_PAD_CSI0_DAT8__GPIO5_IO26 0x40013070 + MX6QDL_PAD_CSI0_DAT9__GPIO5_IO27 0x40013070 + >; + }; + + pinctrl_microsom_brcm_bt: microsom-brcm-bt { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x40013070 + MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x40013070 + MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x40013070 + >; + }; + + pinctrl_microsom_uart1: microsom-uart1 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_microsom_uart4_1: microsom-uart4 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x1b0b1 + MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x1b0b1 + >; + }; + + pinctrl_microsom_usbotg: microsom-usbotg { + /* + * Similar to pinctrl_usbotg_2, but we want it + * pulled down for a fixed host connection. + */ + fsl,pins = ; + }; + }; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_uart1>; + status = "okay"; +}; + +/* UART4 - Connected to optional BRCM Wifi/BT/FM */ +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_brcm_bt &pinctrl_microsom_uart4_1>; + fsl,uart-has-rtscts; +}; + +&usbotg { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_usbotg>; +}; + +/* USDHC1 - Connected to optional BRCM Wifi/BT/FM */ +&usdhc1 { + card-external-vcc-supply = <®_brcm>; + card-reset-gpios = <&gpio5 26 GPIO_ACTIVE_LOW>, <&gpio6 0 GPIO_ACTIVE_LOW>; + keep-power-in-suspend; + non-removable; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_brcm_wifi &pinctrl_usdhc1_2>; + vmmc-supply = <®_brcm>; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -11,9 +11,282 @@ */ / { + aliases { + mxcfb0 = &mxcfb1; + mxcfb1 = &mxcfb2; + mxcfb2 = &mxcfb3; + mxcfb3 = &mxcfb4; + }; + memory { reg = <0x10000000 0x80000000>; }; + + gpio-keys { + compatible = "gpio-keys"; + home { + label = "Home"; + gpios = <&gpio1 11 0>; + linux,code = <102>; /* KEY_HOME */ + gpio-key,wakeup; + }; + + back { + label = "Back"; + gpios = <&gpio1 12 0>; + linux,code = <158>; /* KEY_BACK */ + gpio-key,wakeup; + }; + + program { + label = "Program"; + gpios = <&gpio2 12 0>; + linux,code = <362>; /* KEY_PROGRAM */ + gpio-key,wakeup; + }; + + volume-up { + label = "Volume Up"; + gpios = <&gpio2 15 0>; + linux,code = <115>; /* KEY_VOLUMEUP */ + gpio-key,wakeup; + }; + + volume-down { + label = "Volume Down"; + gpios = <&gpio5 14 0>; + linux,code = <114>; /* KEY_VOLUMEDOWN */ + gpio-key,wakeup; + }; + }; + + max7310_reset: max7310-reset { + compatible = "gpio-reset"; + reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; + reset-delay-us = <1>; + #reset-cells = <0>; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="LDB-XGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <24>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb3: fb@2 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "lcd"; + interface_pix_fmt = "RGB565"; + mode_str ="CLAA-WVGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb4: fb@3 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="LDB-XGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm3 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + }; + + regulators { + compatible = "simple-bus"; + reg_audio: cs42888_supply { + compatible = "regulator-fixed"; + regulator-name = "cs42888_supply"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + si4763_vio1: vio1_tnr { + compatible = "regulator-fixed"; + regulator-name = "vio1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + si4763_vio2: vio2_tnr { + compatible = "regulator-fixed"; + regulator-name = "vio2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + si4763_vd: f3v3_tnr { + compatible = "regulator-fixed"; + regulator-name = "vd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + si4763_va: f5v_tnr { + compatible = "regulator-fixed"; + regulator-name = "va"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + reg_usb_h1_vbus: usb_h1_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&max7310_b 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usb_otg_vbus: usb_otg_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&max7310_c 1 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + + sound-cs42888 { + compatible = "fsl,imx6-sabreauto-cs42888", + "fsl,imx-audio-cs42888"; + model = "imx-cs42888"; + esai-controller = <&esai>; + asrc-controller = <&asrc_p2p>; + audio-codec = <&codec>; + }; + + clocks { + codec_osc: codec_osc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + }; + }; + + sound-fm { + compatible = "fsl,imx-audio-si476x", + "fsl,imx-tuner-si476x"; + model = "imx-radio-si4763"; + + ssi-controller = <&ssi2>; + fm-controller = <&si4763>; + mux-int-port = <2>; + mux-ext-port = <5>; + }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif", + "fsl,imx-sabreauto-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-in; + }; + + v4l2_cap_0 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <0>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_cap_1 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <1>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux_3>; + status = "okay"; +}; + +&ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_1 &pinctrl_ecspi1_sabreauto>; + status = "disabled"; /* pin conflict with WEIM NOR */ + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25p32"; + spi-max-frequency = <20000000>; + reg = <0>; + }; +}; + +&ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_1>; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25p32"; + spi-max-frequency = <20000000>; + reg = <0>; + }; }; &fec { @@ -23,16 +296,409 @@ status = "okay"; }; +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand_1>; + status = "okay"; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_3>; + status = "okay"; + + egalax_ts@04 { + compatible = "eeti,egalax_ts"; + reg = <0x04>; + interrupt-parent = <&gpio2>; + interrupts = <28 2>; + wakeup-gpios = <&gpio2 28 0>; + }; + + pmic: pfuze100@08 { + compatible = "fsl,pfuze100"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1ab { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw1c_reg: sw1c { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3a { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3b_reg: sw3b { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + vgen4_reg: vgen4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen5_reg: vgen5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vgen6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; + + codec: cs42888@048 { + compatible = "cirrus,cs42888"; + reg = <0x048>; + clocks = <&codec_osc 0>; + clock-names = "codec_osc"; + VA-supply = <®_audio>; + VD-supply = <®_audio>; + VLS-supply = <®_audio>; + VLC-supply = <®_audio>; + }; + + hdmi: edid@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; + + si4763: si4763@63 { + compatible = "si4761"; + reg = <0x63>; + va-supply = <&si4763_va>; + vd-supply = <&si4763_vd>; + vio1-supply = <&si4763_vio1>; + vio2-supply = <&si4763_vio2>; + }; +}; + +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3_4>; + pinctrl-assert-gpios = <&gpio5 4 GPIO_ACTIVE_HIGH>; + status = "okay"; + + max7310_a: gpio@30 { + compatible = "maxim,max7310"; + reg = <0x30>; + gpio-controller; + #gpio-cells = <2>; + resets = <&max7310_reset>; + }; + + max7310_b: gpio@32 { + compatible = "maxim,max7310"; + reg = <0x32>; + gpio-controller; + #gpio-cells = <2>; + }; + + max7310_c: gpio@34 { + compatible = "maxim,max7310"; + reg = <0x34>; + gpio-controller; + #gpio-cells = <2>; + }; + + mma8451@1c { + compatible = "fsl,mma8451"; + reg = <0x1c>; + position = <3>; + }; + + mag3110@0e { + compatible = "fsl,mag3110"; + reg = <0x0e>; + position = <2>; + interrupt-parent = <&gpio2>; + interrupts = <29 2>; + }; + + isl29023@44 { + compatible = "fsl,isl29023"; + reg = <0x44>; + rext = <499>; + interrupt-parent = <&gpio5>; + interrupts = <17 2>; + }; + + adv7180: adv7180@21 { + compatible = "adv,adv7180"; + reg = <0x21>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_3>; + clocks = <&clks 201>; + clock-names = "csi_mclk"; + DOVDD-supply = <®_3p3v>; /* 3.3v, enabled via 2.8 VGEN6 */ + AVDD-supply = <®_3p3v>; /* 1.8v */ + DVDD-supply = <®_3p3v>; /* 1.8v */ + PVDD-supply = <®_3p3v>; /* 1.8v */ + pwn-gpios = <&max7310_b 2 0>; + csi_id = <0>; + mclk = <24000000>; + mclk_source = <0>; + cvbs = <1>; + }; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x80000000 + MX6QDL_PAD_SD2_DAT2__GPIO1_IO13 0x80000000 + MX6QDL_PAD_EIM_A24__GPIO5_IO04 0x80000000 + MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x80000000 + MX6QDL_PAD_EIM_EB0__GPIO2_IO28 0x80000000 + MX6QDL_PAD_EIM_EB1__GPIO2_IO29 0x80000000 + MX6QDL_PAD_DISP0_DAT23__GPIO5_IO17 0x80000000 + MX6QDL_PAD_SD2_CMD__GPIO1_IO11 0x80000000 + MX6QDL_PAD_SD2_DAT3__GPIO1_IO12 0x80000000 + MX6QDL_PAD_SD4_DAT4__GPIO2_IO12 0x80000000 + MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x80000000 + MX6QDL_PAD_DISP0_DAT20__GPIO5_IO14 0x80000000 + MX6QDL_PAD_GPIO_18__SD3_VSELECT 0x17059 + >; + }; + }; + + ecspi1 { + pinctrl_ecspi1_sabreauto: ecspi1-sabreauto { + fsl,pins = < + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x80000000 + >; + }; + }; +}; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif_1>; + status = "okay"; +}; + +&ssi2 { + fsl,mode = "i2s-master"; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3_1>; + pinctrl-assert-gpios = <&max7310_b 4 GPIO_ACTIVE_HIGH>, /* CTS */ + <&max7310_c 3 GPIO_ACTIVE_HIGH>; /* RXD and TXD */ + fsl,uart-has-rtscts; + status = "okay"; +}; + +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand_1>; + status = "disabled"; /* pin conflict with uart3 */ +}; + &uart4 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart4_1>; status = "okay"; }; -&usdhc3 { +&usbh1 { + vbus-supply = <®_usb_h1_vbus>; + status = "okay"; +}; + +&usbotg { + vbus-supply = <®_usb_otg_vbus>; pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg_2>; + imx6-usb-charger-detection; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc3_1>; + pinctrl-1 = <&pinctrl_usdhc3_1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_1_200mhz>; cd-gpios = <&gpio6 15 0>; wp-gpios = <&gpio1 13 0>; status = "okay"; }; + +&weim { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_weim_nor_1 &pinctrl_weim_cs0_1>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x08000000>; + status = "disabled"; /* pin conflict with ecspi1, i2c3 and uart3 */ + + nor@0,0 { + compatible = "cfi-flash"; + reg = <0 0 0x02000000>; + #address-cells = <1>; + #size-cells = <1>; + bank-width = <2>; + fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000 + 0x0000c000 0x1404a38e 0x00000000>; + }; +}; + +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1_1>; + pinctrl-assert-gpios = <&max7310_b 3 GPIO_ACTIVE_HIGH>; /* TX */ + trx-en-gpio = <&max7310_b 6 GPIO_ACTIVE_HIGH>; + trx-stby-gpio = <&max7310_b 5 GPIO_ACTIVE_HIGH>; + status = "disabled"; /* pin conflict with fec */ +}; + +&flexcan2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan2_1>; + trx-en-gpio = <&max7310_c 6 GPIO_ACTIVE_HIGH>; + trx-stby-gpio = <&max7310_c 5 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&ldb { + ipu_id = <1>; + disp_id = <0>; + ext_ref = <1>; + mode = "sep0"; + sec_ipu_id = <1>; + sec_disp_id = <1>; + status = "okay"; +}; + +&mipi_csi { + status = "okay"; + ipu_id = <0>; + csi_id = <1>; + v_channel = <0>; + lanes = <1>; +}; + +&mlb { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mlb_2>; + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&pwm3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm3_1>; + status = "okay"; +}; + +&esai { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esai_2>; + status = "okay"; +}; + +&hdmi_core { + ipu_id = <0>; + disp_id = <1>; + status = "okay"; +}; + +&hdmi_video { + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + status = "okay"; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_cec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_cec_1>; + status = "okay"; +}; + +&gpc { + fsl,cpu_pupscr_sw2iso = <0xf>; + fsl,cpu_pupscr_sw = <0xf>; + fsl,cpu_pdnscr_iso2sw = <0x1>; + fsl,cpu_pdnscr_iso = <0x1>; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6qdl-sabresd.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-sabresd.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6qdl-sabresd.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl-sabresd.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -11,10 +11,30 @@ */ / { + aliases { + mxcfb0 = &mxcfb1; + mxcfb1 = &mxcfb2; + mxcfb2 = &mxcfb3; + mxcfb3 = &mxcfb4; + }; + memory { reg = <0x10000000 0x40000000>; }; + battery: max8903@0 { + compatible = "fsl,max8903-charger"; + pinctrl-names = "default"; + dok_input = <&gpio2 24 1>; + uok_input = <&gpio1 27 1>; + chg_input = <&gpio3 23 1>; + flt_input = <&gpio5 2 1>; + fsl,dcm_always_high; + fsl,dc_valid; + fsl,usb_valid; + status = "okay"; + }; + regulators { compatible = "simple-bus"; @@ -26,29 +46,605 @@ gpio = <&gpio3 22 0>; enable-active-high; }; + + reg_usb_h1_vbus: usb_h1_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio1 29 0>; + enable-active-high; + }; + + reg_usb_h1_vbus: usb_h1_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio1 29 0>; + enable-active-high; + }; + + reg_audio: wm8962_supply { + compatible = "regulator-fixed"; + regulator-name = "wm8962-supply"; + gpio = <&gpio4 10 0>; + enable-active-high; + }; + + reg_mipi_dsi_pwr_on: mipi_dsi_pwr_on { + compatible = "regulator-fixed"; + regulator-name = "mipi_dsi_pwr_on"; + gpio = <&gpio6 14 0>; + enable-active-high; + }; + + reg_sensor: sensor_supply { + compatible = "regulator-fixed"; + regulator-name = "sensor-supply"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 31 0>; + startup-delay-us = <500>; + enable-active-high; + }; }; gpio-keys { compatible = "gpio-keys"; + power { + label = "Power Button"; + gpios = <&gpio3 29 0>; + linux,code = <116>; /* KEY_POWER */ + gpio-key,wakeup; + }; volume-up { label = "Volume Up"; gpios = <&gpio1 4 0>; + gpio-key,wakeup; linux,code = <115>; /* KEY_VOLUMEUP */ }; volume-down { label = "Volume Down"; gpios = <&gpio1 5 0>; + gpio-key,wakeup; linux,code = <114>; /* KEY_VOLUMEDOWN */ }; }; + + imx_drm: imx-drm { + compatible = "fsl,imx-drm"; + crtcs = <&ipu1 0>, <&ipu1 1>; + connectors = <&ldb>; + }; + + sound { + compatible = "fsl,imx6q-sabresd-wm8962", + "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + ssi-controller = <&ssi2>; + audio-codec = <&codec>; + audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "MICBIAS", "AMIC", + "IN3R", "MICBIAS", + "DMIC", "MICBIAS", + "DMICDAT", "DMIC"; + mux-int-port = <2>; + mux-ext-port = <3>; + hp-det-gpios = <&gpio7 8 1>; + mic-det-gpios = <&gpio1 9 1>; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm1 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + status = "okay"; + }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="LDB-XGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <24>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb3: fb@2 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "lcd"; + interface_pix_fmt = "RGB565"; + mode_str ="CLAA-WVGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb4: fb@3 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="LDB-XGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + lcd@0 { + compatible = "fsl,lcd"; + ipu_id = <0>; + disp_id = <0>; + default_ifmt = "RGB565"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_1>; + status = "okay"; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm1 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + }; + + v4l2_cap_0 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <0>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_cap_1 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <1>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; + + lvds_cabc_ctrl { + lvds0-gpios = <&gpio6 15 0>; + lvds1-gpios = <&gpio6 16 0>; + }; + + mipi_dsi_reset: mipi-dsi-reset { + compatible = "gpio-reset"; + reset-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>; + reset-delay-us = <50>; + #reset-cells = <0>; + }; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux_2>; + status = "okay"; +}; + +&cpu0 { + arm-supply = <&sw1a_reg>; + soc-supply = <&sw1c_reg>; + pu-supply = <&pu_dummy>; /* use pu_dummy if VDDSOC share with VDDPU */ +}; + +&ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio4 9 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_2>; + status = "okay"; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25p32"; + spi-max-frequency = <20000000>; + reg = <0>; + }; }; &fec { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet_1>; phy-mode = "rgmii"; + phy-reset-gpios = <&gpio1 25 0>; + status = "okay"; +}; + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1_2>; + status = "okay"; + + codec: wm8962@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&clks 201>; + DCVDD-supply = <®_audio>; + DBVDD-supply = <®_audio>; + AVDD-supply = <®_audio>; + CPVDD-supply = <®_audio>; + MICVDD-supply = <®_audio>; + PLLVDD-supply = <®_audio>; + SPKVDD1-supply = <®_audio>; + SPKVDD2-supply = <®_audio>; + amic-mono; + gpio-cfg = < + 0x0000 /* 0:Default */ + 0x0000 /* 1:Default */ + 0x0013 /* 2:FN_DMICCLK */ + 0x0000 /* 3:Default */ + 0x8014 /* 4:FN_DMICCDAT */ + 0x0000 /* 5:Default */ + >; + }; + + ov564x: ov564x@3c { + compatible = "ovti,ov564x"; + reg = <0x3c>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_2>; + clocks = <&clks 201>; + clock-names = "csi_mclk"; + DOVDD-supply = <&vgen4_reg>; /* 1.8v */ + AVDD-supply = <&vgen3_reg>; /* 2.8v, on rev C board is VGEN3, + on rev B board is VGEN5 */ + DVDD-supply = <&vgen2_reg>; /* 1.5v*/ + pwn-gpios = <&gpio1 16 1>; /* active low: SD1_DAT0 */ + rst-gpios = <&gpio1 17 0>; /* active high: SD1_DAT1 */ + csi_id = <0>; + mclk = <24000000>; + mclk_source = <0>; + }; + + mma8451@1c { + compatible = "fsl,mma8451"; + reg = <0x1c>; + position = <1>; + vdd-supply = <®_sensor>; + vddio-supply = <®_sensor>; + }; +}; + +&i2c3 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3_2>; + status = "okay"; + + egalax_ts@04 { + compatible = "eeti,egalax_ts"; + reg = <0x04>; + interrupt-parent = <&gpio6>; + interrupts = <7 2>; + wakeup-gpios = <&gpio6 7 0>; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_2>; + status = "okay"; + + hdmi: edid@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; + + max11801@48 { + compatible = "maxim,max11801"; + reg = <0x48>; + interrupt-parent = <&gpio3>; + interrupts = <26 2>; + work-mode = <1>;/*DCM mode*/ + }; + + ov5640_mipi: ov5640_mipi@3c { /* i2c2 driver */ + compatible = "ovti,ov5640_mipi"; + reg = <0x3c>; + clocks = <&clks 201>; + clock-names = "csi_mclk"; + DOVDD-supply = <&vgen4_reg>; /* 1.8v */ + AVDD-supply = <&vgen3_reg>; /* 2.8v, rev C board is VGEN3 + rev B board is VGEN5 */ + DVDD-supply = <&vgen2_reg>; /* 1.5v*/ + pwn-gpios = <&gpio1 19 1>; /* active low: SD1_CLK */ + rst-gpios = <&gpio1 20 0>; /* active high: SD1_DAT2 */ + csi_id = <1>; + mclk = <24000000>; + mclk_source = <0>; + }; + + pmic: pfuze100@08 { + compatible = "fsl,pfuze100"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1ab { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw1c_reg: sw1c { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3a { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3b_reg: sw3b { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + vgen4_reg: vgen4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen5_reg: vgen5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vgen6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; + + egalax_ts@04 { + compatible = "eeti,egalax_ts"; + reg = <0x04>; + interrupt-parent = <&gpio6>; + interrupts = <8 2>; + wakeup-gpios = <&gpio6 8 0>; + }; +}; + +&i2c3 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3_2>; + status = "okay"; + + egalax_ts@04 { + compatible = "eeti,egalax_ts"; + reg = <0x04>; + interrupt-parent = <&gpio6>; + interrupts = <7 2>; + wakeup-gpios = <&gpio6 7 0>; + }; + + mag3110@0e { + compatible = "fsl,mag3110"; + reg = <0x0e>; + position = <2>; + vdd-supply = <®_sensor>; + vddio-supply = <®_sensor>; + interrupt-parent = <&gpio3>; + interrupts = <16 2>; + }; + + elan@10 { + compatible = "elan,elan-touch"; + reg = <0x10>; + interrupt-parent = <&gpio3>; + interrupts = <28 3>; + gpio_elan_cs = <&gpio2 18 0>; + gpio_elan_rst = <&gpio3 8 0>; + gpio_intr = <&gpio3 28 0>; + status = "okay"; + }; + + isl29023@44 { + compatible = "fsl,isl29023"; + reg = <0x44>; + rext = <499>; + vdd-supply = <®_sensor>; + interrupt-parent = <&gpio3>; + interrupts = <9 2>; + }; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog_1>; + + hog { + pinctrl_hog_1: hoggrp-1 { + fsl,pins = < + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 + MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000 + MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x80000000 + MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x80000000 + MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 + MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x80000000 + MX6QDL_PAD_NANDF_ALE__GPIO6_IO08 0x80000000 + MX6QDL_PAD_NANDF_CLE__GPIO6_IO07 0x80000000 + MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0 + MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x80000000 + MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000 + MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x80000000 + MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 + MX6QDL_PAD_ENET_RXD0__GPIO1_IO27 0x80000000 + MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x80000000 + MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 + MX6QDL_PAD_NANDF_CLE__GPIO6_IO07 0x80000000 + MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 + MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 + MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x80000000 + MX6QDL_PAD_EIM_EB3__GPIO2_IO31 0x80000000 + MX6QDL_PAD_EIM_D16__GPIO3_IO16 0x80000000 + MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x80000000 + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 + MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x80000000 + MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 + MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 + MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x80000000 + MX6QDL_PAD_GPIO_1__WDOG2_B 0x80000000 + MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000 + >; + }; + }; +}; + +&ldb { + status = "okay"; + + lvds-channel@1 { + fsl,data-mapping = "spwg"; + fsl,data-width = <18>; + status = "okay"; + + display-timings { + native-mode = <&timing0>; + timing0: hsd100pxn1 { + clock-frequency = <65000000>; + hactive = <1024>; + vactive = <768>; + hback-porch = <220>; + hfront-porch = <40>; + vback-porch = <21>; + vfront-porch = <7>; + hsync-len = <60>; + vsync-len = <10>; + }; + }; + }; +}; + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm0_1>; + status = "okay"; +}; + +&ldb { + ipu_id = <1>; + disp_id = <1>; + ext_ref = <1>; + mode = "sep1"; + sec_ipu_id = <1>; + sec_disp_id = <0>; + status = "okay"; +}; + +&pcie { + power-on-gpio = <&gpio3 19 0>; + reset-gpio = <&gpio7 12 0>; + status = "okay"; +}; + + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm1_1>; + status = "okay"; +}; + +&ssi2 { + fsl,mode = "i2s-slave"; status = "okay"; }; @@ -58,7 +654,25 @@ status = "okay"; }; +&mipi_csi { + status = "okay"; + ipu_id = <0>; + csi_id = <1>; + v_channel = <0>; + lanes = <2>; +}; + +&mipi_dsi { + dev_id = <0>; + disp_id = <0>; + lcd_panel = "TRULY-WVGA"; + disp-power-on-supply = <®_mipi_dsi_pwr_on>; + resets = <&mipi_dsi_reset>; + status = "okay"; +}; + &usbh1 { + vbus-supply = <®_usb_h1_vbus>; status = "okay"; }; @@ -73,15 +687,68 @@ &usdhc2 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usdhc2_1>; + bus-width = <8>; cd-gpios = <&gpio2 2 0>; wp-gpios = <&gpio2 3 0>; + no-1-8-v; status = "okay"; }; &usdhc3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usdhc3_1>; + bus-width = <8>; cd-gpios = <&gpio2 0 0>; wp-gpios = <&gpio2 1 0>; + no-1-8-v; + status = "okay"; +}; + +&usdhc4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc4_1>; + bus-width = <8>; + non-removable; + no-1-8-v; + status = "okay"; +}; + +&vpu { + pu-supply = <&pu_dummy>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; + +&hdmi_core { + ipu_id = <0>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + status = "okay"; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_cec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_cec_2>; status = "okay"; }; + +&gpc { + fsl,cpu_pupscr_sw2iso = <0xf>; + fsl,cpu_pupscr_sw = <0xf>; + fsl,cpu_pdnscr_iso2sw = <0x1>; + fsl,cpu_pdnscr_iso = <0x1>; + fsl,ldo-bypass = <1>; /* use ldo-bypass, u-boot will check it and configure */ + fsl,wdog-reset = <2>; /* watchdog select of reset source */ + pu-supply = <&pu_dummy>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; + +&gpu { + pu-supply = <&pu_dummy>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6qdl.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6qdl.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6qdl.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -11,14 +11,12 @@ */ #include "skeleton.dtsi" +#include / { aliases { - serial0 = &uart1; - serial1 = &uart2; - serial2 = &uart3; - serial3 = &uart4; - serial4 = &uart5; + flexcan0 = &flexcan1; + flexcan1 = &flexcan2; gpio0 = &gpio1; gpio1 = &gpio2; gpio2 = &gpio3; @@ -26,6 +24,21 @@ gpio4 = &gpio5; gpio5 = &gpio6; gpio6 = &gpio7; + i2c0 = &i2c1; + i2c1 = &i2c2; + i2c2 = &i2c3; + ipu0 = &ipu1; + serial0 = &uart1; + serial1 = &uart2; + serial2 = &uart3; + serial3 = &uart4; + serial4 = &uart5; + spi0 = &ecspi1; + spi1 = &ecspi2; + spi2 = &ecspi3; + spi3 = &ecspi4; + usbphy0 = &usbphy1; + usbphy1 = &usbphy2; }; intc: interrupt-controller@00a01000 { @@ -58,6 +71,10 @@ }; }; + pu_dummy: pudummy_reg { + compatible = "fsl,imx6-dummy-pureg"; /* only used in ldo-bypass */ + }; + soc { #address-cells = <1>; #size-cells = <1>; @@ -65,6 +82,11 @@ interrupt-parent = <&intc>; ranges; + caam_sm: caam-sm@00100000 { + compatible = "fsl,imx6q-caam-sm"; + reg = <0x00100000 0x3fff>; + }; + dma_apbh: dma-apbh@00110000 { compatible = "fsl,imx6q-dma-apbh", "fsl,imx28-dma-apbh"; reg = <0x00110000 0x2000>; @@ -75,24 +97,35 @@ clocks = <&clks 106>; }; + irq_sec_vio: caam_secvio { + compatible = "fsl,imx6q-caam-secvio"; + interrupts = <0 20 0x04>; + secvio_src = <0x8000001d>; + }; + gpmi: gpmi-nand@00112000 { compatible = "fsl,imx6q-gpmi-nand"; #address-cells = <1>; #size-cells = <1>; reg = <0x00112000 0x2000>, <0x00114000 0x2000>; reg-names = "gpmi-nand", "bch"; - interrupts = <0 13 0x04>, <0 15 0x04>; - interrupt-names = "gpmi-dma", "bch"; + interrupts = <0 15 0x04>; + interrupt-names = "bch"; clocks = <&clks 152>, <&clks 153>, <&clks 151>, <&clks 150>, <&clks 149>; clock-names = "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch"; dmas = <&dma_apbh 0>; dma-names = "rx-tx"; - fsl,gpmi-dma-channel = <0>; status = "disabled"; }; + ocram: sram@00900000 { + compatible = "mmio-sram"; + reg = <0x00900000 0x3f000>; + clocks = <&clks 142>; + }; + timer@00a00600 { compatible = "arm,cortex-a9-twd-timer"; reg = <0x00a00600 0x20>; @@ -106,6 +139,24 @@ interrupts = <0 92 0x04>; cache-unified; cache-level = <2>; + arm,tag-latency = <4 2 3>; + arm,data-latency = <4 2 3>; + }; + + pcie: pcie@0x01000000 { + compatible = "fsl,imx6q-pcie", "snps,dw-pcie"; + reg = <0x01ffc000 0x4000>; /* DBI */ + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x00000800 0 0x01f00000 0x01f00000 0 0x00080000 /* configuration space */ + 0x81000000 0 0 0x01f80000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0x01000000 0x01000000 0 0x00f00000>; /* non-prefetchable memory */ + num-lanes = <1>; + interrupts = <0 123 0x04>; + clocks = <&clks 189>, <&clks 187>, <&clks 144>, <&clks 212>; + clock-names = "pcie_ref_125m", "sata_ref_100m", "pcie_axi", "lvds_gate"; + status = "disabled"; }; pmu { @@ -128,8 +179,24 @@ ranges; spdif: spdif@02004000 { + compatible = "fsl,imx6q-spdif", + "fsl,imx35-spdif"; reg = <0x02004000 0x4000>; interrupts = <0 52 0x04>; + dmas = <&sdma 14 18 0>, + <&sdma 15 18 0>; + dma-names = "rx", "tx"; + clocks = <&clks 197>, <&clks 3>, + <&clks 197>, <&clks 107>, + <&clks 0>, <&clks 118>, + <&clks 0>, <&clks 139>, + <&clks 0>, <&clks 156>; + clock-names = "core", "rxtx0", + "rxtx1", "rxtx2", + "rxtx3", "rxtx4", + "rxtx5", "rxtx6", + "rxtx7", "dma"; + status = "disabled"; }; ecspi1: ecspi@02008000 { @@ -182,19 +249,31 @@ interrupts = <0 26 0x04>; clocks = <&clks 160>, <&clks 161>; clock-names = "ipg", "per"; + dmas = <&sdma 25 4 0>, <&sdma 26 4 0>; + dma-names = "rx", "tx"; status = "disabled"; }; esai: esai@02024000 { + compatible = "fsl,imx6q-esai"; reg = <0x02024000 0x4000>; interrupts = <0 51 0x04>; + clocks = <&clks 118>, <&clks 156>; + clock-names = "core", "dma"; + fsl,esai-dma-events = <24 23>; + fsl,flags = <1>; + status = "disabled"; }; ssi1: ssi@02028000 { compatible = "fsl,imx6q-ssi","fsl,imx21-ssi"; reg = <0x02028000 0x4000>; interrupts = <0 46 0x04>; - clocks = <&clks 178>; + clocks = <&clks 178>, <&clks 157>; + clock-names = "ipg", "baud"; + dmas = <&sdma 37 1 0>, + <&sdma 38 1 0>; + dma-names = "rx", "tx"; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <38 37>; status = "disabled"; @@ -204,7 +283,11 @@ compatible = "fsl,imx6q-ssi","fsl,imx21-ssi"; reg = <0x0202c000 0x4000>; interrupts = <0 47 0x04>; - clocks = <&clks 179>; + clocks = <&clks 179>, <&clks 158>; + clock-names = "ipg", "baud"; + dmas = <&sdma 41 1 0>, + <&sdma 42 1 0>; + dma-names = "rx", "tx"; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <42 41>; status = "disabled"; @@ -214,15 +297,36 @@ compatible = "fsl,imx6q-ssi","fsl,imx21-ssi"; reg = <0x02030000 0x4000>; interrupts = <0 48 0x04>; - clocks = <&clks 180>; + clocks = <&clks 180>, <&clks 159>; + clock-names = "ipg", "baud"; + dmas = <&sdma 45 1 0>, + <&sdma 46 1 0>; + dma-names = "rx", "tx"; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <46 45>; status = "disabled"; }; asrc: asrc@02034000 { + compatible = "fsl,imx53-asrc"; reg = <0x02034000 0x4000>; interrupts = <0 50 0x04>; + clocks = <&clks 107>, <&clks 156>; + clock-names = "core", "dma"; + dmas = <&sdma 17 20 1>, <&sdma 18 20 1>, <&sdma 19 20 1>, + <&sdma 20 20 1>, <&sdma 21 20 1>, <&sdma 22 20 1>; + dma-names = "rxa", "rxb", "rxc", + "txa", "txb", "txc"; + status = "okay"; + }; + + asrc_p2p: asrc_p2p { + compatible = "fsl,imx6q-asrc-p2p"; + fsl,output-rate = <48000>; + fsl,output-width = <16>; + fsl,asrc-dma-rx-events = <17 18 19>; + fsl,asrc-dma-tx-events = <20 21 22>; + status = "okay"; }; spba@0203c000 { @@ -231,8 +335,18 @@ }; vpu: vpu@02040000 { + compatible = "fsl,imx6-vpu"; reg = <0x02040000 0x3c000>; - interrupts = <0 3 0x04 0 12 0x04>; + reg-names = "vpu_regs"; + interrupts = <0 3 0x01>, <0 12 0x04>; + interrupt-names = "vpu_jpu_irq", "vpu_ipi_irq"; + clocks = <&clks 168>, <&clks 140>, <&clks 142>; + clock-names = "vpu_clk", "mmdc_ch0_axi", "ocram"; + iramsize = <0x21000>; + iram = <&ocram>; + resets = <&src 1>; + pu-supply = <®_pu>; + status = "disabled"; }; aipstz@0207c000 { /* AIPSTZ1 */ @@ -275,18 +389,28 @@ clock-names = "ipg", "per"; }; - can1: flexcan@02090000 { + flexcan1: flexcan@02090000 { + compatible = "fsl,imx6q-flexcan"; reg = <0x02090000 0x4000>; interrupts = <0 110 0x04>; + clocks = <&clks 108>, <&clks 109>; + clock-names = "ipg", "per"; + gpr = <&gpr>; + status = "disabled"; }; - can2: flexcan@02094000 { + flexcan2: flexcan@02094000 { + compatible = "fsl,imx6q-flexcan"; reg = <0x02094000 0x4000>; interrupts = <0 111 0x04>; + clocks = <&clks 110>, <&clks 111>; + clock-names = "ipg", "per"; + gpr = <&gpr>; + status = "disabled"; }; gpt: gpt@02098000 { - compatible = "fsl,imx6q-gpt"; + compatible = "fsl,imx6q-gpt", "fsl,imx31-gpt"; reg = <0x02098000 0x4000>; interrupts = <0 55 0x04>; clocks = <&clks 119>, <&clks 120>; @@ -459,7 +583,6 @@ regulator-name = "vddpu"; regulator-min-microvolt = <725000>; regulator-max-microvolt = <1450000>; - regulator-always-on; anatop-reg-offset = <0x140>; anatop-vol-bit-shift = <9>; anatop-vol-bit-width = <5>; @@ -489,11 +612,20 @@ }; }; + tempmon: tempmon { + compatible = "fsl,imx6q-tempmon"; + interrupts = <0 49 0x04>; + fsl,tempmon = <&anatop>; + fsl,tempmon-data = <&ocotp>; + clocks = <&clks 172>; + }; + usbphy1: usbphy@020c9000 { compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; reg = <0x020c9000 0x1000>; interrupts = <0 44 0x04>; clocks = <&clks 182>; + fsl,anatop = <&anatop>; }; usbphy2: usbphy@020ca000 { @@ -501,6 +633,12 @@ reg = <0x020ca000 0x1000>; interrupts = <0 45 0x04>; clocks = <&clks 183>; + fsl,anatop = <&anatop>; + }; + + caam_snvs: caam-snvs@020cc000 { + compatible = "fsl,imx6q-caam-snvs"; + reg = <0x020cc000 0x4000>; }; snvs@020cc000 { @@ -537,6 +675,11 @@ compatible = "fsl,imx6q-gpc"; reg = <0x020dc000 0x4000>; interrupts = <0 89 0x04 0 90 0x04>; + clocks = <&clks 122>, <&clks 74>, <&clks 121>, + <&clks 26>, <&clks 143>, <&clks 168>, <&clks 62>; + clock-names = "gpu3d_core", "gpu3d_shader", "gpu2d_core", + "gpu2d_axi", "openvg_axi", "vpu_axi", "ipg"; + pu-supply = <®_pu>; }; gpr: iomuxc-gpr@020e0000 { @@ -544,24 +687,775 @@ reg = <0x020e0000 0x38>; }; + iomuxc: iomuxc@020e0000 { + compatible = "fsl,imx6dl-iomuxc", "fsl,imx6q-iomuxc"; + reg = <0x020e0000 0x4000>; + + audmux { + pinctrl_audmux_1: audmux-1 { + fsl,pins = < + MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x130b0 + MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x130b0 + MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x130b0 + MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x130b0 + >; + }; + + pinctrl_audmux_2: audmux-2 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0 + MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0 + MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x130b0 + MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0 + >; + }; + + pinctrl_audmux_3: audmux-3 { + fsl,pins = < + MX6QDL_PAD_DISP0_DAT16__AUD5_TXC 0x130b0 + MX6QDL_PAD_DISP0_DAT18__AUD5_TXFS 0x130b0 + MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x130b0 + >; + }; + }; + + ecspi1 { + pinctrl_ecspi1_1: ecspi1grp-1 { + fsl,pins = < + MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 + >; + }; + + pinctrl_ecspi1_2: ecspi1grp-2 { + fsl,pins = < + MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1 + >; + }; + }; + + ecspi3 { + pinctrl_ecspi3_1: ecspi3grp-1 { + fsl,pins = < + MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO 0x100b1 + MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI 0x100b1 + MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK 0x100b1 + >; + }; + }; + + enet { + pinctrl_enet_1: enetgrp-1 { + fsl,pins = < + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 + >; + }; + + pinctrl_enet_2: enetgrp-2 { + fsl,pins = < + MX6QDL_PAD_KEY_COL1__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_KEY_COL2__ENET_MDC 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 + >; + }; + + pinctrl_enet_3: enetgrp-3 { + fsl,pins = < + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0 + >; + }; + }; + + esai { + pinctrl_esai_1: esaigrp-1 { + fsl,pins = < + MX6QDL_PAD_ENET_RXD0__ESAI_TX_HF_CLK 0x1b030 + MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1b030 + MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1b030 + MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1b030 + MX6QDL_PAD_ENET_TXD1__ESAI_TX2_RX3 0x1b030 + MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1b030 + MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1b030 + MX6QDL_PAD_NANDF_CS2__ESAI_TX0 0x1b030 + MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x1b030 + >; + }; + + pinctrl_esai_2: esaigrp-2 { + fsl,pins = < + MX6QDL_PAD_ENET_CRS_DV__ESAI_TX_CLK 0x1b030 + MX6QDL_PAD_ENET_RXD1__ESAI_TX_FS 0x1b030 + MX6QDL_PAD_ENET_TX_EN__ESAI_TX3_RX2 0x1b030 + MX6QDL_PAD_GPIO_5__ESAI_TX2_RX3 0x1b030 + MX6QDL_PAD_ENET_TXD0__ESAI_TX4_RX1 0x1b030 + MX6QDL_PAD_ENET_MDC__ESAI_TX5_RX0 0x1b030 + MX6QDL_PAD_GPIO_17__ESAI_TX0 0x1b030 + MX6QDL_PAD_NANDF_CS3__ESAI_TX1 0x1b030 + MX6QDL_PAD_ENET_MDIO__ESAI_RX_CLK 0x1b030 + MX6QDL_PAD_GPIO_9__ESAI_RX_FS 0x1b030 + >; + }; + }; + + flexcan1 { + pinctrl_flexcan1_1: flexcan1grp-1 { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000 + MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x80000000 + >; + }; + + pinctrl_flexcan1_2: flexcan1grp-2 { + fsl,pins = < + MX6QDL_PAD_GPIO_7__FLEXCAN1_TX 0x80000000 + MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000 + >; + }; + }; + + flexcan2 { + pinctrl_flexcan2_1: flexcan2grp-1 { + fsl,pins = < + MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x80000000 + MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x80000000 + >; + }; + }; + + gpmi-nand { + pinctrl_gpmi_nand_1: gpmi-nand-1 { + fsl,pins = < + MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 + MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 + MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 + MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 + MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 + MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 + MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 + MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 + MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 + MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 + MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 + MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 + MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 + MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 + MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 + MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 + MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 + >; + }; + }; + + hdmi_hdcp { + pinctrl_hdmi_hdcp_1: hdmihdcpgrp-1 { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1 + >; + }; + + pinctrl_hdmi_hdcp_2: hdmihdcpgrp-2 { + fsl,pins = < + MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D16__HDMI_TX_DDC_SDA 0x4001b8b1 + >; + }; + + pinctrl_hdmi_hdcp_3: hdmihdcpgrp-3 { + fsl,pins = < + MX6QDL_PAD_EIM_EB2__HDMI_TX_DDC_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1 + >; + }; + }; + + hdmi_cec { + pinctrl_hdmi_cec_1: hdmicecgrp-1 { + fsl,pins = < + MX6QDL_PAD_EIM_A25__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + + pinctrl_hdmi_cec_2: hdmicecgrp-2 { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + }; + + i2c1 { + pinctrl_i2c1_1: i2c1grp-1 { + fsl,pins = < + MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c1_2: i2c1grp-2 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1 + MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1 + >; + }; + }; + + i2c2 { + pinctrl_i2c2_1: i2c2grp-1 { + fsl,pins = < + MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D16__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c2_2: i2c2grp-2 { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c2_3: i2c2grp-3 { + fsl,pins = < + MX6QDL_PAD_EIM_EB2__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + }; + + i2c3 { + pinctrl_i2c3_1: i2c3grp-1 { + fsl,pins = < + MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c3_2: i2c3grp-2 { + fsl,pins = < + MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c3_3: i2c3grp-3 { + fsl,pins = < + MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_16__I2C3_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c3_4: i2c3grp-4 { + fsl,pins = < + MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 + >; + }; + }; + + ipu1 { + pinctrl_ipu1_1: ipu1grp-1 { + fsl,pins = < + MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x10 + MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x10 + MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x10 + MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x10 + MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x80000000 + MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x10 + MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x10 + MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x10 + MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x10 + MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x10 + MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x10 + MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x10 + MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x10 + MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x10 + MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x10 + MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x10 + MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x10 + MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x10 + MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x10 + MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x10 + MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x10 + MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x10 + MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x10 + MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x10 + MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x10 + MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x10 + MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x10 + MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x10 + MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x10 + >; + }; + + pinctrl_ipu1_2: ipu1grp-2 { /* parallel camera */ + fsl,pins = < + MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x80000000 + MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x80000000 + MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x80000000 + MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x80000000 + MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x80000000 + MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x80000000 + MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x80000000 + MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x80000000 + MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x80000000 + MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000 + MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x80000000 + MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x80000000 + >; + }; + + pinctrl_ipu1_3: ipu1grp-3 { /* parallel port 16-bit */ + fsl,pins = < + MX6QDL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04 0x80000000 + MX6QDL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05 0x80000000 + MX6QDL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06 0x80000000 + MX6QDL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07 0x80000000 + MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08 0x80000000 + MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09 0x80000000 + MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10 0x80000000 + MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11 0x80000000 + MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x80000000 + MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x80000000 + MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x80000000 + MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x80000000 + MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x80000000 + MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x80000000 + MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x80000000 + MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x80000000 + MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000 + MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x80000000 + MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x80000000 + >; + }; + }; + + mlb { + pinctrl_mlb_1: mlbgrp-1 { + fsl,pins = < + MX6QDL_PAD_GPIO_3__MLB_CLK 0x71 + MX6QDL_PAD_GPIO_6__MLB_SIG 0x71 + MX6QDL_PAD_GPIO_2__MLB_DATA 0x71 + >; + }; + + pinctrl_mlb_2: mlbgrp-2 { + fsl,pins = < + MX6QDL_PAD_ENET_TXD1__MLB_CLK 0x80000000 + MX6QDL_PAD_GPIO_6__MLB_SIG 0x80000000 + MX6QDL_PAD_GPIO_2__MLB_DATA 0x80000000 + >; + }; + }; + + pwm1 { + pinctrl_pwm1_1: pwm1grp-1 { + fsl,pins = < + MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1 + >; + }; + }; + + pwm3 { + pinctrl_pwm3_1: pwm3grp-1 { + fsl,pins = < + MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1 + >; + }; + }; + + spdif { + pinctrl_spdif_1: spdifgrp-1 { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0 + >; + }; + + pinctrl_spdif_2: spdifgrp-2 { + fsl,pins = < + MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 + MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x1b0b0 + >; + }; + + pinctrl_spdif_3: spdifgrp-3 { + fsl,pins = < + MX6QDL_PAD_ENET_RXD0__SPDIF_OUT 0x1b0b0 + >; + }; + }; + + uart1 { + pinctrl_uart1_1: uart1grp-1 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 + >; + }; + }; + + uart2 { + pinctrl_uart2_1: uart2grp-1 { + fsl,pins = < + MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_uart2_2: uart2grp-2 { /* DTE mode */ + fsl,pins = < + MX6QDL_PAD_EIM_D26__UART2_RX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D27__UART2_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D28__UART2_DTE_CTS_B 0x1b0b1 + MX6QDL_PAD_EIM_D29__UART2_DTE_RTS_B 0x1b0b1 + >; + }; + }; + + uart3 { + pinctrl_uart3_1: uart3grp-1 { + fsl,pins = < + MX6QDL_PAD_SD4_CLK__UART3_RX_DATA 0x1b0b1 + MX6QDL_PAD_SD4_CMD__UART3_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D30__UART3_CTS_B 0x1b0b1 + MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1 + >; + }; + + pinctrl_uart3_2: uart3grp-2 { + fsl,pins = < + MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D23__UART3_CTS_B 0x1b0b1 + MX6QDL_PAD_EIM_EB3__UART3_RTS_B 0x1b0b1 + >; + }; + }; + + uart4 { + pinctrl_uart4_1: uart4grp-1 { + fsl,pins = < + MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 + >; + }; + }; + + usbotg { + pinctrl_usbotg_1: usbotggrp-1 { + fsl,pins = < + MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 + >; + }; + + pinctrl_usbotg_2: usbotggrp-2 { + fsl,pins = < + MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 + >; + }; + }; + + usbh2 { + pinctrl_usbh2_1: usbh2grp-1 { + fsl,pins = < + MX6QDL_PAD_RGMII_TXC__USB_H2_DATA 0x40013030 + MX6QDL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x40013030 + >; + }; + + pinctrl_usbh2_2: usbh2grp-2 { + fsl,pins = < + MX6QDL_PAD_RGMII_TX_CTL__USB_H2_STROBE 0x40017030 + >; + }; + }; + + usbh3 { + pinctrl_usbh3_1: usbh3grp-1 { + fsl,pins = < + MX6QDL_PAD_RGMII_RX_CTL__USB_H3_DATA 0x40013030 + MX6QDL_PAD_RGMII_RXC__USB_H3_STROBE 0x40013030 + >; + }; + + pinctrl_usbh3_2: usbh3grp-2 { + fsl,pins = < + MX6QDL_PAD_RGMII_RXC__USB_H3_STROBE 0x40017030 + >; + }; + }; + + usdhc1 { + pinctrl_usdhc1_1: usdhc1grp-1 { + fsl,pins = < + MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 + MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 + MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 + MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 + MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 + MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 + MX6QDL_PAD_NANDF_D0__SD1_DATA4 0x17059 + MX6QDL_PAD_NANDF_D1__SD1_DATA5 0x17059 + MX6QDL_PAD_NANDF_D2__SD1_DATA6 0x17059 + MX6QDL_PAD_NANDF_D3__SD1_DATA7 0x17059 + >; + }; + + pinctrl_usdhc1_2: usdhc1grp-2 { + fsl,pins = < + MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 + MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 + MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 + MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 + MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 + MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 + >; + }; + }; + + usdhc2 { + pinctrl_usdhc2_1: usdhc2grp-1 { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 + MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x17059 + MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x17059 + MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x17059 + MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x17059 + >; + }; + + pinctrl_usdhc2_2: usdhc2grp-2 { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 + >; + }; + }; + + usdhc3 { + pinctrl_usdhc3_1: usdhc3grp-1 { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059 + MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059 + MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 + MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 + >; + }; + + pinctrl_usdhc3_1_100mhz: usdhc3grp-1-100mhz { /* 100Mhz */ + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170b9 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100b9 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170b9 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170b9 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170b9 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170b9 + MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170b9 + MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170b9 + MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170b9 + MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170b9 + >; + }; + + pinctrl_usdhc3_1_200mhz: usdhc3grp-1-200mhz { /* 200Mhz */ + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170f9 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100f9 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170f9 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170f9 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170f9 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170f9 + MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x170f9 + MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x170f9 + MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x170f9 + MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x170f9 + >; + }; + + pinctrl_usdhc3_2: usdhc3grp-2 { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + >; + }; + }; + + usdhc4 { + pinctrl_usdhc4_1: usdhc4grp-1 { + fsl,pins = < + MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 + MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 + MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 + MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 + MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 + MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 + MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 + MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 + MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 + MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 + >; + }; + + pinctrl_usdhc4_2: usdhc4grp-2 { + fsl,pins = < + MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 + MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 + MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 + MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 + MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 + MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 + >; + }; + }; + + weim { + pinctrl_weim_cs0_1: weim_cs0grp-1 { + fsl,pins = < + MX6QDL_PAD_EIM_CS0__EIM_CS0_B 0xb0b1 + >; + }; + + pinctrl_weim_nor_1: weim_norgrp-1 { + fsl,pins = < + MX6QDL_PAD_EIM_OE__EIM_OE_B 0xb0b1 + MX6QDL_PAD_EIM_RW__EIM_RW 0xb0b1 + MX6QDL_PAD_EIM_WAIT__EIM_WAIT_B 0xb060 + /* data */ + MX6QDL_PAD_EIM_D16__EIM_DATA16 0x1b0b0 + MX6QDL_PAD_EIM_D17__EIM_DATA17 0x1b0b0 + MX6QDL_PAD_EIM_D18__EIM_DATA18 0x1b0b0 + MX6QDL_PAD_EIM_D19__EIM_DATA19 0x1b0b0 + MX6QDL_PAD_EIM_D20__EIM_DATA20 0x1b0b0 + MX6QDL_PAD_EIM_D21__EIM_DATA21 0x1b0b0 + MX6QDL_PAD_EIM_D22__EIM_DATA22 0x1b0b0 + MX6QDL_PAD_EIM_D23__EIM_DATA23 0x1b0b0 + MX6QDL_PAD_EIM_D24__EIM_DATA24 0x1b0b0 + MX6QDL_PAD_EIM_D25__EIM_DATA25 0x1b0b0 + MX6QDL_PAD_EIM_D26__EIM_DATA26 0x1b0b0 + MX6QDL_PAD_EIM_D27__EIM_DATA27 0x1b0b0 + MX6QDL_PAD_EIM_D28__EIM_DATA28 0x1b0b0 + MX6QDL_PAD_EIM_D29__EIM_DATA29 0x1b0b0 + MX6QDL_PAD_EIM_D30__EIM_DATA30 0x1b0b0 + MX6QDL_PAD_EIM_D31__EIM_DATA31 0x1b0b0 + /* address */ + MX6QDL_PAD_EIM_A23__EIM_ADDR23 0xb0b1 + MX6QDL_PAD_EIM_A22__EIM_ADDR22 0xb0b1 + MX6QDL_PAD_EIM_A21__EIM_ADDR21 0xb0b1 + MX6QDL_PAD_EIM_A20__EIM_ADDR20 0xb0b1 + MX6QDL_PAD_EIM_A19__EIM_ADDR19 0xb0b1 + MX6QDL_PAD_EIM_A18__EIM_ADDR18 0xb0b1 + MX6QDL_PAD_EIM_A17__EIM_ADDR17 0xb0b1 + MX6QDL_PAD_EIM_A16__EIM_ADDR16 0xb0b1 + MX6QDL_PAD_EIM_DA15__EIM_AD15 0xb0b1 + MX6QDL_PAD_EIM_DA14__EIM_AD14 0xb0b1 + MX6QDL_PAD_EIM_DA13__EIM_AD13 0xb0b1 + MX6QDL_PAD_EIM_DA12__EIM_AD12 0xb0b1 + MX6QDL_PAD_EIM_DA11__EIM_AD11 0xb0b1 + MX6QDL_PAD_EIM_DA10__EIM_AD10 0xb0b1 + MX6QDL_PAD_EIM_DA9__EIM_AD09 0xb0b1 + MX6QDL_PAD_EIM_DA8__EIM_AD08 0xb0b1 + MX6QDL_PAD_EIM_DA7__EIM_AD07 0xb0b1 + MX6QDL_PAD_EIM_DA6__EIM_AD06 0xb0b1 + MX6QDL_PAD_EIM_DA5__EIM_AD05 0xb0b1 + MX6QDL_PAD_EIM_DA4__EIM_AD04 0xb0b1 + MX6QDL_PAD_EIM_DA3__EIM_AD03 0xb0b1 + MX6QDL_PAD_EIM_DA2__EIM_AD02 0xb0b1 + MX6QDL_PAD_EIM_DA1__EIM_AD01 0xb0b1 + MX6QDL_PAD_EIM_DA0__EIM_AD00 0xb0b1 + >; + }; + }; + }; + ldb: ldb@020e0008 { - #address-cells = <1>; - #size-cells = <0>; compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb"; - gpr = <&gpr>; + reg = <0x020e0000 0x4000>; + clocks = <&clks 135>, <&clks 136>, + <&clks 39>, <&clks 40>, + <&clks 41>, <&clks 42>, + <&clks 184>, <&clks 185>, + <&clks 205>, <&clks 206>, + <&clks 207>, <&clks 208>; + clock-names = "ldb_di0", "ldb_di1", + "ipu1_di0_sel", "ipu1_di1_sel", + "ipu2_di0_sel", "ipu2_di1_sel", + "di0_div_3_5", "di1_div_3_5", + "di0_div_7", "di1_div_7", + "di0_div_sel", "di1_div_sel"; status = "disabled"; + }; - lvds-channel@0 { - reg = <0>; - crtcs = <&ipu1 0>; - status = "disabled"; - }; - - lvds-channel@1 { - reg = <1>; - crtcs = <&ipu1 1>; - status = "disabled"; - }; + hdmi: hdmi@0120000 { + reg = <0x00120000 0x9000>; + interrupts = <0 115 0x04>; + gpr = <&gpr>; + clocks = <&clks 123>, <&clks 124>; + clock-names = "iahb", "isfr"; + status = "disabled"; }; dcic1: dcic@020e4000 { @@ -580,6 +1474,8 @@ interrupts = <0 2 0x04>; clocks = <&clks 155>, <&clks 155>; clock-names = "ipg", "ahb"; + #dma-cells = <3>; + iram = <&ocram>; fsl,sdma-ram-script-name = "imx/sdma/sdma-imx6q.bin"; }; }; @@ -591,9 +1487,29 @@ reg = <0x02100000 0x100000>; ranges; - caam@02100000 { - reg = <0x02100000 0x40000>; - interrupts = <0 105 0x04 0 106 0x04>; + crypto: caam@2100000 { + compatible = "fsl,sec-v4.0"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2100000 0x40000>; + ranges = <0 0x2100000 0x40000>; + interrupt-parent = <&intc>; /* interrupts = <0 92 0x4>; */ + clocks = <&clks 213>, <&clks 214>, <&clks 215>; + clock-names = "caam_mem", "caam_aclk", "caam_ipg"; + + sec_jr0: jr0@1000 { + compatible = "fsl,sec-v4.0-job-ring"; + reg = <0x1000 0x1000>; + interrupt-parent = <&intc>; + interrupts = <0 105 0x4>; + }; + + sec_jr1: jr1@2000 { + compatible = "fsl,sec-v4.0-job-ring"; + reg = <0x2000 0x1000>; + interrupt-parent = <&intc>; + interrupts = <0 106 0x4>; + }; }; aipstz@0217c000 { /* AIPSTZ2 */ @@ -607,6 +1523,7 @@ clocks = <&clks 162>; fsl,usbphy = <&usbphy1>; fsl,usbmisc = <&usbmisc 0>; + fsl,anatop = <&anatop>; status = "disabled"; }; @@ -638,7 +1555,7 @@ status = "disabled"; }; - usbmisc: usbmisc: usbmisc@02184800 { + usbmisc: usbmisc@02184800 { #index-cells = <1>; compatible = "fsl,imx6q-usbmisc"; reg = <0x02184800 0x200>; @@ -654,9 +1571,14 @@ status = "disabled"; }; - mlb@0218c000 { + mlb: mlb@0218c000 { + compatible = "fsl,imx6q-mlb150"; reg = <0x0218c000 0x4000>; interrupts = <0 53 0x04 0 117 0x04 0 126 0x04>; + clocks = <&clks 139>, <&clks 175>; + clock-names = "mlb", "pll8_mlb"; + iram = <&ocram>; + status = "disabled"; }; usdhc1: usdhc@02190000 { @@ -733,6 +1655,11 @@ reg = <0x021ac000 0x4000>; }; + mmdc0-1@021b0000 { /* MMDC0-1 */ + compatible = "fsl,imx6q-mmdc-combine"; + reg = <0x021b0000 0x8000>; + }; + mmdc0: mmdc@021b0000 { /* MMDC0 */ compatible = "fsl,imx6q-mmdc"; reg = <0x021b0000 0x4000>; @@ -742,20 +1669,23 @@ reg = <0x021b4000 0x4000>; }; - weim@021b8000 { + weim: weim@021b8000 { + compatible = "fsl,imx6q-weim"; reg = <0x021b8000 0x4000>; interrupts = <0 14 0x04>; + clocks = <&clks 196>; }; - ocotp@021bc000 { - compatible = "fsl,imx6q-ocotp"; + ocotp: ocotp-ctrl@021bc000 { + compatible = "syscon"; reg = <0x021bc000 0x4000>; }; - ocotp@021c0000 { - reg = <0x021c0000 0x4000>; - interrupts = <0 21 0x04>; - }; + ocotp-fuse@021bc000 { + compatible = "fsl,imx6q-ocotp"; + reg = <0x021bc000 0x4000>; + clocks = <&clks 128>; + }; tzasc@021d0000 { /* TZASC1 */ reg = <0x021d0000 0x4000>; @@ -773,17 +1703,26 @@ status = "disabled"; }; - mipi@021dc000 { /* MIPI-CSI */ + mipi_csi: mipi_csi@021dc000 { + compatible = "fsl,imx6q-mipi-csi2"; reg = <0x021dc000 0x4000>; - }; - - mipi@021e0000 { /* MIPI-DSI */ - reg = <0x021e0000 0x4000>; + interrupts = <0 100 0x04>, <0 101 0x04>; + clocks = <&clks 138>, <&clks 53>; + /* Note: clks 138 is hsi_tx, however, the dphy_c + * hsi_tx and pll_refclk use the same clk gate. + * In current clk driver, open/close clk gate do + * use hsi_tx for a temporary debug purpose. + */ + clock-names = "dphy_clk", "pixel_clk"; + status = "disabled"; }; vdoa@021e4000 { + compatible = "fsl,imx6q-vdoa"; reg = <0x021e4000 0x4000>; interrupts = <0 18 0x04>; + clocks = <&clks 202>; + iram = <&ocram>; }; uart2: serial@021e8000 { @@ -792,6 +1731,8 @@ interrupts = <0 27 0x04>; clocks = <&clks 160>, <&clks 161>; clock-names = "ipg", "per"; + dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -801,6 +1742,8 @@ interrupts = <0 28 0x04>; clocks = <&clks 160>, <&clks 161>; clock-names = "ipg", "per"; + dmas = <&sdma 29 4 0>, <&sdma 30 4 0>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -810,6 +1753,8 @@ interrupts = <0 29 0x04>; clocks = <&clks 160>, <&clks 161>; clock-names = "ipg", "per"; + dmas = <&sdma 31 4 0>, <&sdma 32 4 0>; + dma-names = "rx", "tx"; status = "disabled"; }; @@ -819,18 +1764,24 @@ interrupts = <0 30 0x04>; clocks = <&clks 160>, <&clks 161>; clock-names = "ipg", "per"; + dmas = <&sdma 33 4 0>, <&sdma 34 4 0>; + dma-names = "rx", "tx"; status = "disabled"; }; }; ipu1: ipu@02400000 { - #crtc-cells = <1>; compatible = "fsl,imx6q-ipu"; reg = <0x02400000 0x400000>; interrupts = <0 6 0x4 0 5 0x4>; - clocks = <&clks 130>, <&clks 131>, <&clks 132>; - clock-names = "bus", "di0", "di1"; + clocks = <&clks 130>, <&clks 131>, <&clks 132>, + <&clks 39>, <&clks 40>, + <&clks 135>, <&clks 136>; + clock-names = "bus", "di0", "di1", + "di0_sel", "di1_sel", + "ldb_di0", "ldb_di1"; resets = <&src 2>; + bypass_reset = <0>; }; }; }; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6sl-evk-csi.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6sl-evk-csi.dts --- linux-3.10.30/arch/arm/boot/dts/imx6sl-evk-csi.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6sl-evk-csi.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6sl-evk.dts" + +/ { + csi_v4l2_cap { + status = "okay"; + }; +}; + +&csi { + status = "okay"; +}; + +&i2c3 { + status = "okay"; +}; + +&epdc { + status = "disabled"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6sl-evk-ldo.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6sl-evk-ldo.dts --- linux-3.10.30/arch/arm/boot/dts/imx6sl-evk-ldo.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6sl-evk-ldo.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include "imx6sl-evk.dts" + +&cpu0 { + arm-supply = <®_arm>; + soc-supply = <®_soc>; + pu-supply = <®_pu>; /* use pu_dummy if VDDSOC share with VDDPU */ +}; + +&gpc { + fsl,ldo-bypass = <0>; /* use ldo-bypass, u-boot will check it and configure */ + fsl,wdog-reset = <1>; /* watchdog select of reset source */ + pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; + +&gpu { + pu-supply = <®_pu>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6sl-evk.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6sl-evk.dts --- linux-3.10.30/arch/arm/boot/dts/imx6sl-evk.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6sl-evk.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,604 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +/dts-v1/; + +#include "imx6sl.dtsi" + +/ { + model = "Freescale i.MX6 SoloLite EVK Board"; + compatible = "fsl,imx6sl-evk", "fsl,imx6sl"; + + memory { + reg = <0x80000000 0x40000000>; + }; + + battery: max8903@0 { + compatible = "fsl,max8903-charger"; + pinctrl-names = "default"; + dok_input = <&gpio4 13 1>; + uok_input = <&gpio4 13 1>; + chg_input = <&gpio4 15 1>; + flt_input = <&gpio4 14 1>; + fsl,dcm_always_high; + fsl,dc_valid; + fsl,adc_disable; + status = "okay"; + }; + regulators { + compatible = "simple-bus"; + + reg_lcd_3v3: lcd-3v3 { + compatible = "regulator-fixed"; + regulator-name = "lcd-3v3"; + gpio = <&gpio4 3 0>; + enable-active-high; + }; + + reg_aud3v: wm8962_supply_3v15 { + compatible = "regulator-fixed"; + regulator-name = "wm8962-supply-3v15"; + regulator-min-microvolt = <3150000>; + regulator-max-microvolt = <3150000>; + regulator-boot-on; + }; + + reg_aud4v: wm8962_supply_4v2 { + compatible = "regulator-fixed"; + regulator-name = "wm8962-supply-4v2"; + regulator-min-microvolt = <4325000>; + regulator-max-microvolt = <4325000>; + regulator-boot-on; + }; + + reg_usb_otg1_vbus: usb_otg1_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_otg1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio4 0 0>; + enable-active-high; + }; + + reg_usb_otg2_vbus: usb_otg2_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_otg2_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio4 2 0>; + enable-active-high; + }; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm1 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <6>; + }; + + csi_v4l2_cap { + compatible = "fsl,imx6sl-csi-v4l2"; + status = "disabled"; + }; + + pxp_v4l2_out { + compatible = "fsl,imx6sl-pxp-v4l2"; + status = "okay"; + }; + + sound { + compatible = "fsl,imx6q-sabresd-wm8962", + "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + ssi-controller = <&ssi2>; + audio-codec = <&codec>; + audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "AMIC", "MICBIAS", + "IN3R", "AMIC"; + amic-mono; + mux-int-port = <2>; + mux-ext-port = <3>; + hp-det-gpios = <&gpio4 19 1>; + }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif", + "fsl,imx6sl-evk-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + }; + + sii902x_reset: sii902x-reset { + compatible = "gpio-reset"; + reset-gpios = <&gpio2 19 1>; + reset-delay-us = <100000>; + #reset-cells = <0>; + }; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux_1>; + status = "okay"; +}; + +&csi { + status = "disabled"; +}; + +&ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio4 11 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_1>; + status = "okay"; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25p32"; + spi-max-frequency = <20000000>; + reg = <0>; + }; +}; + +&epdc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_epdc_0>; + V3P3-supply = <&V3P3_reg>; + VCOM-supply = <&VCOM_reg>; + DISPLAY-supply = <&DISPLAY_reg>; + status = "okay"; +}; + +&cpu0 { + arm-supply = <&sw1a_reg>; + soc-supply = <&sw1c_reg>; + pu-supply = <&pu_dummy>; /* use pu_dummy if VDDSOC share with VDDPU */ +}; + +&fec { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_fec_1>; + pinctrl-1 = <&pinctrl_fec_1_sleep>; + phy-mode = "rmii"; + phy-reset-gpios = <&gpio4 21 0>; /* GPIO4_21 */ + phy-reset-duration = <1>; + status = "okay"; +}; + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1_1>; + status = "okay"; + + pmic: pfuze100@08 { + compatible = "fsl,pfuze100"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1ab { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw1c_reg: sw1c { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3a { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3b_reg: sw3b { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen4_reg: vgen4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen5_reg: vgen5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vgen6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; + + elan@10 { + compatible = "elan,elan-touch"; + reg = <0x10>; + interrupt-parent = <&gpio2>; + interrupts = <10 2>; + gpio_elan_cs = <&gpio2 9 0>; + gpio_elan_rst = <&gpio4 4 0>; + gpio_intr = <&gpio2 10 0>; + status = "okay"; + }; + + max17135@48 { + compatible = "maxim,max17135"; + reg = <0x48>; + vneg_pwrup = <1>; + gvee_pwrup = <2>; + vpos_pwrup = <10>; + gvdd_pwrup = <12>; + gvdd_pwrdn = <1>; + vpos_pwrdn = <2>; + gvee_pwrdn = <8>; + vneg_pwrdn = <10>; + gpio_pmic_pwrgood = <&gpio2 13 0>; + gpio_pmic_vcom_ctrl = <&gpio2 3 0>; + gpio_pmic_wakeup = <&gpio2 14 0>; + gpio_pmic_v3p3 = <&gpio2 7 0>; + gpio_pmic_intr = <&gpio2 12 0>; + + regulators { + DISPLAY_reg: DISPLAY { + regulator-name = "DISPLAY"; + }; + + GVDD_reg: GVDD { + /* 20v */ + regulator-name = "GVDD"; + }; + + GVEE_reg: GVEE { + /* -22v */ + regulator-name = "GVEE"; + }; + + HVINN_reg: HVINN { + /* -22v */ + regulator-name = "HVINN"; + }; + + HVINP_reg: HVINP { + /* 20v */ + regulator-name = "HVINP"; + }; + + VCOM_reg: VCOM { + regulator-name = "VCOM"; + /* 2's-compliment, -4325000 */ + regulator-min-microvolt = <0xffbe0178>; + /* 2's-compliment, -500000 */ + regulator-max-microvolt = <0xfff85ee0>; + }; + + VNEG_reg: VNEG { + /* -15v */ + regulator-name = "VNEG"; + }; + + VPOS_reg: VPOS { + /* 15v */ + regulator-name = "VPOS"; + }; + + V3P3_reg: V3P3 { + regulator-name = "V3P3"; + }; + }; + }; + + mma8450@1c { + compatible = "fsl,mma8450"; + reg = <0x1c>; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_1>; + status = "okay"; + + codec: wm8962@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&clks IMX6SL_CLK_EXTERN_AUDIO>; + DCVDD-supply = <&vgen3_reg>; + DBVDD-supply = <®_aud3v>; + AVDD-supply = <&vgen3_reg>; + CPVDD-supply = <&vgen3_reg>; + MICVDD-supply = <®_aud3v>; + PLLVDD-supply = <&vgen3_reg>; + SPKVDD1-supply = <®_aud4v>; + SPKVDD2-supply = <®_aud4v>; + amic-mono; + }; + + sii902x@39 { + compatible = "SiI,sii902x"; + interrupt-parent = <&gpio2>; + interrupts = <10 2>; + mode_str ="1280x720M@60"; + bits-per-pixel = <32>; + resets = <&sii902x_reset>; + reg = <0x39>; + }; +}; + +&i2c3 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3_1>; + status = "disabled"; + + ov564x: ov564x@3c { + compatible = "ovti,ov564x"; + reg = <0x3c>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_csi_0>; + clocks = <&clks IMX6SL_CLK_CSI>; + clock-names = "csi_mclk"; + AVDD-supply = <&vgen6_reg>; /* 2.8v */ + DVDD-supply = <&vgen2_reg>; /* 1.5v*/ + pwn-gpios = <&gpio1 25 1>; + rst-gpios = <&gpio1 26 0>; + csi_id = <0>; + mclk = <24000000>; + mclk_source = <0>; + }; +}; + +&iomuxc { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_hog>; + pinctrl-1 = <&pinctrl_hog_sleep>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6SL_PAD_KEY_ROW7__GPIO4_IO07 0x17059 + MX6SL_PAD_KEY_COL7__GPIO4_IO06 0x17059 + MX6SL_PAD_SD2_DAT7__GPIO5_IO00 0x17059 + MX6SL_PAD_SD2_DAT6__GPIO4_IO29 0x17059 + MX6SL_PAD_REF_CLK_32K__GPIO3_IO22 0x17059 + MX6SL_PAD_FEC_TX_CLK__GPIO4_IO21 0x80000000 + MX6SL_PAD_KEY_ROW5__GPIO4_IO03 0x110b0 + MX6SL_PAD_EPDC_VCOM0__GPIO2_IO03 0x80000000 + MX6SL_PAD_EPDC_PWRSTAT__GPIO2_IO13 0x80000000 + MX6SL_PAD_EPDC_PWRCTRL0__GPIO2_IO07 0x80000000 + MX6SL_PAD_EPDC_PWRWAKEUP__GPIO2_IO14 0x80000000 + MX6SL_PAD_EPDC_PWRINT__GPIO2_IO12 0x80000000 + MX6SL_PAD_EPDC_PWRCTRL3__GPIO2_IO10 0x170b0 + MX6SL_PAD_EPDC_PWRCTRL2__GPIO2_IO09 0x80000000 + MX6SL_PAD_KEY_COL6__GPIO4_IO04 0x110b0 + MX6SL_PAD_ECSPI2_MISO__GPIO4_IO14 0x17000 + MX6SL_PAD_ECSPI2_MOSI__GPIO4_IO13 0x17000 + MX6SL_PAD_ECSPI2_SS0__GPIO4_IO15 0x17000 + MX6SL_PAD_FEC_RX_ER__GPIO4_IO19 0x1b0b0 + MX6SL_PAD_LCD_RESET__GPIO2_IO19 0x1b0b0 + MX6SL_PAD_KEY_COL4__GPIO4_IO00 0x80000000 + MX6SL_PAD_KEY_COL5__GPIO4_IO02 0x80000000 + >; + }; + + pinctrl_hog_sleep: hoggrp_sleep { + fsl,pins = < + MX6SL_PAD_KEY_ROW5__GPIO4_IO03 0x3080 + MX6SL_PAD_KEY_COL6__GPIO4_IO04 0x3080 + MX6SL_PAD_LCD_RESET__GPIO2_IO19 0x3080 + >; + }; + }; +}; + +&kpp { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_kpp_1>; + pinctrl-1 = <&pinctrl_kpp_1_sleep>; + linux,keymap = < + 0x00000067 /* KEY_UP */ + 0x0001006c /* KEY_DOWN */ + 0x0002001c /* KEY_ENTER */ + 0x01000066 /* KEY_HOME */ + 0x0101006a /* KEY_RIGHT */ + 0x01020069 /* KEY_LEFT */ + 0x02000072 /* KEY_VOLUMEDOWN */ + 0x02010073 /* KEY_VOLUMEUP */ + >; + status = "okay"; +}; + +&lcdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcdif_dat_0 + &pinctrl_lcdif_ctrl_0>; + lcd-supply = <®_lcd_3v3>; + display = <&display>; + status = "okay"; + + display: display { + bits-per-pixel = <16>; + bus-width = <24>; + + display-timings { + native-mode = <&timing0>; + timing0: timing0 { + clock-frequency = <33500000>; + hactive = <800>; + vactive = <480>; + hback-porch = <89>; + hfront-porch = <164>; + vback-porch = <23>; + vfront-porch = <10>; + hsync-len = <10>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; +}; + +&pwm1 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_pwm1_0>; + pinctrl-1 = <&pinctrl_pwm1_0_sleep>; + status = "okay"; +}; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif_1>; + status = "okay"; +}; + +&ssi2 { + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_1>; + status = "okay"; +}; + +&usbotg1 { + vbus-supply = <®_usb_otg1_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg1_1>; + disable-over-current; + imx6-usb-charger-detection; + status = "okay"; +}; + +&usbotg2 { + vbus-supply = <®_usb_otg2_vbus>; + dr_mode = "host"; + disable-over-current; + status = "okay"; +}; + +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1_1>; + pinctrl-1 = <&pinctrl_usdhc1_1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_1_200mhz>; + bus-width = <8>; + cd-gpios = <&gpio4 7 0>; + wp-gpios = <&gpio4 6 0>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc2_1>; + pinctrl-1 = <&pinctrl_usdhc2_1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc2_1_200mhz>; + cd-gpios = <&gpio5 0 0>; + wp-gpios = <&gpio4 29 0>; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc3_1>; + pinctrl-1 = <&pinctrl_usdhc3_1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_1_200mhz>; + cd-gpios = <&gpio3 22 0>; + status = "okay"; +}; + +&pxp { + status = "okay"; +}; + +&gpc { + fsl,cpu_pupscr_sw2iso = <0xf>; + fsl,cpu_pupscr_sw = <0xf>; + fsl,cpu_pdnscr_iso2sw = <0x1>; + fsl,cpu_pdnscr_iso = <0x1>; + fsl,ldo-bypass = <1>; /* use ldo-bypass, u-boot will check it and configure */ + fsl,wdog-reset = <1>; /* watchdog select of reset source */ + pu-supply = <&pu_dummy>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; + +&gpu { + pu-supply = <&pu_dummy>; /* ldo-bypass:use pu_dummy if VDDSOC share with VDDPU */ +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/imx6sl.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6sl.dtsi --- linux-3.10.30/arch/arm/boot/dts/imx6sl.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/imx6sl.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,1255 @@ +/* + * Copyright 2013 Freescale Semiconductor, 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. + * + */ + +#include "skeleton.dtsi" +#include "imx6sl-pinfunc.h" +#include + +/ { + aliases { + serial0 = &uart1; + serial1 = &uart2; + serial2 = &uart3; + serial3 = &uart4; + serial4 = &uart5; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + gpio3 = &gpio4; + gpio4 = &gpio5; + usbphy0 = &usbphy1; + usbphy1 = &usbphy2; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-a9"; + device_type = "cpu"; + reg = <0x0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 996000 1275000 + 792000 1175000 + 396000 975000 + >; + fsl,soc-operating-points = < + /* ARM kHz SOC-PU uV */ + 996000 1225000 + 792000 1175000 + 396000 1175000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + clocks = <&clks IMX6SL_CLK_ARM>, <&clks IMX6SL_CLK_PLL2_PFD2>, + <&clks IMX6SL_CLK_STEP>, <&clks IMX6SL_CLK_PLL1_SW>, + <&clks IMX6SL_CLK_PLL1_SYS>; + clock-names = "arm", "pll2_pfd2_396m", "step", + "pll1_sw", "pll1_sys"; + arm-supply = <®_arm>; + pu-supply = <®_pu>; + soc-supply = <®_soc>; + }; + }; + + intc: interrupt-controller@00a01000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <1>; + #size-cells = <1>; + interrupt-controller; + reg = <0x00a01000 0x1000>, + <0x00a00100 0x100>; + }; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + ckil { + compatible = "fixed-clock"; + clock-frequency = <32768>; + }; + + osc { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + }; + }; + + pu_dummy: pudummy_reg { + compatible = "fsl,imx6-dummy-pureg"; /* only used in ldo-bypass */ + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&intc>; + ranges; + + busfreq { /* BUSFREQ */ + compatible = "fsl,imx6_busfreq"; + clocks = <&clks IMX6SL_CLK_PLL2_BUS>, <&clks IMX6SL_CLK_PLL2_PFD2>, + <&clks IMX6SL_CLK_PLL2_198M>, <&clks IMX6SL_CLK_ARM>, + <&clks IMX6SL_CLK_PLL3_USB_OTG>, <&clks IMX6SL_CLK_PERIPH>, + <&clks IMX6SL_CLK_PRE_PERIPH_SEL>, <&clks IMX6SL_CLK_PERIPH_CLK2>, + <&clks IMX6SL_CLK_PERIPH_CLK2_SEL>, <&clks IMX6SL_CLK_OSC>, + <&clks IMX6SL_CLK_PLL1_SYS>, <&clks IMX6SL_CLK_PERIPH2>, + <&clks IMX6SL_CLK_AHB>, <&clks IMX6SL_CLK_OCRAM>, + <&clks IMX6SL_CLK_PLL1_SW>, <&clks IMX6SL_CLK_PRE_PERIPH2_SEL>, + <&clks IMX6SL_CLK_PERIPH2_CLK2_SEL>, <&clks IMX6SL_CLK_PERIPH2_CLK2>, + <&clks IMX6SL_CLK_STEP>; + clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph", + "periph_pre", "periph_clk2", "periph_clk2_sel", "osc", "pll1_sys", "periph2", "ahb", "ocram", "pll1_sw", + "periph2_pre", "periph2_clk2_sel", "periph2_clk2", "step"; + fsl,max_ddr_freq = <400000000>; + }; + + L2: l2-cache@00a02000 { + compatible = "arm,pl310-cache"; + reg = <0x00a02000 0x1000>; + interrupts = <0 92 0x04>; + cache-unified; + cache-level = <2>; + arm,tag-latency = <4 2 3>; + arm,data-latency = <4 2 3>; + }; + + pmu { + compatible = "arm,cortex-a9-pmu"; + interrupts = <0 94 0x04>; + }; + + aips1: aips-bus@02000000 { + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x02000000 0x100000>; + ranges; + + spba: spba-bus@02000000 { + compatible = "fsl,spba-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x02000000 0x40000>; + ranges; + + spdif: spdif@02004000 { + compatible = "fsl,imx6sl-spdif", + "fsl,imx35-spdif"; + reg = <0x02004000 0x4000>; + interrupts = <0 52 0x04>; + dmas = <&sdma 14 18 0>, + <&sdma 15 18 0>; + dma-names = "rx", "tx"; + clocks = <&clks IMX6SL_CLK_SPDIF>, + <&clks IMX6SL_CLK_OSC>, + <&clks IMX6SL_CLK_SPDIF>, + <&clks 0>, <&clks 0>, <&clks 0>, + <&clks IMX6SL_CLK_IPG>, + <&clks 0>, <&clks 0>, + <&clks IMX6SL_CLK_SPBA>; + clock-names = "core", "rxtx0", + "rxtx1", "rxtx2", + "rxtx3", "rxtx4", + "rxtx5", "rxtx6", + "rxtx7", "dma"; + status = "disabled"; + }; + + ecspi1: ecspi@02008000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi"; + reg = <0x02008000 0x4000>; + interrupts = <0 31 0x04>; + clocks = <&clks IMX6SL_CLK_ECSPI1>, + <&clks IMX6SL_CLK_ECSPI1>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + ecspi2: ecspi@0200c000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi"; + reg = <0x0200c000 0x4000>; + interrupts = <0 32 0x04>; + clocks = <&clks IMX6SL_CLK_ECSPI2>, + <&clks IMX6SL_CLK_ECSPI2>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + ecspi3: ecspi@02010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi"; + reg = <0x02010000 0x4000>; + interrupts = <0 33 0x04>; + clocks = <&clks IMX6SL_CLK_ECSPI3>, + <&clks IMX6SL_CLK_ECSPI3>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + ecspi4: ecspi@02014000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6sl-ecspi", "fsl,imx51-ecspi"; + reg = <0x02014000 0x4000>; + interrupts = <0 34 0x04>; + clocks = <&clks IMX6SL_CLK_ECSPI4>, + <&clks IMX6SL_CLK_ECSPI4>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart5: serial@02018000 { + compatible = "fsl,imx6sl-uart", "fsl,imx21-uart"; + reg = <0x02018000 0x4000>; + interrupts = <0 30 0x04>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart1: serial@02020000 { + compatible = "fsl,imx6sl-uart", "fsl,imx21-uart"; + reg = <0x02020000 0x4000>; + interrupts = <0 26 0x04>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart2: serial@02024000 { + compatible = "fsl,imx6sl-uart", "fsl,imx21-uart"; + reg = <0x02024000 0x4000>; + interrupts = <0 27 0x04>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + ssi1: ssi@02028000 { + compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi"; + reg = <0x02028000 0x4000>; + interrupts = <0 46 0x04>; + clocks = <&clks IMX6SL_CLK_SSI1>, <&clks IMX6SL_CLK_SSI1>; + clock-names = "ipg", "baud"; + status = "disabled"; + }; + + ssi2: ssi@0202c000 { + compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi"; + reg = <0x0202c000 0x4000>; + interrupts = <0 47 0x04>; + clocks = <&clks IMX6SL_CLK_SSI2>, <&clks IMX6SL_CLK_SSI2>; + clock-names = "ipg", "baud"; + status = "disabled"; + }; + + ssi3: ssi@02030000 { + compatible = "fsl,imx6sl-ssi","fsl,imx21-ssi"; + reg = <0x02030000 0x4000>; + interrupts = <0 48 0x04>; + clocks = <&clks IMX6SL_CLK_SSI3>, <&clks IMX6SL_CLK_SSI3>; + clock-names = "ipg", "baud"; + status = "disabled"; + }; + + uart3: serial@02034000 { + compatible = "fsl,imx6sl-uart", "fsl,imx21-uart"; + reg = <0x02034000 0x4000>; + interrupts = <0 28 0x04>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart4: serial@02038000 { + compatible = "fsl,imx6sl-uart", "fsl,imx21-uart"; + reg = <0x02038000 0x4000>; + interrupts = <0 29 0x04>; + clocks = <&clks IMX6SL_CLK_UART>, + <&clks IMX6SL_CLK_UART_SERIAL>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + }; + + pwm1: pwm@02080000 { + #pwm-cells = <2>; + compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm"; + reg = <0x02080000 0x4000>; + interrupts = <0 83 0x04>; + clocks = <&clks IMX6SL_CLK_PWM1>, + <&clks IMX6SL_CLK_PWM1>; + clock-names = "ipg", "per"; + }; + + pwm2: pwm@02084000 { + #pwm-cells = <2>; + compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm"; + reg = <0x02084000 0x4000>; + interrupts = <0 84 0x04>; + clocks = <&clks IMX6SL_CLK_PWM2>, + <&clks IMX6SL_CLK_PWM2>; + clock-names = "ipg", "per"; + }; + + pwm3: pwm@02088000 { + #pwm-cells = <2>; + compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm"; + reg = <0x02088000 0x4000>; + interrupts = <0 85 0x04>; + clocks = <&clks IMX6SL_CLK_PWM3>, + <&clks IMX6SL_CLK_PWM3>; + clock-names = "ipg", "per"; + }; + + pwm4: pwm@0208c000 { + #pwm-cells = <2>; + compatible = "fsl,imx6sl-pwm", "fsl,imx27-pwm"; + reg = <0x0208c000 0x4000>; + interrupts = <0 86 0x04>; + clocks = <&clks IMX6SL_CLK_PWM4>, + <&clks IMX6SL_CLK_PWM4>; + clock-names = "ipg", "per"; + }; + + gpt: gpt@02098000 { + compatible = "fsl,imx6sl-gpt"; + reg = <0x02098000 0x4000>; + interrupts = <0 55 0x04>; + clocks = <&clks IMX6SL_CLK_GPT>, + <&clks IMX6SL_CLK_GPT_SERIAL>; + clock-names = "ipg", "per"; + }; + + gpio1: gpio@0209c000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x0209c000 0x4000>; + interrupts = <0 66 0x04 0 67 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio2: gpio@020a0000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x020a0000 0x4000>; + interrupts = <0 68 0x04 0 69 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio3: gpio@020a4000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x020a4000 0x4000>; + interrupts = <0 70 0x04 0 71 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio4: gpio@020a8000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x020a8000 0x4000>; + interrupts = <0 72 0x04 0 73 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio5: gpio@020ac000 { + compatible = "fsl,imx6sl-gpio", "fsl,imx35-gpio"; + reg = <0x020ac000 0x4000>; + interrupts = <0 74 0x04 0 75 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + kpp: kpp@020b8000 { + compatible = "fsl,imx6sl-kpp", "fsl,imx21-kpp"; + reg = <0x020b8000 0x4000>; + interrupts = <0 82 0x04>; + clocks = <&clks IMX6SL_CLK_DUMMY>; + }; + + wdog1: wdog@020bc000 { + compatible = "fsl,imx6sl-wdt", "fsl,imx21-wdt"; + reg = <0x020bc000 0x4000>; + interrupts = <0 80 0x04>; + clocks = <&clks IMX6SL_CLK_DUMMY>; + }; + + wdog2: wdog@020c0000 { + compatible = "fsl,imx6sl-wdt", "fsl,imx21-wdt"; + reg = <0x020c0000 0x4000>; + interrupts = <0 81 0x04>; + clocks = <&clks IMX6SL_CLK_DUMMY>; + status = "disabled"; + }; + + clks: ccm@020c4000 { + compatible = "fsl,imx6sl-ccm"; + reg = <0x020c4000 0x4000>; + interrupts = <0 87 0x04 0 88 0x04>; + #clock-cells = <1>; + }; + + anatop: anatop@020c8000 { + compatible = "fsl,imx6sl-anatop", + "fsl,imx6q-anatop", + "syscon", "simple-bus"; + reg = <0x020c8000 0x1000>; + interrupts = <0 49 0x04 0 54 0x04 0 127 0x04>; + + regulator-1p1@110 { + compatible = "fsl,anatop-regulator"; + regulator-name = "vdd1p1"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1375000>; + regulator-always-on; + anatop-reg-offset = <0x110>; + anatop-vol-bit-shift = <8>; + anatop-vol-bit-width = <5>; + anatop-min-bit-val = <4>; + anatop-min-voltage = <800000>; + anatop-max-voltage = <1375000>; + }; + + regulator-3p0@120 { + compatible = "fsl,anatop-regulator"; + regulator-name = "vdd3p0"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <3150000>; + regulator-always-on; + anatop-reg-offset = <0x120>; + anatop-vol-bit-shift = <8>; + anatop-vol-bit-width = <5>; + anatop-min-bit-val = <0>; + anatop-min-voltage = <2625000>; + anatop-max-voltage = <3400000>; + }; + + regulator-2p5@130 { + compatible = "fsl,anatop-regulator"; + regulator-name = "vdd2p5"; + regulator-min-microvolt = <2100000>; + regulator-max-microvolt = <2850000>; + regulator-always-on; + anatop-reg-offset = <0x130>; + anatop-vol-bit-shift = <8>; + anatop-vol-bit-width = <5>; + anatop-min-bit-val = <0>; + anatop-min-voltage = <2100000>; + anatop-max-voltage = <2850000>; + }; + + reg_arm: regulator-vddcore@140 { + compatible = "fsl,anatop-regulator"; + regulator-name = "cpu"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1450000>; + regulator-always-on; + anatop-reg-offset = <0x140>; + anatop-vol-bit-shift = <0>; + anatop-vol-bit-width = <5>; + anatop-delay-reg-offset = <0x170>; + anatop-delay-bit-shift = <24>; + anatop-delay-bit-width = <2>; + anatop-min-bit-val = <1>; + anatop-min-voltage = <725000>; + anatop-max-voltage = <1450000>; + }; + + reg_pu: regulator-vddpu@140 { + compatible = "fsl,anatop-regulator"; + regulator-name = "vddpu"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1450000>; + anatop-reg-offset = <0x140>; + anatop-vol-bit-shift = <9>; + anatop-vol-bit-width = <5>; + anatop-delay-reg-offset = <0x170>; + anatop-delay-bit-shift = <26>; + anatop-delay-bit-width = <2>; + anatop-min-bit-val = <1>; + anatop-min-voltage = <725000>; + anatop-max-voltage = <1450000>; + }; + + reg_soc: regulator-vddsoc@140 { + compatible = "fsl,anatop-regulator"; + regulator-name = "vddsoc"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1450000>; + regulator-always-on; + anatop-reg-offset = <0x140>; + anatop-vol-bit-shift = <18>; + anatop-vol-bit-width = <5>; + anatop-delay-reg-offset = <0x170>; + anatop-delay-bit-shift = <28>; + anatop-delay-bit-width = <2>; + anatop-min-bit-val = <1>; + anatop-min-voltage = <725000>; + anatop-max-voltage = <1450000>; + }; + }; + + tempmon: tempmon { + compatible = "fsl,imx6sl-tempmon", "fsl,imx6q-tempmon"; + interrupts = <0 49 0x04>; + fsl,tempmon = <&anatop>; + fsl,tempmon-data = <&ocotp>; + clocks = <&clks IMX6SL_CLK_PLL3_USB_OTG>; + }; + + usbphy1: usbphy@020c9000 { + compatible = "fsl,imx6sl-usbphy", "fsl,imx23-usbphy"; + reg = <0x020c9000 0x1000>; + interrupts = <0 44 0x04>; + clocks = <&clks IMX6SL_CLK_USBPHY1>; + fsl,anatop = <&anatop>; + }; + + usbphy2: usbphy@020ca000 { + compatible = "fsl,imx6sl-usbphy", "fsl,imx23-usbphy"; + reg = <0x020ca000 0x1000>; + interrupts = <0 45 0x04>; + clocks = <&clks IMX6SL_CLK_USBPHY2>; + fsl,anatop = <&anatop>; + }; + + snvs@020cc000 { + compatible = "fsl,sec-v4.0-mon", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x020cc000 0x4000>; + + snvs-rtc-lp@34 { + compatible = "fsl,sec-v4.0-mon-rtc-lp"; + reg = <0x34 0x58>; + interrupts = <0 19 0x04 0 20 0x04>; + }; + }; + + epit1: epit@020d0000 { + reg = <0x020d0000 0x4000>; + interrupts = <0 56 0x04>; + }; + + epit2: epit@020d4000 { + reg = <0x020d4000 0x4000>; + interrupts = <0 57 0x04>; + }; + + src: src@020d8000 { + compatible = "fsl,imx6sl-src", "fsl,imx51-src"; + reg = <0x020d8000 0x4000>; + interrupts = <0 91 0x04 0 96 0x04>; + #reset-cells = <1>; + }; + + ocram: sram@00900000 { + compatible = "mmio-sram"; + reg = <0x00900000 0x20000>; + clocks = <&clks IMX6SL_CLK_OCRAM>; + }; + + gpc: gpc@020dc000 { + compatible = "fsl,imx6sl-gpc", "fsl,imx6q-gpc"; + reg = <0x020dc000 0x4000>; + interrupts = <0 89 0x04>; + clocks = <&clks IMX6SL_CLK_GPU2D_PODF>, <&clks IMX6SL_CLK_GPU2D_OVG>, + <&clks IMX6SL_CLK_IPG>, <&clks IMX6SL_CLK_LCDIF_AXI>, + <&clks IMX6SL_CLK_LCDIF_PIX>, <&clks IMX6SL_CLK_EPDC_AXI>, + <&clks IMX6SL_CLK_EPDC_PIX>, <&clks IMX6SL_CLK_PXP_AXI>; + clock-names = "gpu2d_podf", "gpu2d_ovg", "ipg", "lcd_axi", + "lcd_pix", "epdc_axi", "epdc_pix", "pxp_axi"; + pu-supply = <®_pu>; + }; + + gpr: iomuxc-gpr@020e0000 { + compatible = "fsl,imx6sl-iomuxc-gpr", "syscon"; + reg = <0x020e0000 0x38>; + }; + + iomuxc: iomuxc@020e0000 { + compatible = "fsl,imx6sl-iomuxc"; + reg = <0x020e0000 0x4000>; + + audmux { + pinctrl_audmux_1: audmux-1 { + fsl,pins = < + MX6SL_PAD_AUD_RXD__AUD3_RXD 0x4130B0 + MX6SL_PAD_AUD_TXC__AUD3_TXC 0x4130B0 + MX6SL_PAD_AUD_TXD__AUD3_TXD 0x4110B0 + MX6SL_PAD_AUD_TXFS__AUD3_TXFS 0x4130B0 + MX6SL_PAD_AUD_MCLK__AUDIO_CLK_OUT 0x4130B0 + >; + }; + }; + + csi { + pinctrl_csi_0: csigrp-0 { + fsl,pins = < + MX6SL_PAD_EPDC_GDRL__CSI_MCLK 0x110b0 + MX6SL_PAD_EPDC_GDCLK__CSI_PIXCLK 0x110b0 + MX6SL_PAD_EPDC_GDSP__CSI_VSYNC 0x110b0 + MX6SL_PAD_EPDC_GDOE__CSI_HSYNC 0x110b0 + MX6SL_PAD_EPDC_SDLE__CSI_DATA09 0x110b0 + MX6SL_PAD_EPDC_SDCLK__CSI_DATA08 0x110b0 + MX6SL_PAD_EPDC_D7__CSI_DATA07 0x110b0 + MX6SL_PAD_EPDC_D6__CSI_DATA06 0x110b0 + MX6SL_PAD_EPDC_D5__CSI_DATA05 0x110b0 + MX6SL_PAD_EPDC_D4__CSI_DATA04 0x110b0 + MX6SL_PAD_EPDC_D3__CSI_DATA03 0x110b0 + MX6SL_PAD_EPDC_D2__CSI_DATA02 0x110b0 + MX6SL_PAD_EPDC_D1__CSI_DATA01 0x110b0 + MX6SL_PAD_EPDC_D0__CSI_DATA00 0x110b0 + MX6SL_PAD_EPDC_SDSHR__GPIO1_IO26 0x80000000 + MX6SL_PAD_EPDC_SDOE__GPIO1_IO25 0x80000000 + >; + }; + }; + + ecspi1 { + pinctrl_ecspi1_1: ecspi1grp-1 { + fsl,pins = < + MX6SL_PAD_ECSPI1_MISO__ECSPI1_MISO 0x100b1 + MX6SL_PAD_ECSPI1_MOSI__ECSPI1_MOSI 0x100b1 + MX6SL_PAD_ECSPI1_SCLK__ECSPI1_SCLK 0x100b1 + >; + }; + }; + + epdc { + pinctrl_epdc_0: epdcgrp-0 { + fsl,pins = < + MX6SL_PAD_EPDC_D0__EPDC_DATA00 0x80000000 + MX6SL_PAD_EPDC_D1__EPDC_DATA01 0x80000000 + MX6SL_PAD_EPDC_D2__EPDC_DATA02 0x80000000 + MX6SL_PAD_EPDC_D3__EPDC_DATA03 0x80000000 + MX6SL_PAD_EPDC_D4__EPDC_DATA04 0x80000000 + MX6SL_PAD_EPDC_D5__EPDC_DATA05 0x80000000 + MX6SL_PAD_EPDC_D6__EPDC_DATA06 0x80000000 + MX6SL_PAD_EPDC_D7__EPDC_DATA07 0x80000000 + MX6SL_PAD_EPDC_D8__EPDC_DATA08 0x80000000 + MX6SL_PAD_EPDC_D9__EPDC_DATA09 0x80000000 + MX6SL_PAD_EPDC_D10__EPDC_DATA10 0x80000000 + MX6SL_PAD_EPDC_D11__EPDC_DATA11 0x80000000 + MX6SL_PAD_EPDC_D12__EPDC_DATA12 0x80000000 + MX6SL_PAD_EPDC_D13__EPDC_DATA13 0x80000000 + MX6SL_PAD_EPDC_D14__EPDC_DATA14 0x80000000 + MX6SL_PAD_EPDC_D15__EPDC_DATA15 0x80000000 + MX6SL_PAD_EPDC_GDCLK__EPDC_GDCLK 0x80000000 + MX6SL_PAD_EPDC_GDSP__EPDC_GDSP 0x80000000 + MX6SL_PAD_EPDC_GDOE__EPDC_GDOE 0x80000000 + MX6SL_PAD_EPDC_GDRL__EPDC_GDRL 0x80000000 + MX6SL_PAD_EPDC_SDCLK__EPDC_SDCLK_P 0x80000000 + MX6SL_PAD_EPDC_SDOE__EPDC_SDOE 0x80000000 + MX6SL_PAD_EPDC_SDLE__EPDC_SDLE 0x80000000 + MX6SL_PAD_EPDC_SDSHR__EPDC_SDSHR 0x80000000 + MX6SL_PAD_EPDC_BDR0__EPDC_BDR0 0x80000000 + MX6SL_PAD_EPDC_SDCE0__EPDC_SDCE0 0x80000000 + MX6SL_PAD_EPDC_SDCE1__EPDC_SDCE1 0x80000000 + MX6SL_PAD_EPDC_SDCE2__EPDC_SDCE2 0x80000000 + >; + }; + }; + + fec { + pinctrl_fec_1: fecgrp-1 { + fsl,pins = < + MX6SL_PAD_FEC_MDC__FEC_MDC 0x1b0b0 + MX6SL_PAD_FEC_MDIO__FEC_MDIO 0x1b0b0 + MX6SL_PAD_FEC_CRS_DV__FEC_RX_DV 0x1b0b0 + MX6SL_PAD_FEC_RXD0__FEC_RX_DATA0 0x1b0b0 + MX6SL_PAD_FEC_RXD1__FEC_RX_DATA1 0x1b0b0 + MX6SL_PAD_FEC_TX_EN__FEC_TX_EN 0x1b0b0 + MX6SL_PAD_FEC_TXD0__FEC_TX_DATA0 0x1b0b0 + MX6SL_PAD_FEC_TXD1__FEC_TX_DATA1 0x1b0b0 + MX6SL_PAD_FEC_REF_CLK__FEC_REF_OUT 0x4001b0a8 + >; + }; + }; + + spdif { + pinctrl_spdif_1: spdifgrp-1 { + fsl,pins = < + MX6SL_PAD_SD2_DAT4__SPDIF_OUT 0x80000000 + >; + }; + }; + + uart1 { + pinctrl_uart1_1: uart1grp-1 { + fsl,pins = < + MX6SL_PAD_UART1_RXD__UART1_RX_DATA 0x1b0b1 + MX6SL_PAD_UART1_TXD__UART1_TX_DATA 0x1b0b1 + >; + }; + }; + + usbotg1 { + pinctrl_usbotg1_1: usbotg1grp-1 { + fsl,pins = < + MX6SL_PAD_EPDC_PWRCOM__USB_OTG1_ID 0x17059 + >; + }; + + pinctrl_usbotg1_2: usbotg1grp-2 { + fsl,pins = < + MX6SL_PAD_FEC_RXD0__USB_OTG1_ID 0x17059 + >; + }; + + pinctrl_usbotg1_3: usbotg1grp-3 { + fsl,pins = < + MX6SL_PAD_LCD_DAT1__USB_OTG1_ID 0x17059 + >; + }; + + pinctrl_usbotg1_4: usbotg1grp-4 { + fsl,pins = < + MX6SL_PAD_REF_CLK_32K__USB_OTG1_ID 0x17059 + >; + }; + + pinctrl_usbotg1_5: usbotg1grp-5 { + fsl,pins = < + MX6SL_PAD_SD3_DAT0__USB_OTG1_ID 0x17059 + >; + }; + }; + + usbotg2 { + pinctrl_usbotg2_1: usbotg2grp-1 { + fsl,pins = < + MX6SL_PAD_ECSPI1_SCLK__USB_OTG2_OC 0x17059 + >; + }; + + pinctrl_usbotg2_2: usbotg2grp-2 { + fsl,pins = < + MX6SL_PAD_ECSPI2_SCLK__USB_OTG2_OC 0x17059 + >; + }; + + pinctrl_usbotg2_3: usbotg2grp-3 { + fsl,pins = < + MX6SL_PAD_KEY_ROW5__USB_OTG2_OC 0x17059 + >; + }; + + pinctrl_usbotg2_4: usbotg2grp-4 { + fsl,pins = < + MX6SL_PAD_SD3_DAT2__USB_OTG2_OC 0x17059 + >; + }; + }; + + usdhc1 { + pinctrl_usdhc1_1: usdhc1grp-1 { + fsl,pins = < + MX6SL_PAD_SD1_CMD__SD1_CMD 0x17059 + MX6SL_PAD_SD1_CLK__SD1_CLK 0x10059 + MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x17059 + MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x17059 + MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x17059 + MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x17059 + MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x17059 + MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x17059 + MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x17059 + MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x17059 + >; + }; + }; + + usdhc2 { + pinctrl_usdhc2_1: usdhc2grp-1 { + fsl,pins = < + MX6SL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6SL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x17059 + >; + }; + + pinctrl_usdhc2_1_100mhz: usdhc2grp-1-100mhz { + fsl,pins = < + MX6SL_PAD_SD2_CMD__SD2_CMD 0x170b9 + MX6SL_PAD_SD2_CLK__SD2_CLK 0x100b9 + MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 + MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 + MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 + MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170b9 + >; + }; + + pinctrl_usdhc2_1_200mhz: usdhc2grp-1-200mhz { + fsl,pins = < + MX6SL_PAD_SD2_CMD__SD2_CMD 0x170f9 + MX6SL_PAD_SD2_CLK__SD2_CLK 0x100f9 + MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 + MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 + MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 + MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170f9 + >; + }; + }; + + usdhc3 { + pinctrl_usdhc3_1: usdhc3grp-1 { + fsl,pins = < + MX6SL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6SL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + >; + }; + + pinctrl_usdhc3_1_100mhz: usdhc3grp-1-100mhz { + fsl,pins = < + MX6SL_PAD_SD3_CMD__SD3_CMD 0x170b9 + MX6SL_PAD_SD3_CLK__SD3_CLK 0x100b9 + MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170b9 + MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170b9 + MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170b9 + MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170b9 + >; + }; + + pinctrl_usdhc3_1_200mhz: usdhc3grp-1-200mhz { + fsl,pins = < + MX6SL_PAD_SD3_CMD__SD3_CMD 0x170f9 + MX6SL_PAD_SD3_CLK__SD3_CLK 0x100f9 + MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x170f9 + MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x170f9 + MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x170f9 + MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x170f9 + >; + }; + }; + + i2c1 { + pinctrl_i2c1_1: i2c1grp-1 { + fsl,pins = < + MX6SL_PAD_I2C1_SCL__I2C1_SCL 0x4001b8b1 + MX6SL_PAD_I2C1_SDA__I2C1_SDA 0x4001b8b1 + >; + }; + }; + + i2c2 { + pinctrl_i2c2_1: i2c2grp-1 { + fsl,pins = < + MX6SL_PAD_I2C2_SCL__I2C2_SCL 0x4001b8b1 + MX6SL_PAD_I2C2_SDA__I2C2_SDA 0x4001b8b1 + >; + }; + }; + + i2c3 { + pinctrl_i2c3_1: i2c3grp-1 { + fsl,pins = < + MX6SL_PAD_EPDC_SDCE2__I2C3_SCL 0x4001b8b1 + MX6SL_PAD_EPDC_SDCE3__I2C3_SDA 0x4001b8b1 + >; + }; + }; + + kpp { + pinctrl_kpp_1: kpp_grp_1 { + fsl,pins = < + MX6SL_PAD_KEY_ROW0__KEY_ROW0 0x1b010 + MX6SL_PAD_KEY_ROW1__KEY_ROW1 0x1b010 + MX6SL_PAD_KEY_ROW2__KEY_ROW2 0x1b0b0 + MX6SL_PAD_KEY_COL0__KEY_COL0 0x1b0b0 + MX6SL_PAD_KEY_COL1__KEY_COL1 0x1b0b0 + MX6SL_PAD_KEY_COL2__KEY_COL2 0x1b0b0 + >; + }; + + pinctrl_kpp_1_sleep: kpp_grp_1_sleep { + fsl,pins = < + MX6SL_PAD_KEY_ROW0__GPIO3_IO25 0x3080 + MX6SL_PAD_KEY_ROW1__GPIO3_IO27 0x3080 + MX6SL_PAD_KEY_ROW2__GPIO3_IO29 0x3080 + MX6SL_PAD_KEY_COL0__GPIO3_IO24 0x3080 + MX6SL_PAD_KEY_COL1__GPIO3_IO26 0x3080 + MX6SL_PAD_KEY_COL2__GPIO3_IO28 0x3080 + >; + }; + }; + + lcdif { + pinctrl_lcdif_dat_0: lcdifdatgrp-0 { + fsl,pins = < + MX6SL_PAD_LCD_DAT0__LCD_DATA00 0x1b0b0 + MX6SL_PAD_LCD_DAT1__LCD_DATA01 0x1b0b0 + MX6SL_PAD_LCD_DAT2__LCD_DATA02 0x1b0b0 + MX6SL_PAD_LCD_DAT3__LCD_DATA03 0x1b0b0 + MX6SL_PAD_LCD_DAT4__LCD_DATA04 0x1b0b0 + MX6SL_PAD_LCD_DAT5__LCD_DATA05 0x1b0b0 + MX6SL_PAD_LCD_DAT6__LCD_DATA06 0x1b0b0 + MX6SL_PAD_LCD_DAT7__LCD_DATA07 0x1b0b0 + MX6SL_PAD_LCD_DAT8__LCD_DATA08 0x1b0b0 + MX6SL_PAD_LCD_DAT9__LCD_DATA09 0x1b0b0 + MX6SL_PAD_LCD_DAT10__LCD_DATA10 0x1b0b0 + MX6SL_PAD_LCD_DAT11__LCD_DATA11 0x1b0b0 + MX6SL_PAD_LCD_DAT12__LCD_DATA12 0x1b0b0 + MX6SL_PAD_LCD_DAT13__LCD_DATA13 0x1b0b0 + MX6SL_PAD_LCD_DAT14__LCD_DATA14 0x1b0b0 + MX6SL_PAD_LCD_DAT15__LCD_DATA15 0x1b0b0 + MX6SL_PAD_LCD_DAT16__LCD_DATA16 0x1b0b0 + MX6SL_PAD_LCD_DAT17__LCD_DATA17 0x1b0b0 + MX6SL_PAD_LCD_DAT18__LCD_DATA18 0x1b0b0 + MX6SL_PAD_LCD_DAT19__LCD_DATA19 0x1b0b0 + MX6SL_PAD_LCD_DAT20__LCD_DATA20 0x1b0b0 + MX6SL_PAD_LCD_DAT21__LCD_DATA21 0x1b0b0 + MX6SL_PAD_LCD_DAT22__LCD_DATA22 0x1b0b0 + MX6SL_PAD_LCD_DAT23__LCD_DATA23 0x1b0b0 + >; + }; + + pinctrl_lcdif_ctrl_0: lcdifctrlgrp-0 { + fsl,pins = < + MX6SL_PAD_LCD_CLK__LCD_CLK 0x1b0b0 + MX6SL_PAD_LCD_ENABLE__LCD_ENABLE 0x1b0b0 + MX6SL_PAD_LCD_HSYNC__LCD_HSYNC 0x1b0b0 + MX6SL_PAD_LCD_VSYNC__LCD_VSYNC 0x1b0b0 + >; + }; + }; + + pwm1 { + pinctrl_pwm1_0: pwm1grp-0 { + fsl,pins = < + MX6SL_PAD_PWM1__PWM1_OUT 0x110b0 + >; + }; + }; + }; + + csi: csi@020e4000 { + compatible = "fsl,imx6sl-csi"; + reg = <0x020e4000 0x4000>; + interrupts = <0 7 0x04>; + status = "disabled"; + }; + + spdc: spdc@020e8000 { + reg = <0x020e8000 0x4000>; + interrupts = <0 6 0x04>; + }; + + sdma: sdma@020ec000 { + compatible = "fsl,imx6sl-sdma", "fsl,imx35-sdma"; + reg = <0x020ec000 0x4000>; + interrupts = <0 2 0x04>; + clocks = <&clks IMX6SL_CLK_SDMA>, + <&clks IMX6SL_CLK_SDMA>; + clock-names = "ipg", "ahb"; + iram = <&ocram>; + /* imx6sl reuses imx6q sdma firmware */ + fsl,sdma-ram-script-name = "imx/sdma/sdma-imx6q.bin"; + }; + + pxp: pxp@020f0000 { + compatible = "fsl,imx6sl-pxp-dma", "fsl,imx6dl-pxp-dma"; + reg = <0x020f0000 0x4000>; + interrupts = <0 98 0x04>; + clocks = <&clks 111>; + clock-names = "pxp-axi"; + status = "disabled"; + }; + + epdc: epdc@020f4000 { + compatible = "fsl,imx6sl-epdc", "fsl,imx6dl-epdc"; + reg = <0x020f4000 0x4000>; + interrupts = <0 97 0x04>; + clocks = <&clks IMX6SL_CLK_EPDC_AXI>, + <&clks IMX6SL_CLK_EPDC_PIX>; + clock-names = "epdc_axi", "epdc_pix"; + }; + + lcdif: lcdif@020f8000 { + compatible = "fsl,imx6sl-lcdif", "fsl,imx28-lcdif"; + reg = <0x020f8000 0x4000>; + interrupts = <0 39 0x04>; + clocks = <&clks IMX6SL_CLK_LCDIF_PIX>, + <&clks IMX6SL_CLK_LCDIF_AXI>; + clock-names = "pix", "axi"; + status = "disabled"; + }; + + dcp: dcp@020fc000 { + reg = <0x020fc000 0x4000>; + interrupts = <0 99 0x04>; + }; + }; + + aips2: aips-bus@02100000 { + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x02100000 0x100000>; + ranges; + + usbotg1: usb@02184000 { + compatible = "fsl,imx6sl-usb", "fsl,imx27-usb"; + reg = <0x02184000 0x200>; + interrupts = <0 43 0x04>; + clocks = <&clks IMX6SL_CLK_USBOH3>; + fsl,usbphy = <&usbphy1>; + fsl,usbmisc = <&usbmisc 0>; + fsl,anatop = <&anatop>; + status = "disabled"; + }; + + usbotg2: usb@02184200 { + compatible = "fsl,imx6sl-usb", "fsl,imx27-usb"; + reg = <0x02184200 0x200>; + interrupts = <0 42 0x04>; + clocks = <&clks IMX6SL_CLK_USBOH3>; + fsl,usbphy = <&usbphy2>; + fsl,usbmisc = <&usbmisc 1>; + status = "disabled"; + }; + + usbh: usb@02184400 { + compatible = "fsl,imx6sl-usb", "fsl,imx27-usb"; + reg = <0x02184400 0x200>; + interrupts = <0 40 0x04>; + clocks = <&clks IMX6SL_CLK_USBOH3>; + fsl,usbmisc = <&usbmisc 2>; + status = "disabled"; + }; + + usbmisc: usbmisc@02184800 { + #index-cells = <1>; + compatible = "fsl,imx6sl-usbmisc", "fsl,imx6q-usbmisc"; + reg = <0x02184800 0x200>; + clocks = <&clks IMX6SL_CLK_USBOH3>; + }; + + fec: ethernet@02188000 { + compatible = "fsl,imx6sl-fec", "fsl,imx25-fec"; + reg = <0x02188000 0x4000>; + interrupts = <0 114 0x04>; + clocks = <&clks IMX6SL_CLK_ENET>, + <&clks IMX6SL_CLK_ENET_REF>; + clock-names = "ipg", "ahb"; + status = "disabled"; + }; + + usdhc1: usdhc@02190000 { + compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc"; + reg = <0x02190000 0x4000>; + interrupts = <0 22 0x04>; + clocks = <&clks IMX6SL_CLK_USDHC1>, + <&clks IMX6SL_CLK_USDHC1>, + <&clks IMX6SL_CLK_USDHC1>; + clock-names = "ipg", "ahb", "per"; + bus-width = <4>; + status = "disabled"; + }; + + usdhc2: usdhc@02194000 { + compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc"; + reg = <0x02194000 0x4000>; + interrupts = <0 23 0x04>; + clocks = <&clks IMX6SL_CLK_USDHC2>, + <&clks IMX6SL_CLK_USDHC2>, + <&clks IMX6SL_CLK_USDHC2>; + clock-names = "ipg", "ahb", "per"; + bus-width = <4>; + status = "disabled"; + }; + + usdhc3: usdhc@02198000 { + compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc"; + reg = <0x02198000 0x4000>; + interrupts = <0 24 0x04>; + clocks = <&clks IMX6SL_CLK_USDHC3>, + <&clks IMX6SL_CLK_USDHC3>, + <&clks IMX6SL_CLK_USDHC3>; + clock-names = "ipg", "ahb", "per"; + bus-width = <4>; + status = "disabled"; + }; + + usdhc4: usdhc@0219c000 { + compatible = "fsl,imx6sl-usdhc", "fsl,imx6q-usdhc"; + reg = <0x0219c000 0x4000>; + interrupts = <0 25 0x04>; + clocks = <&clks IMX6SL_CLK_USDHC4>, + <&clks IMX6SL_CLK_USDHC4>, + <&clks IMX6SL_CLK_USDHC4>; + clock-names = "ipg", "ahb", "per"; + bus-width = <4>; + status = "disabled"; + }; + + i2c1: i2c@021a0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c"; + reg = <0x021a0000 0x4000>; + interrupts = <0 36 0x04>; + clocks = <&clks IMX6SL_CLK_I2C1>; + status = "disabled"; + }; + + i2c2: i2c@021a4000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c"; + reg = <0x021a4000 0x4000>; + interrupts = <0 37 0x04>; + clocks = <&clks IMX6SL_CLK_I2C2>; + status = "disabled"; + }; + + i2c3: i2c@021a8000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6sl-i2c", "fsl,imx21-i2c"; + reg = <0x021a8000 0x4000>; + interrupts = <0 38 0x04>; + clocks = <&clks IMX6SL_CLK_I2C3>; + status = "disabled"; + }; + + mmdc: mmdc@021b0000 { + compatible = "fsl,imx6sl-mmdc", "fsl,imx6q-mmdc"; + reg = <0x021b0000 0x4000>; + }; + + rngb: rngb@021b4000 { + reg = <0x021b4000 0x4000>; + interrupts = <0 5 0x04>; + }; + + weim: weim@021b8000 { + reg = <0x021b8000 0x4000>; + interrupts = <0 14 0x04>; + }; + + ocotp: ocotp-ctrl@021bc000 { + compatible = "syscon"; + reg = <0x021bc000 0x4000>; + }; + + ocotp-fuse@021bc000 { + compatible = "fsl,imx6sl-ocotp", "fsl,imx6q-ocotp"; + reg = <0x021bc000 0x4000>; + clocks = <&clks IMX6SL_CLK_OCOTP>; + }; + + audmux: audmux@021d8000 { + compatible = "fsl,imx6sl-audmux", "fsl,imx31-audmux"; + reg = <0x021d8000 0x4000>; + status = "disabled"; + }; + + gpu: gpu@02200000 { + compatible = "fsl,imx6sl-gpu", "fsl,imx6q-gpu"; + reg = <0x02200000 0x4000>, <0x02204000 0x4000>, + <0x80000000 0x0>; + reg-names = "iobase_2d", "iobase_vg", + "phys_baseaddr"; + interrupts = <0 10 0x04>, <0 11 0x04>; + interrupt-names = "irq_2d", "irq_vg"; + clocks = <&clks IMX6SL_CLK_MMDC_ROOT>, + <&clks IMX6SL_CLK_MMDC_ROOT>, + <&clks IMX6SL_CLK_GPU2D_OVG>; + clock-names = "gpu2d_axi_clk", "openvg_axi_clk", + "gpu2d_clk"; + resets = <&src 3>, <&src 3>; + reset-names = "gpu2d", "gpuvg"; + pu-supply = <®_pu>; + }; + + }; + + pinctrl_fec_1_sleep: fecgrp-1-sleep { + fsl,pins = < + MX6SL_PAD_FEC_MDC__GPIO4_IO23 0x3080 + MX6SL_PAD_FEC_CRS_DV__GPIO4_IO25 0x3080 + MX6SL_PAD_FEC_RXD0__GPIO4_IO17 0x3080 + MX6SL_PAD_FEC_RXD1__GPIO4_IO18 0x3080 + MX6SL_PAD_FEC_TX_EN__GPIO4_IO22 0x3080 + MX6SL_PAD_FEC_TXD0__GPIO4_IO24 0x3080 + MX6SL_PAD_FEC_TXD1__GPIO4_IO16 0x3080 + MX6SL_PAD_FEC_REF_CLK__GPIO4_IO26 0x3080 + >; + }; + + pinctrl_pwm1_0_sleep: pwm1grp-0-sleep { + fsl,pins = < + MX6SL_PAD_PWM1__GPIO3_IO23 0x3080 + >; + }; + + pinctrl_usdhc1_1_100mhz: usdhc1grp-1-100mhz { + fsl,pins = < + MX6SL_PAD_SD1_CMD__SD1_CMD 0x170b9 + MX6SL_PAD_SD1_CLK__SD1_CLK 0x100b9 + MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170b9 + MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170b9 + MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170b9 + MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170b9 + MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170b9 + MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170b9 + MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170b9 + MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170b9 + >; + }; + + pinctrl_usdhc1_1_200mhz: usdhc1grp-1-200mhz { + fsl,pins = < + MX6SL_PAD_SD1_CMD__SD1_CMD 0x170f9 + MX6SL_PAD_SD1_CLK__SD1_CLK 0x100f9 + MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x170f9 + MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x170f9 + MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x170f9 + MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x170f9 + MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x170f9 + MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x170f9 + MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x170f9 + MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x170f9 + >; + }; + + }; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/include/dt-bindings/clock/imx6sl-clock.h linux-3.10.30-cubox-i/arch/arm/boot/dts/include/dt-bindings/clock/imx6sl-clock.h --- linux-3.10.30/arch/arm/boot/dts/include/dt-bindings/clock/imx6sl-clock.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/include/dt-bindings/clock/imx6sl-clock.h 2014-03-08 20:34:36.000000000 +0100 @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + * + */ + +#ifndef __DT_BINDINGS_CLOCK_IMX6SL_H +#define __DT_BINDINGS_CLOCK_IMX6SL_H + +#define IMX6SL_CLK_DUMMY 0 +#define IMX6SL_CLK_CKIL 1 +#define IMX6SL_CLK_OSC 2 +#define IMX6SL_CLK_PLL1_SYS 3 +#define IMX6SL_CLK_PLL2_BUS 4 +#define IMX6SL_CLK_PLL3_USB_OTG 5 +#define IMX6SL_CLK_PLL4_AUDIO 6 +#define IMX6SL_CLK_PLL5_VIDEO 7 +#define IMX6SL_CLK_PLL6_ENET 8 +#define IMX6SL_CLK_PLL7_USB_HOST 9 +#define IMX6SL_CLK_USBPHY1 10 +#define IMX6SL_CLK_USBPHY2 11 +#define IMX6SL_CLK_USBPHY1_GATE 12 +#define IMX6SL_CLK_USBPHY2_GATE 13 +#define IMX6SL_CLK_PLL4_POST_DIV 14 +#define IMX6SL_CLK_PLL5_POST_DIV 15 +#define IMX6SL_CLK_PLL5_VIDEO_DIV 16 +#define IMX6SL_CLK_ENET_REF 17 +#define IMX6SL_CLK_PLL2_PFD0 18 +#define IMX6SL_CLK_PLL2_PFD1 19 +#define IMX6SL_CLK_PLL2_PFD2 20 +#define IMX6SL_CLK_PLL3_PFD0 21 +#define IMX6SL_CLK_PLL3_PFD1 22 +#define IMX6SL_CLK_PLL3_PFD2 23 +#define IMX6SL_CLK_PLL3_PFD3 24 +#define IMX6SL_CLK_PLL2_198M 25 +#define IMX6SL_CLK_PLL3_120M 26 +#define IMX6SL_CLK_PLL3_80M 27 +#define IMX6SL_CLK_PLL3_60M 28 +#define IMX6SL_CLK_STEP 29 +#define IMX6SL_CLK_PLL1_SW 30 +#define IMX6SL_CLK_OCRAM_ALT_SEL 31 +#define IMX6SL_CLK_OCRAM_SEL 32 +#define IMX6SL_CLK_PRE_PERIPH2_SEL 33 +#define IMX6SL_CLK_PRE_PERIPH_SEL 34 +#define IMX6SL_CLK_PERIPH2_CLK2_SEL 35 +#define IMX6SL_CLK_PERIPH_CLK2_SEL 36 +#define IMX6SL_CLK_CSI_SEL 37 +#define IMX6SL_CLK_LCDIF_AXI_SEL 38 +#define IMX6SL_CLK_USDHC1_SEL 39 +#define IMX6SL_CLK_USDHC2_SEL 40 +#define IMX6SL_CLK_USDHC3_SEL 41 +#define IMX6SL_CLK_USDHC4_SEL 42 +#define IMX6SL_CLK_SSI1_SEL 43 +#define IMX6SL_CLK_SSI2_SEL 44 +#define IMX6SL_CLK_SSI3_SEL 45 +#define IMX6SL_CLK_PERCLK_SEL 46 +#define IMX6SL_CLK_PXP_AXI_SEL 47 +#define IMX6SL_CLK_EPDC_AXI_SEL 48 +#define IMX6SL_CLK_GPU2D_OVG_SEL 49 +#define IMX6SL_CLK_GPU2D_SEL 50 +#define IMX6SL_CLK_LCDIF_PIX_SEL 51 +#define IMX6SL_CLK_EPDC_PIX_SEL 52 +#define IMX6SL_CLK_SPDIF0_SEL 53 +#define IMX6SL_CLK_SPDIF1_SEL 54 +#define IMX6SL_CLK_EXTERN_AUDIO_SEL 55 +#define IMX6SL_CLK_ECSPI_SEL 56 +#define IMX6SL_CLK_UART_SEL 57 +#define IMX6SL_CLK_PERIPH 58 +#define IMX6SL_CLK_PERIPH2 59 +#define IMX6SL_CLK_OCRAM_PODF 60 +#define IMX6SL_CLK_PERIPH_CLK2 61 +#define IMX6SL_CLK_PERIPH2_CLK2 62 +#define IMX6SL_CLK_IPG 63 +#define IMX6SL_CLK_CSI_PODF 64 +#define IMX6SL_CLK_LCDIF_AXI_PODF 65 +#define IMX6SL_CLK_USDHC1_PODF 66 +#define IMX6SL_CLK_USDHC2_PODF 67 +#define IMX6SL_CLK_USDHC3_PODF 68 +#define IMX6SL_CLK_USDHC4_PODF 69 +#define IMX6SL_CLK_SSI1_PRED 70 +#define IMX6SL_CLK_SSI1_PODF 71 +#define IMX6SL_CLK_SSI2_PRED 72 +#define IMX6SL_CLK_SSI2_PODF 73 +#define IMX6SL_CLK_SSI3_PRED 74 +#define IMX6SL_CLK_SSI3_PODF 75 +#define IMX6SL_CLK_PERCLK 76 +#define IMX6SL_CLK_PXP_AXI_PODF 77 +#define IMX6SL_CLK_EPDC_AXI_PODF 78 +#define IMX6SL_CLK_GPU2D_OVG_PODF 79 +#define IMX6SL_CLK_GPU2D_PODF 80 +#define IMX6SL_CLK_LCDIF_PIX_PRED 81 +#define IMX6SL_CLK_EPDC_PIX_PRED 82 +#define IMX6SL_CLK_LCDIF_PIX_PODF 83 +#define IMX6SL_CLK_EPDC_PIX_PODF 84 +#define IMX6SL_CLK_SPDIF0_PRED 85 +#define IMX6SL_CLK_SPDIF0_PODF 86 +#define IMX6SL_CLK_SPDIF1_PRED 87 +#define IMX6SL_CLK_SPDIF1_PODF 88 +#define IMX6SL_CLK_EXTERN_AUDIO_PRED 89 +#define IMX6SL_CLK_EXTERN_AUDIO_PODF 90 +#define IMX6SL_CLK_ECSPI_ROOT 91 +#define IMX6SL_CLK_UART_ROOT 92 +#define IMX6SL_CLK_AHB 93 +#define IMX6SL_CLK_MMDC_ROOT 94 +#define IMX6SL_CLK_ARM 95 +#define IMX6SL_CLK_ECSPI1 96 +#define IMX6SL_CLK_ECSPI2 97 +#define IMX6SL_CLK_ECSPI3 98 +#define IMX6SL_CLK_ECSPI4 99 +#define IMX6SL_CLK_EPIT1 100 +#define IMX6SL_CLK_EPIT2 101 +#define IMX6SL_CLK_EXTERN_AUDIO 102 +#define IMX6SL_CLK_GPT 103 +#define IMX6SL_CLK_GPT_SERIAL 104 +#define IMX6SL_CLK_GPU2D_OVG 105 +#define IMX6SL_CLK_I2C1 106 +#define IMX6SL_CLK_I2C2 107 +#define IMX6SL_CLK_I2C3 108 +#define IMX6SL_CLK_OCOTP 109 +#define IMX6SL_CLK_CSI 110 +#define IMX6SL_CLK_PXP_AXI 111 +#define IMX6SL_CLK_EPDC_AXI 112 +#define IMX6SL_CLK_LCDIF_AXI 113 +#define IMX6SL_CLK_LCDIF_PIX 114 +#define IMX6SL_CLK_EPDC_PIX 115 +#define IMX6SL_CLK_OCRAM 116 +#define IMX6SL_CLK_PWM1 117 +#define IMX6SL_CLK_PWM2 118 +#define IMX6SL_CLK_PWM3 119 +#define IMX6SL_CLK_PWM4 120 +#define IMX6SL_CLK_SDMA 121 +#define IMX6SL_CLK_SPDIF 122 +#define IMX6SL_CLK_SSI1 123 +#define IMX6SL_CLK_SSI2 124 +#define IMX6SL_CLK_SSI3 125 +#define IMX6SL_CLK_UART 126 +#define IMX6SL_CLK_UART_SERIAL 127 +#define IMX6SL_CLK_USBOH3 128 +#define IMX6SL_CLK_USDHC1 129 +#define IMX6SL_CLK_USDHC2 130 +#define IMX6SL_CLK_USDHC3 131 +#define IMX6SL_CLK_USDHC4 132 +#define IMX6SL_CLK_PLL4_AUDIO_DIV 133 +#define IMX6SL_CLK_ENET 134 +#define IMX6SL_CLK_UART_OSC_4M 135 +#define IMX6SL_CLK_SPBA 136 +#define IMX6SL_CLK_CLK_END 137 + +#endif /* __DT_BINDINGS_CLOCK_IMX6SL_H */ diff -Nur linux-3.10.30/arch/arm/boot/dts/include/dt-bindings/clock/vf610-clock.h linux-3.10.30-cubox-i/arch/arm/boot/dts/include/dt-bindings/clock/vf610-clock.h --- linux-3.10.30/arch/arm/boot/dts/include/dt-bindings/clock/vf610-clock.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/include/dt-bindings/clock/vf610-clock.h 2014-03-08 20:34:36.000000000 +0100 @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#ifndef __DT_BINDINGS_CLOCK_VF610_H +#define __DT_BINDINGS_CLOCK_VF610_H + +#define VF610_CLK_DUMMY 0 +#define VF610_CLK_SIRC_128K 1 +#define VF610_CLK_SIRC_32K 2 +#define VF610_CLK_FIRC 3 +#define VF610_CLK_SXOSC 4 +#define VF610_CLK_FXOSC 5 +#define VF610_CLK_FXOSC_HALF 6 +#define VF610_CLK_SLOW_CLK_SEL 7 +#define VF610_CLK_FASK_CLK_SEL 8 +#define VF610_CLK_AUDIO_EXT 9 +#define VF610_CLK_ENET_EXT 10 +#define VF610_CLK_PLL1_MAIN 11 +#define VF610_CLK_PLL1_PFD1 12 +#define VF610_CLK_PLL1_PFD2 13 +#define VF610_CLK_PLL1_PFD3 14 +#define VF610_CLK_PLL1_PFD4 15 +#define VF610_CLK_PLL2_MAIN 16 +#define VF610_CLK_PLL2_PFD1 17 +#define VF610_CLK_PLL2_PFD2 18 +#define VF610_CLK_PLL2_PFD3 19 +#define VF610_CLK_PLL2_PFD4 20 +#define VF610_CLK_PLL3_MAIN 21 +#define VF610_CLK_PLL3_PFD1 22 +#define VF610_CLK_PLL3_PFD2 23 +#define VF610_CLK_PLL3_PFD3 24 +#define VF610_CLK_PLL3_PFD4 25 +#define VF610_CLK_PLL4_MAIN 26 +#define VF610_CLK_PLL5_MAIN 27 +#define VF610_CLK_PLL6_MAIN 28 +#define VF610_CLK_PLL3_MAIN_DIV 29 +#define VF610_CLK_PLL4_MAIN_DIV 30 +#define VF610_CLK_PLL6_MAIN_DIV 31 +#define VF610_CLK_PLL1_PFD_SEL 32 +#define VF610_CLK_PLL2_PFD_SEL 33 +#define VF610_CLK_SYS_SEL 34 +#define VF610_CLK_DDR_SEL 35 +#define VF610_CLK_SYS_BUS 36 +#define VF610_CLK_PLATFORM_BUS 37 +#define VF610_CLK_IPG_BUS 38 +#define VF610_CLK_UART0 39 +#define VF610_CLK_UART1 40 +#define VF610_CLK_UART2 41 +#define VF610_CLK_UART3 42 +#define VF610_CLK_UART4 43 +#define VF610_CLK_UART5 44 +#define VF610_CLK_PIT 45 +#define VF610_CLK_I2C0 46 +#define VF610_CLK_I2C1 47 +#define VF610_CLK_I2C2 48 +#define VF610_CLK_I2C3 49 +#define VF610_CLK_FTM0_EXT_SEL 50 +#define VF610_CLK_FTM0_FIX_SEL 51 +#define VF610_CLK_FTM0_EXT_FIX_EN 52 +#define VF610_CLK_FTM1_EXT_SEL 53 +#define VF610_CLK_FTM1_FIX_SEL 54 +#define VF610_CLK_FTM1_EXT_FIX_EN 55 +#define VF610_CLK_FTM2_EXT_SEL 56 +#define VF610_CLK_FTM2_FIX_SEL 57 +#define VF610_CLK_FTM2_EXT_FIX_EN 58 +#define VF610_CLK_FTM3_EXT_SEL 59 +#define VF610_CLK_FTM3_FIX_SEL 60 +#define VF610_CLK_FTM3_EXT_FIX_EN 61 +#define VF610_CLK_FTM0 62 +#define VF610_CLK_FTM1 63 +#define VF610_CLK_FTM2 64 +#define VF610_CLK_FTM3 65 +#define VF610_CLK_ENET_50M 66 +#define VF610_CLK_ENET_25M 67 +#define VF610_CLK_ENET_SEL 68 +#define VF610_CLK_ENET 69 +#define VF610_CLK_ENET_TS_SEL 70 +#define VF610_CLK_ENET_TS 71 +#define VF610_CLK_DSPI0 72 +#define VF610_CLK_DSPI1 73 +#define VF610_CLK_DSPI2 74 +#define VF610_CLK_DSPI3 75 +#define VF610_CLK_WDT 76 +#define VF610_CLK_ESDHC0_SEL 77 +#define VF610_CLK_ESDHC0_EN 78 +#define VF610_CLK_ESDHC0_DIV 79 +#define VF610_CLK_ESDHC0 80 +#define VF610_CLK_ESDHC1_SEL 81 +#define VF610_CLK_ESDHC1_EN 82 +#define VF610_CLK_ESDHC1_DIV 83 +#define VF610_CLK_ESDHC1 84 +#define VF610_CLK_DCU0_SEL 85 +#define VF610_CLK_DCU0_EN 86 +#define VF610_CLK_DCU0_DIV 87 +#define VF610_CLK_DCU0 88 +#define VF610_CLK_DCU1_SEL 89 +#define VF610_CLK_DCU1_EN 90 +#define VF610_CLK_DCU1_DIV 91 +#define VF610_CLK_DCU1 92 +#define VF610_CLK_ESAI_SEL 93 +#define VF610_CLK_ESAI_EN 94 +#define VF610_CLK_ESAI_DIV 95 +#define VF610_CLK_ESAI 96 +#define VF610_CLK_SAI0_SEL 97 +#define VF610_CLK_SAI0_EN 98 +#define VF610_CLK_SAI0_DIV 99 +#define VF610_CLK_SAI0 100 +#define VF610_CLK_SAI1_SEL 101 +#define VF610_CLK_SAI1_EN 102 +#define VF610_CLK_SAI1_DIV 103 +#define VF610_CLK_SAI1 104 +#define VF610_CLK_SAI2_SEL 105 +#define VF610_CLK_SAI2_EN 106 +#define VF610_CLK_SAI2_DIV 107 +#define VF610_CLK_SAI2 108 +#define VF610_CLK_SAI3_SEL 109 +#define VF610_CLK_SAI3_EN 110 +#define VF610_CLK_SAI3_DIV 111 +#define VF610_CLK_SAI3 112 +#define VF610_CLK_USBC0 113 +#define VF610_CLK_USBC1 114 +#define VF610_CLK_QSPI0_SEL 115 +#define VF610_CLK_QSPI0_EN 116 +#define VF610_CLK_QSPI0_X4_DIV 117 +#define VF610_CLK_QSPI0_X2_DIV 118 +#define VF610_CLK_QSPI0_X1_DIV 119 +#define VF610_CLK_QSPI1_SEL 120 +#define VF610_CLK_QSPI1_EN 121 +#define VF610_CLK_QSPI1_X4_DIV 122 +#define VF610_CLK_QSPI1_X2_DIV 123 +#define VF610_CLK_QSPI1_X1_DIV 124 +#define VF610_CLK_QSPI0 125 +#define VF610_CLK_QSPI1 126 +#define VF610_CLK_NFC_SEL 127 +#define VF610_CLK_NFC_EN 128 +#define VF610_CLK_NFC_PRE_DIV 129 +#define VF610_CLK_NFC_FRAC_DIV 130 +#define VF610_CLK_NFC_INV 131 +#define VF610_CLK_NFC 132 +#define VF610_CLK_VADC_SEL 133 +#define VF610_CLK_VADC_EN 134 +#define VF610_CLK_VADC_DIV 135 +#define VF610_CLK_VADC_DIV_HALF 136 +#define VF610_CLK_VADC 137 +#define VF610_CLK_ADC0 138 +#define VF610_CLK_ADC1 139 +#define VF610_CLK_DAC0 140 +#define VF610_CLK_DAC1 141 +#define VF610_CLK_FLEXCAN0 142 +#define VF610_CLK_FLEXCAN1 143 +#define VF610_CLK_ASRC 144 +#define VF610_CLK_GPU_SEL 145 +#define VF610_CLK_GPU_EN 146 +#define VF610_CLK_GPU2D 147 +#define VF610_CLK_ENET0 148 +#define VF610_CLK_ENET1 149 +#define VF610_CLK_END 150 + +#endif /* __DT_BINDINGS_CLOCK_VF610_H */ diff -Nur linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts --- linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a15x1.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,159 @@ +/* + * ARM Ltd. Fast Models + * + * Versatile Express (VE) system model + * ARMCortexA15x1CT + * + * RTSM_VE_Cortex_A15x1.lisa + */ + +/dts-v1/; + +/ { + model = "RTSM_VE_CortexA15x1"; + arm,vexpress,site = <0xf>; + compatible = "arm,rtsm_ve,cortex_a15x1", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + serial1 = &v2m_serial1; + serial2 = &v2m_serial2; + serial3 = &v2m_serial3; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0 0x80000000 0 0x80000000>; + }; + + gic: interrupt-controller@2c001000 { + compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0x2c001000 0 0x1000>, + <0 0x2c002000 0 0x1000>, + <0 0x2c004000 0 0x2000>, + <0 0x2c006000 0 0x2000>; + interrupts = <1 9 0xf04>; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = <1 13 0xf08>, + <1 14 0xf08>, + <1 11 0xf08>, + <1 10 0xf08>; + }; + + dcc { + compatible = "arm,vexpress,config-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + /* ACLK clock to the AXI master port on the test chip */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + freq-range = <30000000 50000000>; + #clock-cells = <0>; + clock-output-names = "extsaxiclk"; + }; + + oscclk1: osc@1 { + /* Reference clock for the CLCD */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 1>; + freq-range = <10000000 80000000>; + #clock-cells = <0>; + clock-output-names = "clcdclk"; + }; + + smbclk: oscclk2: osc@2 { + /* Reference clock for the test chip internal PLLs */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 2>; + freq-range = <33000000 100000000>; + #clock-cells = <0>; + clock-output-names = "tcrefclk"; + }; + }; + + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0 0x08000000 0x04000000>, + <1 0 0 0x14000000 0x04000000>, + <2 0 0 0x18000000 0x04000000>, + <3 0 0 0x1c000000 0x04000000>, + <4 0 0 0x0c000000 0x04000000>, + <5 0 0 0x10000000 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <0 0 0 &gic 0 0 4>, + <0 0 1 &gic 0 1 4>, + <0 0 2 &gic 0 2 4>, + <0 0 3 &gic 0 3 4>, + <0 0 4 &gic 0 4 4>, + <0 0 5 &gic 0 5 4>, + <0 0 6 &gic 0 6 4>, + <0 0 7 &gic 0 7 4>, + <0 0 8 &gic 0 8 4>, + <0 0 9 &gic 0 9 4>, + <0 0 10 &gic 0 10 4>, + <0 0 11 &gic 0 11 4>, + <0 0 12 &gic 0 12 4>, + <0 0 13 &gic 0 13 4>, + <0 0 14 &gic 0 14 4>, + <0 0 15 &gic 0 15 4>, + <0 0 16 &gic 0 16 4>, + <0 0 17 &gic 0 17 4>, + <0 0 18 &gic 0 18 4>, + <0 0 19 &gic 0 19 4>, + <0 0 20 &gic 0 20 4>, + <0 0 21 &gic 0 21 4>, + <0 0 22 &gic 0 22 4>, + <0 0 23 &gic 0 23 4>, + <0 0 24 &gic 0 24 4>, + <0 0 25 &gic 0 25 4>, + <0 0 26 &gic 0 26 4>, + <0 0 27 &gic 0 27 4>, + <0 0 28 &gic 0 28 4>, + <0 0 29 &gic 0 29 4>, + <0 0 30 &gic 0 30 4>, + <0 0 31 &gic 0 31 4>, + <0 0 32 &gic 0 32 4>, + <0 0 33 &gic 0 33 4>, + <0 0 34 &gic 0 34 4>, + <0 0 35 &gic 0 35 4>, + <0 0 36 &gic 0 36 4>, + <0 0 37 &gic 0 37 4>, + <0 0 38 &gic 0 38 4>, + <0 0 39 &gic 0 39 4>, + <0 0 40 &gic 0 40 4>, + <0 0 41 &gic 0 41 4>, + <0 0 42 &gic 0 42 4>; + + /include/ "rtsm_ve-motherboard.dtsi" + }; +}; + +/include/ "clcd-panels.dtsi" diff -Nur linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts --- linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a15x2.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,165 @@ +/* + * ARM Ltd. Fast Models + * + * Versatile Express (VE) system model + * ARMCortexA15x2CT + * + * RTSM_VE_Cortex_A15x2.lisa + */ + +/dts-v1/; + +/ { + model = "RTSM_VE_CortexA15x2"; + arm,vexpress,site = <0xf>; + compatible = "arm,rtsm_ve,cortex_a15x2", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + serial1 = &v2m_serial1; + serial2 = &v2m_serial2; + serial3 = &v2m_serial3; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <1>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0 0x80000000 0 0x80000000>; + }; + + gic: interrupt-controller@2c001000 { + compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0x2c001000 0 0x1000>, + <0 0x2c002000 0 0x1000>, + <0 0x2c004000 0 0x2000>, + <0 0x2c006000 0 0x2000>; + interrupts = <1 9 0xf04>; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = <1 13 0xf08>, + <1 14 0xf08>, + <1 11 0xf08>, + <1 10 0xf08>; + }; + + dcc { + compatible = "arm,vexpress,config-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + /* ACLK clock to the AXI master port on the test chip */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + freq-range = <30000000 50000000>; + #clock-cells = <0>; + clock-output-names = "extsaxiclk"; + }; + + oscclk1: osc@1 { + /* Reference clock for the CLCD */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 1>; + freq-range = <10000000 80000000>; + #clock-cells = <0>; + clock-output-names = "clcdclk"; + }; + + smbclk: oscclk2: osc@2 { + /* Reference clock for the test chip internal PLLs */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 2>; + freq-range = <33000000 100000000>; + #clock-cells = <0>; + clock-output-names = "tcrefclk"; + }; + }; + + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0 0x08000000 0x04000000>, + <1 0 0 0x14000000 0x04000000>, + <2 0 0 0x18000000 0x04000000>, + <3 0 0 0x1c000000 0x04000000>, + <4 0 0 0x0c000000 0x04000000>, + <5 0 0 0x10000000 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <0 0 0 &gic 0 0 4>, + <0 0 1 &gic 0 1 4>, + <0 0 2 &gic 0 2 4>, + <0 0 3 &gic 0 3 4>, + <0 0 4 &gic 0 4 4>, + <0 0 5 &gic 0 5 4>, + <0 0 6 &gic 0 6 4>, + <0 0 7 &gic 0 7 4>, + <0 0 8 &gic 0 8 4>, + <0 0 9 &gic 0 9 4>, + <0 0 10 &gic 0 10 4>, + <0 0 11 &gic 0 11 4>, + <0 0 12 &gic 0 12 4>, + <0 0 13 &gic 0 13 4>, + <0 0 14 &gic 0 14 4>, + <0 0 15 &gic 0 15 4>, + <0 0 16 &gic 0 16 4>, + <0 0 17 &gic 0 17 4>, + <0 0 18 &gic 0 18 4>, + <0 0 19 &gic 0 19 4>, + <0 0 20 &gic 0 20 4>, + <0 0 21 &gic 0 21 4>, + <0 0 22 &gic 0 22 4>, + <0 0 23 &gic 0 23 4>, + <0 0 24 &gic 0 24 4>, + <0 0 25 &gic 0 25 4>, + <0 0 26 &gic 0 26 4>, + <0 0 27 &gic 0 27 4>, + <0 0 28 &gic 0 28 4>, + <0 0 29 &gic 0 29 4>, + <0 0 30 &gic 0 30 4>, + <0 0 31 &gic 0 31 4>, + <0 0 32 &gic 0 32 4>, + <0 0 33 &gic 0 33 4>, + <0 0 34 &gic 0 34 4>, + <0 0 35 &gic 0 35 4>, + <0 0 36 &gic 0 36 4>, + <0 0 37 &gic 0 37 4>, + <0 0 38 &gic 0 38 4>, + <0 0 39 &gic 0 39 4>, + <0 0 40 &gic 0 40 4>, + <0 0 41 &gic 0 41 4>, + <0 0 42 &gic 0 42 4>; + + /include/ "rtsm_ve-motherboard.dtsi" + }; +}; + +/include/ "clcd-panels.dtsi" diff -Nur linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts --- linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a15x4.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,177 @@ +/* + * ARM Ltd. Fast Models + * + * Versatile Express (VE) system model + * ARMCortexA15x4CT + * + * RTSM_VE_Cortex_A15x4.lisa + */ + +/dts-v1/; + +/ { + model = "RTSM_VE_CortexA15x4"; + arm,vexpress,site = <0xf>; + compatible = "arm,rtsm_ve,cortex_a15x4", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + serial1 = &v2m_serial1; + serial2 = &v2m_serial2; + serial3 = &v2m_serial3; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <1>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <2>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <3>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0 0x80000000 0 0x80000000>; + }; + + gic: interrupt-controller@2c001000 { + compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0x2c001000 0 0x1000>, + <0 0x2c002000 0 0x1000>, + <0 0x2c004000 0 0x2000>, + <0 0x2c006000 0 0x2000>; + interrupts = <1 9 0xf04>; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = <1 13 0xf08>, + <1 14 0xf08>, + <1 11 0xf08>, + <1 10 0xf08>; + }; + + dcc { + compatible = "arm,vexpress,config-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + /* ACLK clock to the AXI master port on the test chip */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + freq-range = <30000000 50000000>; + #clock-cells = <0>; + clock-output-names = "extsaxiclk"; + }; + + oscclk1: osc@1 { + /* Reference clock for the CLCD */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 1>; + freq-range = <10000000 80000000>; + #clock-cells = <0>; + clock-output-names = "clcdclk"; + }; + + smbclk: oscclk2: osc@2 { + /* Reference clock for the test chip internal PLLs */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 2>; + freq-range = <33000000 100000000>; + #clock-cells = <0>; + clock-output-names = "tcrefclk"; + }; + }; + + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0 0x08000000 0x04000000>, + <1 0 0 0x14000000 0x04000000>, + <2 0 0 0x18000000 0x04000000>, + <3 0 0 0x1c000000 0x04000000>, + <4 0 0 0x0c000000 0x04000000>, + <5 0 0 0x10000000 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <0 0 0 &gic 0 0 4>, + <0 0 1 &gic 0 1 4>, + <0 0 2 &gic 0 2 4>, + <0 0 3 &gic 0 3 4>, + <0 0 4 &gic 0 4 4>, + <0 0 5 &gic 0 5 4>, + <0 0 6 &gic 0 6 4>, + <0 0 7 &gic 0 7 4>, + <0 0 8 &gic 0 8 4>, + <0 0 9 &gic 0 9 4>, + <0 0 10 &gic 0 10 4>, + <0 0 11 &gic 0 11 4>, + <0 0 12 &gic 0 12 4>, + <0 0 13 &gic 0 13 4>, + <0 0 14 &gic 0 14 4>, + <0 0 15 &gic 0 15 4>, + <0 0 16 &gic 0 16 4>, + <0 0 17 &gic 0 17 4>, + <0 0 18 &gic 0 18 4>, + <0 0 19 &gic 0 19 4>, + <0 0 20 &gic 0 20 4>, + <0 0 21 &gic 0 21 4>, + <0 0 22 &gic 0 22 4>, + <0 0 23 &gic 0 23 4>, + <0 0 24 &gic 0 24 4>, + <0 0 25 &gic 0 25 4>, + <0 0 26 &gic 0 26 4>, + <0 0 27 &gic 0 27 4>, + <0 0 28 &gic 0 28 4>, + <0 0 29 &gic 0 29 4>, + <0 0 30 &gic 0 30 4>, + <0 0 31 &gic 0 31 4>, + <0 0 32 &gic 0 32 4>, + <0 0 33 &gic 0 33 4>, + <0 0 34 &gic 0 34 4>, + <0 0 35 &gic 0 35 4>, + <0 0 36 &gic 0 36 4>, + <0 0 37 &gic 0 37 4>, + <0 0 38 &gic 0 38 4>, + <0 0 39 &gic 0 39 4>, + <0 0 40 &gic 0 40 4>, + <0 0 41 &gic 0 41 4>, + <0 0 42 &gic 0 42 4>; + + /include/ "rtsm_ve-motherboard.dtsi" + }; +}; + +/include/ "clcd-panels.dtsi" diff -Nur linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts --- linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a9x2.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,171 @@ +/* + * ARM Ltd. Fast Models + * + * Versatile Express (VE) system model + * ARMCortexA9MPx2CT + * + * RTSM_VE_Cortex_A9x2.lisa + */ + +/dts-v1/; + +/ { + model = "RTSM_VE_CortexA9x2"; + arm,vexpress,site = <0xf>; + compatible = "arm,rtsm_ve,cortex_a9x2", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + serial1 = &v2m_serial1; + serial2 = &v2m_serial2; + serial3 = &v2m_serial3; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <1>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x80000000>; + }; + + scu@2c000000 { + compatible = "arm,cortex-a9-scu"; + reg = <0x2c000000 0x58>; + }; + + timer@2c000600 { + compatible = "arm,cortex-a9-twd-timer"; + reg = <0x2c000600 0x20>; + interrupts = <1 13 0xf04>; + }; + + watchdog@2c000620 { + compatible = "arm,cortex-a9-twd-wdt"; + reg = <0x2c000620 0x20>; + interrupts = <1 14 0xf04>; + }; + + gic: interrupt-controller@2c001000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x2c001000 0x1000>, + <0x2c000100 0x100>; + }; + + dcc { + compatible = "arm,vexpress,config-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + /* ACLK clock to the AXI master port on the test chip */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + freq-range = <30000000 50000000>; + #clock-cells = <0>; + clock-output-names = "extsaxiclk"; + }; + + oscclk1: osc@1 { + /* Reference clock for the CLCD */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 1>; + freq-range = <10000000 80000000>; + #clock-cells = <0>; + clock-output-names = "clcdclk"; + }; + + smbclk: oscclk2: osc@2 { + /* Reference clock for the test chip internal PLLs */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 2>; + freq-range = <33000000 100000000>; + #clock-cells = <0>; + clock-output-names = "tcrefclk"; + }; + }; + + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x04000000>, + <1 0 0x14000000 0x04000000>, + <2 0 0x18000000 0x04000000>, + <3 0 0x1c000000 0x04000000>, + <4 0 0x0c000000 0x04000000>, + <5 0 0x10000000 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <0 0 0 &gic 0 0 4>, + <0 0 1 &gic 0 1 4>, + <0 0 2 &gic 0 2 4>, + <0 0 3 &gic 0 3 4>, + <0 0 4 &gic 0 4 4>, + <0 0 5 &gic 0 5 4>, + <0 0 6 &gic 0 6 4>, + <0 0 7 &gic 0 7 4>, + <0 0 8 &gic 0 8 4>, + <0 0 9 &gic 0 9 4>, + <0 0 10 &gic 0 10 4>, + <0 0 11 &gic 0 11 4>, + <0 0 12 &gic 0 12 4>, + <0 0 13 &gic 0 13 4>, + <0 0 14 &gic 0 14 4>, + <0 0 15 &gic 0 15 4>, + <0 0 16 &gic 0 16 4>, + <0 0 17 &gic 0 17 4>, + <0 0 18 &gic 0 18 4>, + <0 0 19 &gic 0 19 4>, + <0 0 20 &gic 0 20 4>, + <0 0 21 &gic 0 21 4>, + <0 0 22 &gic 0 22 4>, + <0 0 23 &gic 0 23 4>, + <0 0 24 &gic 0 24 4>, + <0 0 25 &gic 0 25 4>, + <0 0 26 &gic 0 26 4>, + <0 0 27 &gic 0 27 4>, + <0 0 28 &gic 0 28 4>, + <0 0 29 &gic 0 29 4>, + <0 0 30 &gic 0 30 4>, + <0 0 31 &gic 0 31 4>, + <0 0 32 &gic 0 32 4>, + <0 0 33 &gic 0 33 4>, + <0 0 34 &gic 0 34 4>, + <0 0 35 &gic 0 35 4>, + <0 0 36 &gic 0 36 4>, + <0 0 37 &gic 0 37 4>, + <0 0 38 &gic 0 38 4>, + <0 0 39 &gic 0 39 4>, + <0 0 40 &gic 0 40 4>, + <0 0 41 &gic 0 41 4>, + <0 0 42 &gic 0 42 4>; + + /include/ "rtsm_ve-motherboard.dtsi" + }; +}; + +/include/ "clcd-panels.dtsi" diff -Nur linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts --- linux-3.10.30/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-cortex_a9x4.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,183 @@ +/* + * ARM Ltd. Fast Models + * + * Versatile Express (VE) system model + * ARMCortexA9MPx4CT + * + * RTSM_VE_Cortex_A9x4.lisa + */ + +/dts-v1/; + +/ { + model = "RTSM_VE_CortexA9x4"; + arm,vexpress,site = <0xf>; + compatible = "arm,rtsm_ve,cortex_a9x4", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <1>; + #size-cells = <1>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + serial1 = &v2m_serial1; + serial2 = &v2m_serial2; + serial3 = &v2m_serial3; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <1>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <2>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <3>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x80000000>; + }; + + scu@2c000000 { + compatible = "arm,cortex-a9-scu"; + reg = <0x2c000000 0x58>; + }; + + timer@2c000600 { + compatible = "arm,cortex-a9-twd-timer"; + reg = <0x2c000600 0x20>; + interrupts = <1 13 0xf04>; + }; + + watchdog@2c000620 { + compatible = "arm,cortex-a9-twd-wdt"; + reg = <0x2c000620 0x20>; + interrupts = <1 14 0xf04>; + }; + + gic: interrupt-controller@2c001000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x2c001000 0x1000>, + <0x2c000100 0x100>; + }; + + dcc { + compatible = "arm,vexpress,config-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + /* ACLK clock to the AXI master port on the test chip */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + freq-range = <30000000 50000000>; + #clock-cells = <0>; + clock-output-names = "extsaxiclk"; + }; + + oscclk1: osc@1 { + /* Reference clock for the CLCD */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 1>; + freq-range = <10000000 80000000>; + #clock-cells = <0>; + clock-output-names = "clcdclk"; + }; + + smbclk: oscclk2: osc@2 { + /* Reference clock for the test chip internal PLLs */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 2>; + freq-range = <33000000 100000000>; + #clock-cells = <0>; + clock-output-names = "tcrefclk"; + }; + }; + + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x04000000>, + <1 0 0x14000000 0x04000000>, + <2 0 0x18000000 0x04000000>, + <3 0 0x1c000000 0x04000000>, + <4 0 0x0c000000 0x04000000>, + <5 0 0x10000000 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <0 0 0 &gic 0 0 4>, + <0 0 1 &gic 0 1 4>, + <0 0 2 &gic 0 2 4>, + <0 0 3 &gic 0 3 4>, + <0 0 4 &gic 0 4 4>, + <0 0 5 &gic 0 5 4>, + <0 0 6 &gic 0 6 4>, + <0 0 7 &gic 0 7 4>, + <0 0 8 &gic 0 8 4>, + <0 0 9 &gic 0 9 4>, + <0 0 10 &gic 0 10 4>, + <0 0 11 &gic 0 11 4>, + <0 0 12 &gic 0 12 4>, + <0 0 13 &gic 0 13 4>, + <0 0 14 &gic 0 14 4>, + <0 0 15 &gic 0 15 4>, + <0 0 16 &gic 0 16 4>, + <0 0 17 &gic 0 17 4>, + <0 0 18 &gic 0 18 4>, + <0 0 19 &gic 0 19 4>, + <0 0 20 &gic 0 20 4>, + <0 0 21 &gic 0 21 4>, + <0 0 22 &gic 0 22 4>, + <0 0 23 &gic 0 23 4>, + <0 0 24 &gic 0 24 4>, + <0 0 25 &gic 0 25 4>, + <0 0 26 &gic 0 26 4>, + <0 0 27 &gic 0 27 4>, + <0 0 28 &gic 0 28 4>, + <0 0 29 &gic 0 29 4>, + <0 0 30 &gic 0 30 4>, + <0 0 31 &gic 0 31 4>, + <0 0 32 &gic 0 32 4>, + <0 0 33 &gic 0 33 4>, + <0 0 34 &gic 0 34 4>, + <0 0 35 &gic 0 35 4>, + <0 0 36 &gic 0 36 4>, + <0 0 37 &gic 0 37 4>, + <0 0 38 &gic 0 38 4>, + <0 0 39 &gic 0 39 4>, + <0 0 40 &gic 0 40 4>, + <0 0 41 &gic 0 41 4>, + <0 0 42 &gic 0 42 4>; + + /include/ "rtsm_ve-motherboard.dtsi" + }; +}; + +/include/ "clcd-panels.dtsi" diff -Nur linux-3.10.30/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi --- linux-3.10.30/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-motherboard.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,231 @@ +/* + * ARM Ltd. Fast Models + * + * Versatile Express (VE) system model + * Motherboard component + * + * VEMotherBoard.lisa + */ + + motherboard { + compatible = "arm,vexpress,v2m-p1", "simple-bus"; + arm,hbi = <0x190>; + arm,vexpress,site = <0>; + arm,v2m-memory-map = "rs1"; + #address-cells = <2>; /* SMB chipselect number and offset */ + #size-cells = <1>; + #interrupt-cells = <1>; + ranges; + + flash@0,00000000 { + compatible = "arm,vexpress-flash", "cfi-flash"; + reg = <0 0x00000000 0x04000000>, + <4 0x00000000 0x04000000>; + bank-width = <4>; + }; + + vram@2,00000000 { + compatible = "arm,vexpress-vram"; + reg = <2 0x00000000 0x00800000>; + }; + + ethernet@2,02000000 { + compatible = "smsc,lan91c111"; + reg = <2 0x02000000 0x10000>; + interrupts = <15>; + }; + + iofpga@3,00000000 { + compatible = "arm,amba-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 3 0 0x200000>; + + v2m_sysreg: sysreg@010000 { + compatible = "arm,vexpress-sysreg"; + reg = <0x010000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + }; + + v2m_sysctl: sysctl@020000 { + compatible = "arm,sp810", "arm,primecell"; + reg = <0x020000 0x1000>; + clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&smbclk>; + clock-names = "refclk", "timclk", "apb_pclk"; + #clock-cells = <1>; + clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3"; + }; + + aaci@040000 { + compatible = "arm,pl041", "arm,primecell"; + reg = <0x040000 0x1000>; + interrupts = <11>; + clocks = <&smbclk>; + clock-names = "apb_pclk"; + }; + + mmci@050000 { + compatible = "arm,pl180", "arm,primecell"; + reg = <0x050000 0x1000>; + interrupts = <9 10>; + cd-gpios = <&v2m_sysreg 0 0>; + wp-gpios = <&v2m_sysreg 1 0>; + max-frequency = <12000000>; + vmmc-supply = <&v2m_fixed_3v3>; + clocks = <&v2m_clk24mhz>, <&smbclk>; + clock-names = "mclk", "apb_pclk"; + }; + + kmi@060000 { + compatible = "arm,pl050", "arm,primecell"; + reg = <0x060000 0x1000>; + interrupts = <12>; + clocks = <&v2m_clk24mhz>, <&smbclk>; + clock-names = "KMIREFCLK", "apb_pclk"; + }; + + kmi@070000 { + compatible = "arm,pl050", "arm,primecell"; + reg = <0x070000 0x1000>; + interrupts = <13>; + clocks = <&v2m_clk24mhz>, <&smbclk>; + clock-names = "KMIREFCLK", "apb_pclk"; + }; + + v2m_serial0: uart@090000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x090000 0x1000>; + interrupts = <5>; + clocks = <&v2m_clk24mhz>, <&smbclk>; + clock-names = "uartclk", "apb_pclk"; + }; + + v2m_serial1: uart@0a0000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0a0000 0x1000>; + interrupts = <6>; + clocks = <&v2m_clk24mhz>, <&smbclk>; + clock-names = "uartclk", "apb_pclk"; + }; + + v2m_serial2: uart@0b0000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0b0000 0x1000>; + interrupts = <7>; + clocks = <&v2m_clk24mhz>, <&smbclk>; + clock-names = "uartclk", "apb_pclk"; + }; + + v2m_serial3: uart@0c0000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x0c0000 0x1000>; + interrupts = <8>; + clocks = <&v2m_clk24mhz>, <&smbclk>; + clock-names = "uartclk", "apb_pclk"; + }; + + wdt@0f0000 { + compatible = "arm,sp805", "arm,primecell"; + reg = <0x0f0000 0x1000>; + interrupts = <0>; + clocks = <&v2m_refclk32khz>, <&smbclk>; + clock-names = "wdogclk", "apb_pclk"; + }; + + v2m_timer01: timer@110000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0x110000 0x1000>; + interrupts = <2>; + clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&smbclk>; + clock-names = "timclken1", "timclken2", "apb_pclk"; + }; + + v2m_timer23: timer@120000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0x120000 0x1000>; + interrupts = <3>; + clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&smbclk>; + clock-names = "timclken1", "timclken2", "apb_pclk"; + }; + + rtc@170000 { + compatible = "arm,pl031", "arm,primecell"; + reg = <0x170000 0x1000>; + interrupts = <4>; + clocks = <&smbclk>; + clock-names = "apb_pclk"; + }; + + clcd@1f0000 { + compatible = "arm,pl111", "arm,primecell"; + reg = <0x1f0000 0x1000>; + interrupts = <14>; + clocks = <&v2m_oscclk1>, <&smbclk>; + clock-names = "v2m:oscclk1", "apb_pclk"; + mode = "VGA"; + use_dma = <0>; + framebuffer = <0x18000000 0x00180000>; + }; + + virtio_block@0130000 { + compatible = "virtio,mmio"; + reg = <0x130000 0x200>; + interrupts = <42>; + }; + + }; + + v2m_fixed_3v3: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + v2m_clk24mhz: clk24mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "v2m:clk24mhz"; + }; + + v2m_refclk1mhz: refclk1mhz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <1000000>; + clock-output-names = "v2m:refclk1mhz"; + }; + + v2m_refclk32khz: refclk32khz { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "v2m:refclk32khz"; + }; + + mcc { + compatible = "simple-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + v2m_oscclk1: osc@1 { + /* CLCD clock */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 1>; + freq-range = <23750000 63500000>; + #clock-cells = <0>; + clock-output-names = "v2m:oscclk1"; + }; + + muxfpga@0 { + compatible = "arm,vexpress-muxfpga"; + arm,vexpress-sysreg,func = <7 0>; + }; + + shutdown@0 { + compatible = "arm,vexpress-shutdown"; + arm,vexpress-sysreg,func = <8 0>; + }; + }; + }; diff -Nur linux-3.10.30/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts --- linux-3.10.30/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-v2p-ca15x1-ca7x1.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,244 @@ +/* + * ARM Ltd. Fast Models + * + * Versatile Express (VE) system model + * ARMCortexA15x4CT + * ARMCortexA7x4CT + * RTSM_VE_Cortex_A15x1_A7x1.lisa + */ + +/dts-v1/; + +/memreserve/ 0xff000000 0x01000000; + +/ { + model = "RTSM_VE_CortexA15x1-A7x1"; + arm,vexpress,site = <0xf>; + compatible = "arm,rtsm_ve,cortex_a15x1_a7x1", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + serial1 = &v2m_serial1; + serial2 = &v2m_serial2; + serial3 = &v2m_serial3; + }; + + clusters { + #address-cells = <1>; + #size-cells = <0>; + + cluster0: cluster@0 { + reg = <0>; +// freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>; + cores { + #address-cells = <1>; + #size-cells = <0>; + + core0: core@0 { + reg = <0>; + }; + + }; + }; + + cluster1: cluster@1 { + reg = <1>; +// freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>; + cores { + #address-cells = <1>; + #size-cells = <0>; + + core1: core@0 { + reg = <0>; + }; + + }; + }; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0>; + cluster = <&cluster0>; + core = <&core0>; +// clock-frequency = <1000000000>; + cci-control-port = <&cci_control1>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x100>; + cluster = <&cluster1>; + core = <&core1>; +// clock-frequency = <800000000>; + cci-control-port = <&cci_control2>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0 0x80000000 0 0x80000000>; + }; + + cci@2c090000 { + compatible = "arm,cci-400", "arm,cci"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0 0x2c090000 0 0x1000>; + ranges = <0x0 0x0 0x2c090000 0x10000>; + + cci_control1: slave-if@4000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x4000 0x1000>; + }; + + cci_control2: slave-if@5000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x5000 0x1000>; + }; + }; + + dcscb@60000000 { + compatible = "arm,rtsm,dcscb"; + reg = <0 0x60000000 0 0x1000>; + }; + + gic: interrupt-controller@2c001000 { + compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0x2c001000 0 0x1000>, + <0 0x2c002000 0 0x1000>, + <0 0x2c004000 0 0x2000>, + <0 0x2c006000 0 0x2000>; + interrupts = <1 9 0xf04>; + + gic-cpuif@0 { + compatible = "arm,gic-cpuif"; + cpuif-id = <0>; + cpu = <&cpu0>; + }; + gic-cpuif@1 { + compatible = "arm,gic-cpuif"; + cpuif-id = <1>; + cpu = <&cpu1>; + }; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = <1 13 0xf08>, + <1 14 0xf08>, + <1 11 0xf08>, + <1 10 0xf08>; + }; + + dcc { + compatible = "arm,vexpress,config-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + /* ACLK clock to the AXI master port on the test chip */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + freq-range = <30000000 50000000>; + #clock-cells = <0>; + clock-output-names = "extsaxiclk"; + }; + + oscclk1: osc@1 { + /* Reference clock for the CLCD */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 1>; + freq-range = <10000000 80000000>; + #clock-cells = <0>; + clock-output-names = "clcdclk"; + }; + + smbclk: oscclk2: osc@2 { + /* Reference clock for the test chip internal PLLs */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 2>; + freq-range = <33000000 100000000>; + #clock-cells = <0>; + clock-output-names = "tcrefclk"; + }; + }; + + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0 0x08000000 0x04000000>, + <1 0 0 0x14000000 0x04000000>, + <2 0 0 0x18000000 0x04000000>, + <3 0 0 0x1c000000 0x04000000>, + <4 0 0 0x0c000000 0x04000000>, + <5 0 0 0x10000000 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <0 0 0 &gic 0 0 4>, + <0 0 1 &gic 0 1 4>, + <0 0 2 &gic 0 2 4>, + <0 0 3 &gic 0 3 4>, + <0 0 4 &gic 0 4 4>, + <0 0 5 &gic 0 5 4>, + <0 0 6 &gic 0 6 4>, + <0 0 7 &gic 0 7 4>, + <0 0 8 &gic 0 8 4>, + <0 0 9 &gic 0 9 4>, + <0 0 10 &gic 0 10 4>, + <0 0 11 &gic 0 11 4>, + <0 0 12 &gic 0 12 4>, + <0 0 13 &gic 0 13 4>, + <0 0 14 &gic 0 14 4>, + <0 0 15 &gic 0 15 4>, + <0 0 16 &gic 0 16 4>, + <0 0 17 &gic 0 17 4>, + <0 0 18 &gic 0 18 4>, + <0 0 19 &gic 0 19 4>, + <0 0 20 &gic 0 20 4>, + <0 0 21 &gic 0 21 4>, + <0 0 22 &gic 0 22 4>, + <0 0 23 &gic 0 23 4>, + <0 0 24 &gic 0 24 4>, + <0 0 25 &gic 0 25 4>, + <0 0 26 &gic 0 26 4>, + <0 0 27 &gic 0 27 4>, + <0 0 28 &gic 0 28 4>, + <0 0 29 &gic 0 29 4>, + <0 0 30 &gic 0 30 4>, + <0 0 31 &gic 0 31 4>, + <0 0 32 &gic 0 32 4>, + <0 0 33 &gic 0 33 4>, + <0 0 34 &gic 0 34 4>, + <0 0 35 &gic 0 35 4>, + <0 0 36 &gic 0 36 4>, + <0 0 37 &gic 0 37 4>, + <0 0 38 &gic 0 38 4>, + <0 0 39 &gic 0 39 4>, + <0 0 40 &gic 0 40 4>, + <0 0 41 &gic 0 41 4>, + <0 0 42 &gic 0 42 4>; + + /include/ "rtsm_ve-motherboard.dtsi" + }; +}; + +/include/ "clcd-panels.dtsi" diff -Nur linux-3.10.30/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts --- linux-3.10.30/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/rtsm_ve-v2p-ca15x4-ca7x4.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,358 @@ +/* + * ARM Ltd. Fast Models + * + * Versatile Express (VE) system model + * ARMCortexA15x4CT + * ARMCortexA7x4CT + * RTSM_VE_Cortex_A15x4_A7x4.lisa + */ + +/dts-v1/; + +/memreserve/ 0xff000000 0x01000000; + +/ { + model = "RTSM_VE_CortexA15x4-A7x4"; + arm,vexpress,site = <0xf>; + compatible = "arm,rtsm_ve,cortex_a15x4_a7x4", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + serial1 = &v2m_serial1; + serial2 = &v2m_serial2; + serial3 = &v2m_serial3; + }; + + clusters { + #address-cells = <1>; + #size-cells = <0>; + + cluster0: cluster@0 { + reg = <0>; +// freqs = <500000000 600000000 700000000 800000000 900000000 1000000000 1100000000 1200000000>; + cores { + #address-cells = <1>; + #size-cells = <0>; + + core0: core@0 { + reg = <0>; + }; + + core1: core@1 { + reg = <1>; + }; + + core2: core@2 { + reg = <2>; + }; + + core3: core@3 { + reg = <3>; + }; + + }; + }; + + cluster1: cluster@1 { + reg = <1>; +// freqs = <350000000 400000000 500000000 600000000 700000000 800000000 900000000 1000000000>; + cores { + #address-cells = <1>; + #size-cells = <0>; + + core4: core@0 { + reg = <0>; + }; + + core5: core@1 { + reg = <1>; + }; + + core6: core@2 { + reg = <2>; + }; + + core7: core@3 { + reg = <3>; + }; + + }; + }; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0>; + cluster = <&cluster0>; + core = <&core0>; +// clock-frequency = <1000000000>; + cci-control-port = <&cci_control1>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <1>; + cluster = <&cluster0>; + core = <&core1>; +// clock-frequency = <1000000000>; + cci-control-port = <&cci_control1>; + }; + + cpu2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <2>; + cluster = <&cluster0>; + core = <&core2>; +// clock-frequency = <1000000000>; + cci-control-port = <&cci_control1>; + }; + + cpu3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <3>; + cluster = <&cluster0>; + core = <&core3>; +// clock-frequency = <1000000000>; + cci-control-port = <&cci_control1>; + }; + + cpu4: cpu@4 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x100>; + cluster = <&cluster1>; + core = <&core4>; +// clock-frequency = <800000000>; + cci-control-port = <&cci_control2>; + }; + + cpu5: cpu@5 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x101>; + cluster = <&cluster1>; + core = <&core5>; +// clock-frequency = <800000000>; + cci-control-port = <&cci_control2>; + }; + + cpu6: cpu@6 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x102>; + cluster = <&cluster1>; + core = <&core6>; +// clock-frequency = <800000000>; + cci-control-port = <&cci_control2>; + }; + + cpu7: cpu@7 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x103>; + cluster = <&cluster1>; + core = <&core7>; +// clock-frequency = <800000000>; + cci-control-port = <&cci_control2>; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0 0x80000000 0 0x80000000>; + }; + + cci@2c090000 { + compatible = "arm,cci-400", "arm,cci"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0 0x2c090000 0 0x1000>; + ranges = <0x0 0x0 0x2c090000 0x10000>; + + cci_control1: slave-if@4000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x4000 0x1000>; + }; + + cci_control2: slave-if@5000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x5000 0x1000>; + }; + }; + + dcscb@60000000 { + compatible = "arm,rtsm,dcscb"; + reg = <0 0x60000000 0 0x1000>; + }; + + gic: interrupt-controller@2c001000 { + compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0 0x2c001000 0 0x1000>, + <0 0x2c002000 0 0x1000>, + <0 0x2c004000 0 0x2000>, + <0 0x2c006000 0 0x2000>; + interrupts = <1 9 0xf04>; + + gic-cpuif@0 { + compatible = "arm,gic-cpuif"; + cpuif-id = <0>; + cpu = <&cpu0>; + }; + gic-cpuif@1 { + compatible = "arm,gic-cpuif"; + cpuif-id = <1>; + cpu = <&cpu1>; + }; + gic-cpuif@2 { + compatible = "arm,gic-cpuif"; + cpuif-id = <2>; + cpu = <&cpu2>; + }; + gic-cpuif@3 { + compatible = "arm,gic-cpuif"; + cpuif-id = <3>; + cpu = <&cpu3>; + }; + gic-cpuif@4 { + compatible = "arm,gic-cpuif"; + cpuif-id = <4>; + cpu = <&cpu4>; + }; + gic-cpuif@5 { + compatible = "arm,gic-cpuif"; + cpuif-id = <5>; + cpu = <&cpu5>; + }; + gic-cpuif@6 { + compatible = "arm,gic-cpuif"; + cpuif-id = <6>; + cpu = <&cpu6>; + }; + gic-cpuif@7 { + compatible = "arm,gic-cpuif"; + cpuif-id = <7>; + cpu = <&cpu7>; + }; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = <1 13 0xf08>, + <1 14 0xf08>, + <1 11 0xf08>, + <1 10 0xf08>; + }; + + dcc { + compatible = "arm,vexpress,config-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + /* ACLK clock to the AXI master port on the test chip */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + freq-range = <30000000 50000000>; + #clock-cells = <0>; + clock-output-names = "extsaxiclk"; + }; + + oscclk1: osc@1 { + /* Reference clock for the CLCD */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 1>; + freq-range = <10000000 80000000>; + #clock-cells = <0>; + clock-output-names = "clcdclk"; + }; + + smbclk: oscclk2: osc@2 { + /* Reference clock for the test chip internal PLLs */ + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 2>; + freq-range = <33000000 100000000>; + #clock-cells = <0>; + clock-output-names = "tcrefclk"; + }; + }; + + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0 0x08000000 0x04000000>, + <1 0 0 0x14000000 0x04000000>, + <2 0 0 0x18000000 0x04000000>, + <3 0 0 0x1c000000 0x04000000>, + <4 0 0 0x0c000000 0x04000000>, + <5 0 0 0x10000000 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <0 0 0 &gic 0 0 4>, + <0 0 1 &gic 0 1 4>, + <0 0 2 &gic 0 2 4>, + <0 0 3 &gic 0 3 4>, + <0 0 4 &gic 0 4 4>, + <0 0 5 &gic 0 5 4>, + <0 0 6 &gic 0 6 4>, + <0 0 7 &gic 0 7 4>, + <0 0 8 &gic 0 8 4>, + <0 0 9 &gic 0 9 4>, + <0 0 10 &gic 0 10 4>, + <0 0 11 &gic 0 11 4>, + <0 0 12 &gic 0 12 4>, + <0 0 13 &gic 0 13 4>, + <0 0 14 &gic 0 14 4>, + <0 0 15 &gic 0 15 4>, + <0 0 16 &gic 0 16 4>, + <0 0 17 &gic 0 17 4>, + <0 0 18 &gic 0 18 4>, + <0 0 19 &gic 0 19 4>, + <0 0 20 &gic 0 20 4>, + <0 0 21 &gic 0 21 4>, + <0 0 22 &gic 0 22 4>, + <0 0 23 &gic 0 23 4>, + <0 0 24 &gic 0 24 4>, + <0 0 25 &gic 0 25 4>, + <0 0 26 &gic 0 26 4>, + <0 0 27 &gic 0 27 4>, + <0 0 28 &gic 0 28 4>, + <0 0 29 &gic 0 29 4>, + <0 0 30 &gic 0 30 4>, + <0 0 31 &gic 0 31 4>, + <0 0 32 &gic 0 32 4>, + <0 0 33 &gic 0 33 4>, + <0 0 34 &gic 0 34 4>, + <0 0 35 &gic 0 35 4>, + <0 0 36 &gic 0 36 4>, + <0 0 37 &gic 0 37 4>, + <0 0 38 &gic 0 38 4>, + <0 0 39 &gic 0 39 4>, + <0 0 40 &gic 0 40 4>, + <0 0 41 &gic 0 41 4>, + <0 0 42 &gic 0 42 4>; + + /include/ "rtsm_ve-motherboard.dtsi" + }; +}; + +/include/ "clcd-panels.dtsi" diff -Nur linux-3.10.30/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi --- linux-3.10.30/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -228,6 +228,7 @@ }; clcd@1f0000 { + status = "disabled"; compatible = "arm,pl111", "arm,primecell"; reg = <0x1f0000 0x1000>; interrupts = <14>; diff -Nur linux-3.10.30/arch/arm/boot/dts/vexpress-v2m.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2m.dtsi --- linux-3.10.30/arch/arm/boot/dts/vexpress-v2m.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2m.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -227,6 +227,7 @@ }; clcd@1f000 { + status = "disabled"; compatible = "arm,pl111", "arm,primecell"; reg = <0x1f000 0x1000>; interrupts = <14>; diff -Nur linux-3.10.30/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts --- linux-3.10.30/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts 2014-03-08 20:32:53.000000000 +0100 @@ -9,6 +9,8 @@ /dts-v1/; +/memreserve/ 0xbf000000 0x01000000; + / { model = "V2P-CA15"; arm,hbi = <0x237>; @@ -57,6 +59,8 @@ interrupts = <0 85 4>; clocks = <&oscclk5>; clock-names = "pxlclk"; + mode = "1024x768-16@60"; + framebuffer = <0 0xff000000 0 0x01000000>; }; memory-controller@2b0a0000 { diff -Nur linux-3.10.30/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts --- linux-3.10.30/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts 2014-03-08 20:32:53.000000000 +0100 @@ -9,11 +9,13 @@ /dts-v1/; +/memreserve/ 0xff000000 0x01000000; + / { model = "V2P-CA15_CA7"; arm,hbi = <0x249>; arm,vexpress,site = <0xf>; - compatible = "arm,vexpress,v2p-ca15_a7", "arm,vexpress"; + compatible = "arm,vexpress,v2p-ca15_a7", "arm,vexpress", "arm,generic"; interrupt-parent = <&gic>; #address-cells = <2>; #size-cells = <2>; @@ -29,44 +31,106 @@ i2c1 = &v2m_i2c_pcie; }; - cpus { + clusters { #address-cells = <1>; #size-cells = <0>; - cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a15"; + cluster0: cluster@0 { reg = <0>; + cores { + #address-cells = <1>; + #size-cells = <0>; + + core0: core@0 { + reg = <0>; + }; + + core1: core@1 { + reg = <1>; + }; + + }; }; - cpu1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a15"; + cluster1: cluster@1 { reg = <1>; + cores { + #address-cells = <1>; + #size-cells = <0>; + + core2: core@0 { + reg = <0>; + }; + + core3: core@1 { + reg = <1>; + }; + + core4: core@2 { + reg = <2>; + }; + }; }; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; cpu2: cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x100>; + cluster = <&cluster1>; + core = <&core2>; + clock-frequency = <800000000>; + cci-control-port = <&cci_control2>; }; cpu3: cpu@3 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x101>; + cluster = <&cluster1>; + core = <&core3>; + clock-frequency = <800000000>; + cci-control-port = <&cci_control2>; }; cpu4: cpu@4 { device_type = "cpu"; compatible = "arm,cortex-a7"; reg = <0x102>; + cluster = <&cluster1>; + core = <&core4>; + clock-frequency = <800000000>; + cci-control-port = <&cci_control2>; + }; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0>; + cluster = <&cluster0>; + core = <&core0>; + clock-frequency = <1000000000>; + cci-control-port = <&cci_control1>; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <1>; + cluster = <&cluster0>; + core = <&core1>; + clock-frequency = <1000000000>; + cci-control-port = <&cci_control1>; }; }; memory@80000000 { device_type = "memory"; - reg = <0 0x80000000 0 0x40000000>; + reg = <0 0x80000000 0 0x80000000>; }; wdt@2a490000 { @@ -81,6 +145,8 @@ compatible = "arm,hdlcd"; reg = <0 0x2b000000 0 0x1000>; interrupts = <0 85 4>; + mode = "1024x768-16@60"; + framebuffer = <0 0xff000000 0 0x01000000>; clocks = <&oscclk5>; clock-names = "pxlclk"; }; @@ -102,6 +168,64 @@ <0 0x2c004000 0 0x2000>, <0 0x2c006000 0 0x2000>; interrupts = <1 9 0xf04>; + + gic-cpuif@0 { + compatible = "arm,gic-cpuif"; + cpuif-id = <0>; + cpu = <&cpu0>; + }; + gic-cpuif@1 { + compatible = "arm,gic-cpuif"; + cpuif-id = <1>; + cpu = <&cpu1>; + }; + gic-cpuif@2 { + compatible = "arm,gic-cpuif"; + cpuif-id = <2>; + cpu = <&cpu2>; + }; + + gic-cpuif@3 { + compatible = "arm,gic-cpuif"; + cpuif-id = <3>; + cpu = <&cpu3>; + }; + + gic-cpuif@4 { + compatible = "arm,gic-cpuif"; + cpuif-id = <4>; + cpu = <&cpu4>; + }; + }; + + cci@2c090000 { + compatible = "arm,cci-400"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0 0x2c090000 0 0x1000>; + ranges = <0x0 0x0 0x2c090000 0x10000>; + + cci_control1: slave-if@4000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x4000 0x1000>; + }; + + cci_control2: slave-if@5000 { + compatible = "arm,cci-400-ctrl-if"; + interface-type = "ace"; + reg = <0x5000 0x1000>; + }; + }; + + cci-pmu@2c099000 { + compatible = "arm,cci-400-pmu"; + reg = <0 0x2c099000 0 0x6000>; + interrupts = <0 101 4>, + <0 102 4>, + <0 103 4>, + <0 104 4>, + <0 105 4>; }; memory-controller@7ffd0000 { @@ -125,6 +249,12 @@ clock-names = "apb_pclk"; }; + spc@7fff0000 { + compatible = "arm,vexpress-spc,v2p-ca15_a7","arm,vexpress-spc"; + reg = <0 0x7fff0000 0 0x1000>; + interrupts = <0 95 4>; + }; + timer { compatible = "arm,armv7-timer"; interrupts = <1 13 0xf08>, @@ -133,12 +263,21 @@ <1 10 0xf08>; }; - pmu { + pmu_a15 { compatible = "arm,cortex-a15-pmu"; + cluster = <&cluster0>; interrupts = <0 68 4>, <0 69 4>; }; + pmu_a7 { + compatible = "arm,cortex-a7-pmu"; + cluster = <&cluster1>; + interrupts = <0 128 4>, + <0 129 4>, + <0 130 4>; + }; + oscclk6a: oscclk6a { /* Reference 24MHz clock */ compatible = "fixed-clock"; @@ -147,6 +286,15 @@ clock-output-names = "oscclk6a"; }; + psci { + compatible = "arm,psci"; + method = "smc"; + cpu_suspend = <0x80100001>; + cpu_off = <0x80100002>; + cpu_on = <0x80100003>; + migrate = <0x80100004>; + }; + dcc { compatible = "arm,vexpress,config-bus"; arm,vexpress,config-bridge = <&v2m_sysreg>; diff -Nur linux-3.10.30/arch/arm/boot/dts/vexpress-v2p-ca5s.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2p-ca5s.dts --- linux-3.10.30/arch/arm/boot/dts/vexpress-v2p-ca5s.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2p-ca5s.dts 2014-03-08 20:32:53.000000000 +0100 @@ -9,6 +9,8 @@ /dts-v1/; +/memreserve/ 0xbf000000 0x01000000; + / { model = "V2P-CA5s"; arm,hbi = <0x225>; @@ -59,6 +61,8 @@ interrupts = <0 85 4>; clocks = <&oscclk3>; clock-names = "pxlclk"; + mode = "640x480-16@60"; + framebuffer = <0xbf000000 0x01000000>; }; memory-controller@2a150000 { diff -Nur linux-3.10.30/arch/arm/boot/dts/vexpress-v2p-ca9.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2p-ca9.dts --- linux-3.10.30/arch/arm/boot/dts/vexpress-v2p-ca9.dts 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vexpress-v2p-ca9.dts 2014-03-08 20:32:53.000000000 +0100 @@ -9,6 +9,8 @@ /dts-v1/; +/include/ "clcd-panels.dtsi" + / { model = "V2P-CA9"; arm,hbi = <0x191>; @@ -73,6 +75,8 @@ interrupts = <0 44 4>; clocks = <&oscclk1>, <&oscclk2>; clock-names = "clcdclk", "apb_pclk"; + mode = "XVGA"; + use_dma = <1>; }; memory-controller@100e0000 { diff -Nur linux-3.10.30/arch/arm/boot/dts/vf610-pinfunc.h linux-3.10.30-cubox-i/arch/arm/boot/dts/vf610-pinfunc.h --- linux-3.10.30/arch/arm/boot/dts/vf610-pinfunc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vf610-pinfunc.h 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,810 @@ +/* + * Copyright 2013 Freescale Semiconductor, 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. + * + */ + +#ifndef __DTS_VF610_PINFUNC_H +#define __DTS_VF610_PINFUNC_H + +/* + * The pin function ID for VF610 is a tuple of: + * + */ + +#define ALT0 0x0 +#define ALT1 0x1 +#define ALT2 0x2 +#define ALT3 0x3 +#define ALT4 0x4 +#define ALT5 0x5 +#define ALT6 0x6 +#define ALT7 0x7 + + +#define VF610_PAD_PTA6__GPIO_0 0x000 0x000 ALT0 0x0 +#define VF610_PAD_PTA6__RMII_CLKOUT 0x000 0x000 ALT1 0x0 +#define VF610_PAD_PTA6__RMII_CLKIN 0x000 0x2F0 ALT2 0x0 +#define VF610_PAD_PTA6__DCU1_TCON11 0x000 0x000 ALT4 0x0 +#define VF610_PAD_PTA6__DCU1_R2 0x000 0x000 ALT7 0x0 +#define VF610_PAD_PTA8__GPIO_1 0x004 0x000 ALT0 0x0 +#define VF610_PAD_PTA8__TCLK 0x004 0x000 ALT1 0x0 +#define VF610_PAD_PTA8__DCU0_R0 0x004 0x000 ALT4 0x0 +#define VF610_PAD_PTA8__MLB_CLK 0x004 0x354 ALT7 0x0 +#define VF610_PAD_PTA9__GPIO_2 0x008 0x000 ALT0 0x0 +#define VF610_PAD_PTA9__TDI 0x008 0x000 ALT1 0x0 +#define VF610_PAD_PTA9__RMII_CLKOUT 0x008 0x000 ALT2 0x0 +#define VF610_PAD_PTA9__RMII_CLKIN 0x008 0x2F0 ALT3 0x1 +#define VF610_PAD_PTA9__DCU0_R1 0x008 0x000 ALT4 0x0 +#define VF610_PAD_PTA9__WDOG_B 0x008 0x000 ALT6 0x0 +#define VF610_PAD_PTA10__GPIO_3 0x00C 0x000 ALT0 0x0 +#define VF610_PAD_PTA10__TDO 0x00C 0x000 ALT1 0x0 +#define VF610_PAD_PTA10__EXT_AUDIO_MCLK 0x00C 0x2EC ALT2 0x0 +#define VF610_PAD_PTA10__DCU0_G0 0x00C 0x000 ALT4 0x0 +#define VF610_PAD_PTA10__ENET_TS_CLKIN 0x00C 0x2F4 ALT6 0x0 +#define VF610_PAD_PTA10__MLB_SIGNAL 0x00C 0x35C ALT7 0x0 +#define VF610_PAD_PTA11__GPIO_4 0x010 0x000 ALT0 0x0 +#define VF610_PAD_PTA11__TMS 0x010 0x000 ALT1 0x0 +#define VF610_PAD_PTA11__DCU0_G1 0x010 0x000 ALT4 0x0 +#define VF610_PAD_PTA11__MLB_DATA 0x010 0x358 ALT7 0x0 +#define VF610_PAD_PTA12__GPIO_5 0x014 0x000 ALT0 0x0 +#define VF610_PAD_PTA12__TRACECK 0x014 0x000 ALT1 0x0 +#define VF610_PAD_PTA12__EXT_AUDIO_MCLK 0x014 0x2EC ALT2 0x1 +#define VF610_PAD_PTA12__VIU_DATA13 0x014 0x000 ALT6 0x0 +#define VF610_PAD_PTA12__I2C0_SCL 0x014 0x33C ALT7 0x0 +#define VF610_PAD_PTA16__GPIO_6 0x018 0x000 ALT0 0x0 +#define VF610_PAD_PTA16__TRACED0 0x018 0x000 ALT1 0x0 +#define VF610_PAD_PTA16__USB0_VBUS_EN 0x018 0x000 ALT2 0x0 +#define VF610_PAD_PTA16__ADC1_SE0 0x018 0x000 ALT3 0x0 +#define VF610_PAD_PTA16__LCD29 0x018 0x000 ALT4 0x0 +#define VF610_PAD_PTA16__SAI2_TX_BCLK 0x018 0x370 ALT5 0x0 +#define VF610_PAD_PTA16__VIU_DATA14 0x018 0x000 ALT6 0x0 +#define VF610_PAD_PTA16__I2C0_SDA 0x018 0x340 ALT7 0x0 +#define VF610_PAD_PTA17__GPIO_7 0x01C 0x000 ALT0 0x0 +#define VF610_PAD_PTA17__TRACED1 0x01C 0x000 ALT1 0x0 +#define VF610_PAD_PTA17__USB0_VBUS_OC 0x01C 0x000 ALT2 0x0 +#define VF610_PAD_PTA17__ADC1_SE1 0x01C 0x000 ALT3 0x0 +#define VF610_PAD_PTA17__LCD30 0x01C 0x000 ALT4 0x0 +#define VF610_PAD_PTA17__USB0_SOF_PULSE 0x01C 0x000 ALT5 0x0 +#define VF610_PAD_PTA17__VIU_DATA15 0x01C 0x000 ALT6 0x0 +#define VF610_PAD_PTA17__I2C1_SCL 0x01C 0x344 ALT7 0x0 +#define VF610_PAD_PTA18__GPIO_8 0x020 0x000 ALT0 0x0 +#define VF610_PAD_PTA18__TRACED2 0x020 0x000 ALT1 0x0 +#define VF610_PAD_PTA18__ADC0_SE0 0x020 0x000 ALT2 0x0 +#define VF610_PAD_PTA18__FTM1_QD_PHA 0x020 0x334 ALT3 0x0 +#define VF610_PAD_PTA18__LCD31 0x020 0x000 ALT4 0x0 +#define VF610_PAD_PTA18__SAI2_TX_DATA 0x020 0x000 ALT5 0x0 +#define VF610_PAD_PTA18__VIU_DATA16 0x020 0x000 ALT6 0x0 +#define VF610_PAD_PTA18__I2C1_SDA 0x020 0x348 ALT7 0x0 +#define VF610_PAD_PTA19__GPIO_9 0x024 0x000 ALT0 0x0 +#define VF610_PAD_PTA19__TRACED3 0x024 0x000 ALT1 0x0 +#define VF610_PAD_PTA19__ADC0_SE1 0x024 0x000 ALT2 0x0 +#define VF610_PAD_PTA19__FTM1_QD_PHB 0x024 0x338 ALT3 0x0 +#define VF610_PAD_PTA19__LCD32 0x024 0x000 ALT4 0x0 +#define VF610_PAD_PTA19__SAI2_TX_SYNC 0x024 0x000 ALT5 0x0 +#define VF610_PAD_PTA19__VIU_DATA17 0x024 0x000 ALT6 0x0 +#define VF610_PAD_PTA19__QSPI1_A_QSCK 0x024 0x374 ALT7 0x0 +#define VF610_PAD_PTA20__GPIO_10 0x028 0x000 ALT0 0x0 +#define VF610_PAD_PTA20__TRACED4 0x028 0x000 ALT1 0x0 +#define VF610_PAD_PTA20__LCD33 0x028 0x000 ALT4 0x0 +#define VF610_PAD_PTA20__UART3_TX 0x028 0x394 ALT6 0x0 +#define VF610_PAD_PTA20__DCU1_HSYNC 0x028 0x000 ALT7 0x0 +#define VF610_PAD_PTA21__GPIO_11 0x02C 0x000 ALT0 0x0 +#define VF610_PAD_PTA21__TRACED5 0x02C 0x000 ALT1 0x0 +#define VF610_PAD_PTA21__SAI2_RX_BCLK 0x02C 0x364 ALT5 0x0 +#define VF610_PAD_PTA21__UART3_RX 0x02C 0x390 ALT6 0x0 +#define VF610_PAD_PTA21__DCU1_VSYNC 0x02C 0x000 ALT7 0x0 +#define VF610_PAD_PTA22__GPIO_12 0x030 0x000 ALT0 0x0 +#define VF610_PAD_PTA22__TRACED6 0x030 0x000 ALT1 0x0 +#define VF610_PAD_PTA22__SAI2_RX_DATA 0x030 0x368 ALT5 0x0 +#define VF610_PAD_PTA22__I2C2_SCL 0x030 0x34C ALT6 0x0 +#define VF610_PAD_PTA22__DCU1_TAG 0x030 0x000 ALT7 0x0 +#define VF610_PAD_PTA23__GPIO_13 0x034 0x000 ALT0 0x0 +#define VF610_PAD_PTA23__TRACED7 0x034 0x000 ALT1 0x0 +#define VF610_PAD_PTA23__SAI2_RX_SYNC 0x034 0x36C ALT5 0x0 +#define VF610_PAD_PTA23__I2C2_SDA 0x034 0x350 ALT6 0x0 +#define VF610_PAD_PTA23__DCU1_DE 0x034 0x000 ALT7 0x0 +#define VF610_PAD_PTA24__GPIO_14 0x038 0x000 ALT0 0x0 +#define VF610_PAD_PTA24__TRACED8 0x038 0x000 ALT1 0x0 +#define VF610_PAD_PTA24__USB1_VBUS_EN 0x038 0x000 ALT2 0x0 +#define VF610_PAD_PTA24__ESDHC1_CLK 0x038 0x000 ALT5 0x0 +#define VF610_PAD_PTA24__DCU1_TCON4 0x038 0x000 ALT6 0x0 +#define VF610_PAD_PTA24__DDR_TEST_PAD_CTRL 0x038 0x000 ALT7 0x0 +#define VF610_PAD_PTA25__GPIO_15 0x03C 0x000 ALT0 0x0 +#define VF610_PAD_PTA25__TRACED9 0x03C 0x000 ALT1 0x0 +#define VF610_PAD_PTA25__USB1_VBUS_OC 0x03C 0x000 ALT2 0x0 +#define VF610_PAD_PTA25__ESDHC1_CMD 0x03C 0x000 ALT5 0x0 +#define VF610_PAD_PTA25__DCU1_TCON5 0x03C 0x000 ALT6 0x0 +#define VF610_PAD_PTA26__GPIO_16 0x040 0x000 ALT0 0x0 +#define VF610_PAD_PTA26__TRACED10 0x040 0x000 ALT1 0x0 +#define VF610_PAD_PTA26__SAI3_TX_BCLK 0x040 0x000 ALT2 0x0 +#define VF610_PAD_PTA26__ESDHC1_DAT0 0x040 0x000 ALT5 0x0 +#define VF610_PAD_PTA26__DCU1_TCON6 0x040 0x000 ALT6 0x0 +#define VF610_PAD_PTA27__GPIO_17 0x044 0x000 ALT0 0x0 +#define VF610_PAD_PTA27__TRACED11 0x044 0x000 ALT1 0x0 +#define VF610_PAD_PTA27__SAI3_RX_BCLK 0x044 0x000 ALT2 0x0 +#define VF610_PAD_PTA27__ESDHC1_DAT1 0x044 0x000 ALT5 0x0 +#define VF610_PAD_PTA27__DCU1_TCON7 0x044 0x000 ALT6 0x0 +#define VF610_PAD_PTA28__GPIO_18 0x048 0x000 ALT0 0x0 +#define VF610_PAD_PTA28__TRACED12 0x048 0x000 ALT1 0x0 +#define VF610_PAD_PTA28__SAI3_RX_DATA 0x048 0x000 ALT2 0x0 +#define VF610_PAD_PTA28__ENET1_1588_TMR0 0x048 0x000 ALT3 0x0 +#define VF610_PAD_PTA28__UART4_TX 0x048 0x000 ALT4 0x0 +#define VF610_PAD_PTA28__ESDHC1_DATA2 0x048 0x000 ALT5 0x0 +#define VF610_PAD_PTA28__DCU1_TCON8 0x048 0x000 ALT6 0x0 +#define VF610_PAD_PTA29__GPIO_19 0x04C 0x000 ALT0 0x0 +#define VF610_PAD_PTA29__TRACED13 0x04C 0x000 ALT1 0x0 +#define VF610_PAD_PTA29__SAI3_TX_DATA 0x04C 0x000 ALT2 0x0 +#define VF610_PAD_PTA29__ENET1_1588_TMR1 0x04C 0x000 ALT3 0x0 +#define VF610_PAD_PTA29__UART4_RX 0x04C 0x000 ALT4 0x0 +#define VF610_PAD_PTA29__ESDHC1_DAT3 0x04C 0x000 ALT5 0x0 +#define VF610_PAD_PTA29__DCU1_TCON9 0x04C 0x000 ALT6 0x0 +#define VF610_PAD_PTA30__GPIO_20 0x050 0x000 ALT0 0x0 +#define VF610_PAD_PTA30__TRACED14 0x050 0x000 ALT1 0x0 +#define VF610_PAD_PTA30__SAI3_RX_SYNC 0x050 0x000 ALT2 0x0 +#define VF610_PAD_PTA30__ENET1_1588_TMR2 0x050 0x000 ALT3 0x0 +#define VF610_PAD_PTA30__UART4_RTS 0x050 0x000 ALT4 0x0 +#define VF610_PAD_PTA30__I2C3_SCL 0x050 0x000 ALT5 0x0 +#define VF610_PAD_PTA30__UART3_TX 0x050 0x394 ALT7 0x1 +#define VF610_PAD_PTA31__GPIO_21 0x054 0x000 ALT0 0x0 +#define VF610_PAD_PTA31__TRACED15 0x054 0x000 ALT1 0x0 +#define VF610_PAD_PTA31__SAI3_TX_SYNC 0x054 0x000 ALT2 0x0 +#define VF610_PAD_PTA31__ENET1_1588_TMR3 0x054 0x000 ALT3 0x0 +#define VF610_PAD_PTA31__UART4_CTS 0x054 0x000 ALT4 0x0 +#define VF610_PAD_PTA31__I2C3_SDA 0x054 0x000 ALT5 0x0 +#define VF610_PAD_PTA31__UART3_RX 0x054 0x390 ALT7 0x1 +#define VF610_PAD_PTB0__GPIO_22 0x058 0x000 ALT0 0x0 +#define VF610_PAD_PTB0__FTM0_CH0 0x058 0x000 ALT1 0x0 +#define VF610_PAD_PTB0__ADC0_SE2 0x058 0x000 ALT2 0x0 +#define VF610_PAD_PTB0__TRACE_CTL 0x058 0x000 ALT3 0x0 +#define VF610_PAD_PTB0__LCD34 0x058 0x000 ALT4 0x0 +#define VF610_PAD_PTB0__SAI2_RX_BCLK 0x058 0x364 ALT5 0x1 +#define VF610_PAD_PTB0__VIU_DATA18 0x058 0x000 ALT6 0x0 +#define VF610_PAD_PTB0__QSPI1_A_QPCS0 0x058 0x000 ALT7 0x0 +#define VF610_PAD_PTB1__GPIO_23 0x05C 0x000 ALT0 0x0 +#define VF610_PAD_PTB1__FTM0_CH1 0x05C 0x000 ALT1 0x0 +#define VF610_PAD_PTB1__ADC0_SE3 0x05C 0x000 ALT2 0x0 +#define VF610_PAD_PTB1__SRC_RCON30 0x05C 0x000 ALT3 0x0 +#define VF610_PAD_PTB1__LCD35 0x05C 0x000 ALT4 0x0 +#define VF610_PAD_PTB1__SAI2_RX_DATA 0x05C 0x368 ALT5 0x1 +#define VF610_PAD_PTB1__VIU_DATA19 0x05C 0x000 ALT6 0x0 +#define VF610_PAD_PTB1__QSPI1_A_DATA3 0x05C 0x000 ALT7 0x0 +#define VF610_PAD_PTB2__GPIO_24 0x060 0x000 ALT0 0x0 +#define VF610_PAD_PTB2__FTM0_CH2 0x060 0x000 ALT1 0x0 +#define VF610_PAD_PTB2__ADC1_SE2 0x060 0x000 ALT2 0x0 +#define VF610_PAD_PTB2__SRC_RCON31 0x060 0x000 ALT3 0x0 +#define VF610_PAD_PTB2__LCD36 0x060 0x000 ALT4 0x0 +#define VF610_PAD_PTB2__SAI2_RX_SYNC 0x060 0x36C ALT5 0x1 +#define VF610_PAD_PTB2__VIDEO_IN0_DATA20 0x060 0x000 ALT6 0x0 +#define VF610_PAD_PTB2__QSPI1_A_DATA2 0x060 0x000 ALT7 0x0 +#define VF610_PAD_PTB3__GPIO_25 0x064 0x000 ALT0 0x0 +#define VF610_PAD_PTB3__FTM0_CH3 0x064 0x000 ALT1 0x0 +#define VF610_PAD_PTB3__ADC1_SE3 0x064 0x000 ALT2 0x0 +#define VF610_PAD_PTB3__PDB_EXTRIG 0x064 0x000 ALT3 0x0 +#define VF610_PAD_PTB3__LCD37 0x064 0x000 ALT4 0x0 +#define VF610_PAD_PTB3__VIU_DATA21 0x064 0x000 ALT6 0x0 +#define VF610_PAD_PTB3__QSPI1_A_DATA1 0x064 0x000 ALT7 0x0 +#define VF610_PAD_PTB4__GPIO_26 0x068 0x000 ALT0 0x0 +#define VF610_PAD_PTB4__FTM0_CH4 0x068 0x000 ALT1 0x0 +#define VF610_PAD_PTB4__UART1_TX 0x068 0x380 ALT2 0x0 +#define VF610_PAD_PTB4__ADC0_SE4 0x068 0x000 ALT3 0x0 +#define VF610_PAD_PTB4__LCD38 0x068 0x000 ALT4 0x0 +#define VF610_PAD_PTB4__VIU_FID 0x068 0x3A8 ALT5 0x0 +#define VF610_PAD_PTB4__VIU_DATA22 0x068 0x000 ALT6 0x0 +#define VF610_PAD_PTB4__QSPI1_A_DATA0 0x068 0x000 ALT7 0x0 +#define VF610_PAD_PTB5__GPIO_27 0x06C 0x000 ALT0 0x0 +#define VF610_PAD_PTB5__FTM0_CH5 0x06C 0x000 ALT1 0x0 +#define VF610_PAD_PTB5__UART1_RX 0x06C 0x37C ALT2 0x0 +#define VF610_PAD_PTB5__ADC1_SE4 0x06C 0x000 ALT3 0x0 +#define VF610_PAD_PTB5__LCD39 0x06C 0x000 ALT4 0x0 +#define VF610_PAD_PTB5__VIU_DE 0x06C 0x3A4 ALT5 0x0 +#define VF610_PAD_PTB5__QSPI1_A_DQS 0x06C 0x000 ALT7 0x0 +#define VF610_PAD_PTB6__GPIO_28 0x070 0x000 ALT0 0x0 +#define VF610_PAD_PTB6__FTM0_CH6 0x070 0x000 ALT1 0x0 +#define VF610_PAD_PTB6__UART1_RTS 0x070 0x000 ALT2 0x0 +#define VF610_PAD_PTB6__QSPI0_QPCS1_A 0x070 0x000 ALT3 0x0 +#define VF610_PAD_PTB6__LCD_LCD40 0x070 0x000 ALT4 0x0 +#define VF610_PAD_PTB6__FB_CLKOUT 0x070 0x000 ALT5 0x0 +#define VF610_PAD_PTB6__VIU_HSYNC 0x070 0x000 ALT6 0x0 +#define VF610_PAD_PTB6__UART2_TX 0x070 0x38C ALT7 0x0 +#define VF610_PAD_PTB7__GPIO_29 0x074 0x000 ALT0 0x0 +#define VF610_PAD_PTB7__FTM0_CH7 0x074 0x000 ALT1 0x0 +#define VF610_PAD_PTB7__UART1_CTS 0x074 0x378 ALT2 0x0 +#define VF610_PAD_PTB7__QSPI0_B_QPCS1 0x074 0x000 ALT3 0x0 +#define VF610_PAD_PTB7__LCD41 0x074 0x000 ALT4 0x0 +#define VF610_PAD_PTB7__VIU_VSYNC 0x074 0x000 ALT6 0x0 +#define VF610_PAD_PTB7__UART2_RX 0x074 0x388 ALT7 0x0 +#define VF610_PAD_PTB8__GPIO_30 0x078 0x000 ALT0 0x0 +#define VF610_PAD_PTB8__FTM1_CH0 0x078 0x32C ALT1 0x0 +#define VF610_PAD_PTB8__FTM1_QD_PHA 0x078 0x334 ALT3 0x1 +#define VF610_PAD_PTB8__VIU_DE 0x078 0x3A4 ALT5 0x1 +#define VF610_PAD_PTB8__DCU1_R6 0x078 0x000 ALT7 0x0 +#define VF610_PAD_PTB9__GPIO_31 0x07C 0x000 ALT0 0x0 +#define VF610_PAD_PTB9__FTM1_CH1 0x07C 0x330 ALT1 0x0 +#define VF610_PAD_PTB9__FTM1_QD_PHB 0x07C 0x338 ALT3 0x1 +#define VF610_PAD_PTB9__DCU1_R7 0x07C 0x000 ALT7 0x0 +#define VF610_PAD_PTB10__GPIO_32 0x080 0x000 ALT0 0x0 +#define VF610_PAD_PTB10__UART0_TX 0x080 0x000 ALT1 0x0 +#define VF610_PAD_PTB10__DCU0_TCON4 0x080 0x000 ALT4 0x0 +#define VF610_PAD_PTB10__VIU_DE 0x080 0x3A4 ALT5 0x2 +#define VF610_PAD_PTB10__CKO1 0x080 0x000 ALT6 0x0 +#define VF610_PAD_PTB10__ENET_TS_CLKIN 0x080 0x2F4 ALT7 0x1 +#define VF610_PAD_PTB11__GPIO_33 0x084 0x000 ALT0 0x0 +#define VF610_PAD_PTB11__UART0_RX 0x084 0x000 ALT1 0x0 +#define VF610_PAD_PTB11__DCU0_TCON5 0x084 0x000 ALT4 0x0 +#define VF610_PAD_PTB11__SNVS_ALARM_OUT_B 0x084 0x000 ALT5 0x0 +#define VF610_PAD_PTB11__CKO2 0x084 0x000 ALT6 0x0 +#define VF610_PAD_PTB11_ENET0_1588_TMR0 0x084 0x304 ALT7 0x0 +#define VF610_PAD_PTB12__GPIO_34 0x088 0x000 ALT0 0x0 +#define VF610_PAD_PTB12__UART0_RTS 0x088 0x000 ALT1 0x0 +#define VF610_PAD_PTB12__DSPI0_CS5 0x088 0x000 ALT3 0x0 +#define VF610_PAD_PTB12__DCU0_TCON6 0x088 0x000 ALT4 0x0 +#define VF610_PAD_PTB12__FB_AD1 0x088 0x000 ALT5 0x0 +#define VF610_PAD_PTB12__NMI 0x088 0x000 ALT6 0x0 +#define VF610_PAD_PTB12__ENET0_1588_TMR1 0x088 0x308 ALT7 0x0 +#define VF610_PAD_PTB13__GPIO_35 0x08C 0x000 ALT0 0x0 +#define VF610_PAD_PTB13__UART0_CTS 0x08C 0x000 ALT1 0x0 +#define VF610_PAD_PTB13__DSPI0_CS4 0x08C 0x000 ALT3 0x0 +#define VF610_PAD_PTB13__DCU0_TCON7 0x08C 0x000 ALT4 0x0 +#define VF610_PAD_PTB13__FB_AD0 0x08C 0x000 ALT5 0x0 +#define VF610_PAD_PTB13__TRACE_CTL 0x08C 0x000 ALT6 0x0 +#define VF610_PAD_PTB14__GPIO_36 0x090 0x000 ALT0 0x0 +#define VF610_PAD_PTB14__CAN0_RX 0x090 0x000 ALT1 0x0 +#define VF610_PAD_PTB14__I2C0_SCL 0x090 0x33C ALT2 0x1 +#define VF610_PAD_PTB14__DCU0_TCON8 0x090 0x000 ALT4 0x0 +#define VF610_PAD_PTB14__DCU1_PCLK 0x090 0x000 ALT7 0x0 +#define VF610_PAD_PTB15__GPIO_37 0x094 0x000 ALT0 0x0 +#define VF610_PAD_PTB15__CAN0_TX 0x094 0x000 ALT1 0x0 +#define VF610_PAD_PTB15__I2C0_SDA 0x094 0x340 ALT2 0x1 +#define VF610_PAD_PTB15__DCU0_TCON9 0x094 0x000 ALT4 0x0 +#define VF610_PAD_PTB15__VIU_PIX_CLK 0x094 0x3AC ALT7 0x0 +#define VF610_PAD_PTB16__GPIO_38 0x098 0x000 ALT0 0x0 +#define VF610_PAD_PTB16__CAN1_RX 0x098 0x000 ALT1 0x0 +#define VF610_PAD_PTB16__I2C1_SCL 0x098 0x344 ALT2 0x1 +#define VF610_PAD_PTB16__DCU0_TCON10 0x098 0x000 ALT4 0x0 +#define VF610_PAD_PTB17__GPIO_39 0x09C 0x000 ALT0 0x0 +#define VF610_PAD_PTB17__CAN1_TX 0x09C 0x000 ALT1 0x0 +#define VF610_PAD_PTB17__I2C1_SDA 0x09C 0x348 ALT2 0x1 +#define VF610_PAD_PTB17__DCU0_TCON11 0x09C 0x000 ALT4 0x0 +#define VF610_PAD_PTB18__GPIO_40 0x0A0 0x000 ALT0 0x0 +#define VF610_PAD_PTB18__DSPI0_CS1 0x0A0 0x000 ALT1 0x0 +#define VF610_PAD_PTB18__EXT_AUDIO_MCLK 0x0A0 0x2EC ALT2 0x2 +#define VF610_PAD_PTB18__VIU_DATA9 0x0A0 0x000 ALT6 0x0 +#define VF610_PAD_PTB19__GPIO_41 0x0A4 0x000 ALT0 0x0 +#define VF610_PAD_PTB19__DSPI0_CS0 0x0A4 0x000 ALT1 0x0 +#define VF610_PAD_PTB19__VIU_DATA10 0x0A4 0x000 ALT6 0x0 +#define VF610_PAD_PTB20__GPIO_42 0x0A8 0x000 ALT0 0x0 +#define VF610_PAD_PTB20__DSPI0_SIN 0x0A8 0x000 ALT1 0x0 +#define VF610_PAD_PTB20__LCD42 0x0A8 0x000 ALT4 0x0 +#define VF610_PAD_PTB20__VIU_DATA11 0x0A8 0x000 ALT6 0x0 +#define VF610_PAD_PTB21__GPIO_43 0x0AC 0x000 ALT0 0x0 +#define VF610_PAD_PTB21__DSPI0_SOUT 0x0AC 0x000 ALT1 0x0 +#define VF610_PAD_PTB21__LCD43 0x0AC 0x000 ALT4 0x0 +#define VF610_PAD_PTB21__VIU_DATA12 0x0AC 0x000 ALT6 0x0 +#define VF610_PAD_PTB21__DCU1_PCLK 0x0AC 0x000 ALT7 0x0 +#define VF610_PAD_PTB22__GPIO_44 0x0B0 0x000 ALT0 0x0 +#define VF610_PAD_PTB22__DSPI0_SCK 0x0B0 0x000 ALT1 0x0 +#define VF610_PAD_PTB22__VLCD 0x0B0 0x000 ALT4 0x0 +#define VF610_PAD_PTB22__VIU_FID 0x0B0 0x3A8 ALT5 0x1 +#define VF610_PAD_PTC0__GPIO_45 0x0B4 0x000 ALT0 0x0 +#define VF610_PAD_PTC0__ENET_RMII0_MDC 0x0B4 0x000 ALT1 0x0 +#define VF610_PAD_PTC0__FTM1_CH0 0x0B4 0x32C ALT2 0x1 +#define VF610_PAD_PTC0__DSPI0_CS3 0x0B4 0x000 ALT3 0x0 +#define VF610_PAD_PTC0__ESAI_SCKT 0x0B4 0x310 ALT4 0x0 +#define VF610_PAD_PTC0__ESDHC0_CLK 0x0B4 0x000 ALT5 0x0 +#define VF610_PAD_PTC0__VIU_DATA0 0x0B4 0x000 ALT6 0x0 +#define VF610_PAD_PTC0__SRC_RCON18 0x0B4 0x398 ALT7 0x0 +#define VF610_PAD_PTC1__GPIO_46 0x0B8 0x000 ALT0 0x0 +#define VF610_PAD_PTC1__ENET_RMII0_MDIO 0x0B8 0x000 ALT1 0x0 +#define VF610_PAD_PTC1__FTM1_CH1 0x0B8 0x330 ALT2 0x1 +#define VF610_PAD_PTC1__DSPI0_CS2 0x0B8 0x000 ALT3 0x0 +#define VF610_PAD_PTC1__ESAI_FST 0x0B8 0x30C ALT4 0x0 +#define VF610_PAD_PTC1__ESDHC0_CMD 0x0B8 0x000 ALT5 0x0 +#define VF610_PAD_PTC1__VIU_DATA1 0x0B8 0x000 ALT6 0x0 +#define VF610_PAD_PTC1__SRC_RCON19 0x0B8 0x39C ALT7 0x0 +#define VF610_PAD_PTC2__GPIO_47 0x0BC 0x000 ALT0 0x0 +#define VF610_PAD_PTC2__ENET_RMII0_CRS 0x0BC 0x000 ALT1 0x0 +#define VF610_PAD_PTC2__UART1_TX 0x0BC 0x380 ALT2 0x1 +#define VF610_PAD_PTC2__ESAI_SDO0 0x0BC 0x314 ALT4 0x0 +#define VF610_PAD_PTC2__ESDHC0_DAT0 0x0BC 0x000 ALT5 0x0 +#define VF610_PAD_PTC2__VIU_DATA2 0x0BC 0x000 ALT6 0x0 +#define VF610_PAD_PTC2__SRC_RCON20 0x0BC 0x3A0 ALT7 0x0 +#define VF610_PAD_PTC3__GPIO_48 0x0C0 0x000 ALT0 0x0 +#define VF610_PAD_PTC3__ENET_RMII0_RXD1 0x0C0 0x000 ALT1 0x0 +#define VF610_PAD_PTC3__UART1_RX 0x0C0 0x37C ALT2 0x1 +#define VF610_PAD_PTC3__ESAI_SDO1 0x0C0 0x318 ALT4 0x0 +#define VF610_PAD_PTC3__ESDHC0_DAT1 0x0C0 0x000 ALT5 0x0 +#define VF610_PAD_PTC3__VIU_DATA3 0x0C0 0x000 ALT6 0x0 +#define VF610_PAD_PTC3__DCU0_R0 0x0C0 0x000 ALT7 0x0 +#define VF610_PAD_PTC4__GPIO_49 0x0C4 0x000 ALT0 0x0 +#define VF610_PAD_PTC4__ENET_RMII0_RXD0 0x0C4 0x000 ALT1 0x0 +#define VF610_PAD_PTC4__UART1_RTS 0x0C4 0x000 ALT2 0x0 +#define VF610_PAD_PTC4__DSPI1_CS1 0x0C4 0x000 ALT3 0x0 +#define VF610_PAD_PTC4__ESAI_SDO2 0x0C4 0x31C ALT4 0x0 +#define VF610_PAD_PTC4__ESDHC0_DAT2 0x0C4 0x000 ALT5 0x0 +#define VF610_PAD_PTC4__VIU_DATA4 0x0C4 0x000 ALT6 0x0 +#define VF610_PAD_PTC4__DCU0_R1 0x0C4 0x000 ALT7 0x0 +#define VF610_PAD_PTC5__GPIO_50 0x0C8 0x000 ALT0 0x0 +#define VF610_PAD_PTC5__ENET_RMII0_RXER 0x0C8 0x000 ALT1 0x0 +#define VF610_PAD_PTC5__UART1_CTS 0x0C8 0x378 ALT2 0x1 +#define VF610_PAD_PTC5__DSPI1_CS0 0x0C8 0x300 ALT3 0x0 +#define VF610_PAD_PTC5__ESAI_SDO3 0x0C8 0x320 ALT4 0x0 +#define VF610_PAD_PTC5__ESDHC0_DAT3 0x0C8 0x000 ALT5 0x0 +#define VF610_PAD_PTC5__VIU_DATA5 0x0C8 0x000 ALT6 0x0 +#define VF610_PAD_PTC5__DCU0_G0 0x0C8 0x000 ALT7 0x0 +#define VF610_PAD_PTC6__GPIO_51 0x0CC 0x000 ALT0 0x0 +#define VF610_PAD_PTC6__ENET_RMII0_TXD1 0x0CC 0x000 ALT1 0x0 +#define VF610_PAD_PTC6__DSPI1_SIN 0x0CC 0x2FC ALT3 0x0 +#define VF610_PAD_PTC6__ESAI_SDI0 0x0CC 0x328 ALT4 0x0 +#define VF610_PAD_PTC6__ESDHC0_WP 0x0CC 0x000 ALT5 0x0 +#define VF610_PAD_PTC6__VIU_DATA6 0x0CC 0x000 ALT6 0x0 +#define VF610_PAD_PTC6__DCU0_G1 0x0CC 0x000 ALT7 0x0 +#define VF610_PAD_PTC7__GPIO_52 0x0D0 0x000 ALT0 0x0 +#define VF610_PAD_PTC7__ENET_RMII0_TXD0 0x0D0 0x000 ALT1 0x0 +#define VF610_PAD_PTC7__DSPI1_SOUT 0x0D0 0x000 ALT3 0x0 +#define VF610_PAD_PTC7__ESAI_SDI1 0x0D0 0x324 ALT4 0x0 +#define VF610_PAD_PTC7__VIU_DATA7 0x0D0 0x000 ALT6 0x0 +#define VF610_PAD_PTC7__DCU0_B0 0x0D0 0x000 ALT7 0x0 +#define VF610_PAD_PTC8__GPIO_53 0x0D4 0x000 ALT0 0x0 +#define VF610_PAD_PTC8__ENET_RMII0_TXEN 0x0D4 0x000 ALT1 0x0 +#define VF610_PAD_PTC8__DSPI1_SCK 0x0D4 0x2F8 ALT3 0x0 +#define VF610_PAD_PTC8__VIU_DATA8 0x0D4 0x000 ALT6 0x0 +#define VF610_PAD_PTC8__DCU0_B1 0x0D4 0x000 ALT7 0x0 +#define VF610_PAD_PTC9__GPIO_54 0x0D8 0x000 ALT0 0x0 +#define VF610_PAD_PTC9__ENET_RMII1_MDC 0x0D8 0x000 ALT1 0x0 +#define VF610_PAD_PTC9__ESAI_SCKT 0x0D8 0x310 ALT3 0x1 +#define VF610_PAD_PTC9__MLB_CLK 0x0D8 0x354 ALT6 0x1 +#define VF610_PAD_PTC9__DEBUG_OUT0 0x0D8 0x000 ALT7 0x0 +#define VF610_PAD_PTC10__GPIO_55 0x0DC 0x000 ALT0 0x0 +#define VF610_PAD_PTC10__ENET_RMII1_MDIO 0x0DC 0x000 ALT1 0x0 +#define VF610_PAD_PTC10__ESAI_FST 0x0DC 0x30C ALT3 0x1 +#define VF610_PAD_PTC10__MLB_SIGNAL 0x0DC 0x35C ALT6 0x1 +#define VF610_PAD_PTC10__DEBUG_OUT1 0x0DC 0x000 ALT7 0x0 +#define VF610_PAD_PTC11__GPIO_56 0x0E0 0x000 ALT0 0x0 +#define VF610_PAD_PTC11__ENET_RMII1_CRS 0x0E0 0x000 ALT1 0x0 +#define VF610_PAD_PTC11__ESAI_SDO0 0x0E0 0x314 ALT3 0x1 +#define VF610_PAD_PTC11__MLB_DATA 0x0E0 0x358 ALT6 0x1 +#define VF610_PAD_PTC11__DEBUG_OUT 0x0E0 0x000 ALT7 0x0 +#define VF610_PAD_PTC12__GPIO_57 0x0E4 0x000 ALT0 0x0 +#define VF610_PAD_PTC12__ENET_RMII_RXD1 0x0E4 0x000 ALT1 0x0 +#define VF610_PAD_PTC12__ESAI_SDO1 0x0E4 0x318 ALT3 0x1 +#define VF610_PAD_PTC12__SAI2_TX_BCLK 0x0E4 0x370 ALT5 0x1 +#define VF610_PAD_PTC12__DEBUG_OUT3 0x0E4 0x000 ALT7 0x0 +#define VF610_PAD_PTC13__GPIO_58 0x0E8 0x000 ALT0 0x0 +#define VF610_PAD_PTC13__ENET_RMII1_RXD0 0x0E8 0x000 ALT1 0x0 +#define VF610_PAD_PTC13__ESAI_SDO2 0x0E8 0x31C ALT3 0x1 +#define VF610_PAD_PTC13__SAI2_RX_BCLK 0x0E8 0x364 ALT5 0x2 +#define VF610_PAD_PTC13__DEBUG_OUT4 0x0E8 0x000 ALT7 0x0 +#define VF610_PAD_PTC14__GPIO_59 0x0EC 0x000 ALT0 0x0 +#define VF610_PAD_PTC14__ENET_RMII1_RXER 0x0EC 0x000 ALT1 0x0 +#define VF610_PAD_PTC14__ESAI_SDO3 0x0EC 0x320 ALT3 0x1 +#define VF610_PAD_PTC14__UART5_TX 0x0EC 0x000 ALT4 0x0 +#define VF610_PAD_PTC14__SAI2_RX_DATA 0x0EC 0x368 ALT5 0x2 +#define VF610_PAD_PTC14__ADC0_SE6 0x0EC 0x000 ALT6 0x0 +#define VF610_PAD_PTC14__DEBUG_OUT5 0x0EC 0x000 ALT7 0x0 +#define VF610_PAD_PTC15__GPIO_60 0x0F0 0x000 ALT0 0x0 +#define VF610_PAD_PTC15__ENET_RMII1_TXD1 0x0F0 0x000 ALT1 0x0 +#define VF610_PAD_PTC15__ESAI_SDI0 0x0F0 0x328 ALT3 0x1 +#define VF610_PAD_PTC15__UART5_RX 0x0F0 0x000 ALT4 0x0 +#define VF610_PAD_PTC15__SAI2_TX_DATA 0x0F0 0x000 ALT5 0x0 +#define VF610_PAD_PTC15__ADC0_SE7 0x0F0 0x000 ALT6 0x0 +#define VF610_PAD_PTC15__DEBUG_OUT6 0x0F0 0x000 ALT7 0x0 +#define VF610_PAD_PTC16__GPIO_61 0x0F4 0x000 ALT0 0x0 +#define VF610_PAD_PTC16__ENET_RMII1_TXD0 0x0F4 0x000 ALT1 0x0 +#define VF610_PAD_PTC16__ESAI_SDI1 0x0F4 0x324 ALT3 0x1 +#define VF610_PAD_PTC16__UART5_RTS 0x0F4 0x000 ALT4 0x0 +#define VF610_PAD_PTC16__SAI2_RX_SYNC 0x0F4 0x36C ALT5 0x2 +#define VF610_PAD_PTC16__ADC1_SE6 0x0F4 0x000 ALT6 0x0 +#define VF610_PAD_PTC16__DEBUG_OUT7 0x0F4 0x000 ALT7 0x0 +#define VF610_PAD_PTC17__GPIO_62 0x0F8 0x000 ALT0 0x0 +#define VF610_PAD_PTC17__ENET_RMII1_TXEN 0x0F8 0x000 ALT1 0x0 +#define VF610_PAD_PTC17__ADC1_SE7 0x0F8 0x000 ALT3 0x0 +#define VF610_PAD_PTC17__UART5_CTS 0x0F8 0x000 ALT4 0x0 +#define VF610_PAD_PTC17__SAI2_TX_SYNC 0x0F8 0x374 ALT5 0x1 +#define VF610_PAD_PTC17__USB1_SOF_PULSE 0x0F8 0x000 ALT6 0x0 +#define VF610_PAD_PTC17__DEBUG_OUT8 0x0F8 0x000 ALT7 0x0 +#define VF610_PAD_PTD31__GPIO_63 0x0FC 0x000 ALT0 0x0 +#define VF610_PAD_PTD31__FB_AD31 0x0FC 0x000 ALT1 0x0 +#define VF610_PAD_PTD31__NF_IO15 0x0FC 0x000 ALT2 0x0 +#define VF610_PAD_PTD31__FTM3_CH0 0x0FC 0x000 ALT4 0x0 +#define VF610_PAD_PTD31__DSPI2_CS1 0x0FC 0x000 ALT5 0x0 +#define VF610_PAD_PTD31__DEBUG_OUT9 0x0FC 0x000 ALT7 0x0 +#define VF610_PAD_PTD30__GPIO_64 0x100 0x000 ALT0 0x0 +#define VF610_PAD_PTD30__FB_AD30 0x100 0x000 ALT1 0x0 +#define VF610_PAD_PTD30__NF_IO14 0x100 0x000 ALT2 0x0 +#define VF610_PAD_PTD30__FTM3_CH1 0x100 0x000 ALT4 0x0 +#define VF610_PAD_PTD30__DSPI2_CS0 0x100 0x000 ALT5 0x0 +#define VF610_PAD_PTD30__DEBUG_OUT10 0x100 0x000 ALT7 0x0 +#define VF610_PAD_PTD29__GPIO_65 0x104 0x000 ALT0 0x0 +#define VF610_PAD_PTD29__FB_AD29 0x104 0x000 ALT1 0x0 +#define VF610_PAD_PTD29__NF_IO13 0x104 0x000 ALT2 0x0 +#define VF610_PAD_PTD29__FTM3_CH2 0x104 0x000 ALT4 0x0 +#define VF610_PAD_PTD29__DSPI2_SIN 0x104 0x000 ALT5 0x0 +#define VF610_PAD_PTD29__DEBUG_OUT11 0x104 0x000 ALT7 0x0 +#define VF610_PAD_PTD28__GPIO_66 0x108 0x000 ALT0 0x0 +#define VF610_PAD_PTD28__FB_AD28 0x108 0x000 ALT1 0x0 +#define VF610_PAD_PTD28__NF_IO12 0x108 0x000 ALT2 0x0 +#define VF610_PAD_PTD28__I2C2_SCL 0x108 0x34C ALT3 0x1 +#define VF610_PAD_PTD28__FTM3_CH3 0x108 0x000 ALT4 0x0 +#define VF610_PAD_PTD28__DSPI2_SOUT 0x108 0x000 ALT5 0x0 +#define VF610_PAD_PTD28__DEBUG_OUT12 0x108 0x000 ALT7 0x0 +#define VF610_PAD_PTD27__GPIO_67 0x10C 0x000 ALT0 0x0 +#define VF610_PAD_PTD27__FB_AD27 0x10C 0x000 ALT1 0x0 +#define VF610_PAD_PTD27__NF_IO11 0x10C 0x000 ALT2 0x0 +#define VF610_PAD_PTD27__I2C2_SDA 0x10C 0x350 ALT3 0x1 +#define VF610_PAD_PTD27__FTM3_CH4 0x10C 0x000 ALT4 0x0 +#define VF610_PAD_PTD27__DSPI2_SCK 0x10C 0x000 ALT5 0x0 +#define VF610_PAD_PTD27__DEBUG_OUT13 0x10C 0x000 ALT7 0x0 +#define VF610_PAD_PTD26__GPIO_68 0x110 0x000 ALT0 0x0 +#define VF610_PAD_PTD26__FB_AD26 0x110 0x000 ALT1 0x0 +#define VF610_PAD_PTD26__NF_IO10 0x110 0x000 ALT2 0x0 +#define VF610_PAD_PTD26__FTM3_CH5 0x110 0x000 ALT4 0x0 +#define VF610_PAD_PTD26__ESDHC1_WP 0x110 0x000 ALT5 0x0 +#define VF610_PAD_PTD26__DEBUG_OUT14 0x110 0x000 ALT7 0x0 +#define VF610_PAD_PTD25__GPIO_69 0x114 0x000 ALT0 0x0 +#define VF610_PAD_PTD25__FB_AD25 0x114 0x000 ALT1 0x0 +#define VF610_PAD_PTD25__NF_IO9 0x114 0x000 ALT2 0x0 +#define VF610_PAD_PTD25__FTM3_CH6 0x114 0x000 ALT4 0x0 +#define VF610_PAD_PTD25__DEBUG_OUT15 0x114 0x000 ALT7 0x0 +#define VF610_PAD_PTD24__GPIO_70 0x118 0x000 ALT0 0x0 +#define VF610_PAD_PTD24__FB_AD24 0x118 0x000 ALT1 0x0 +#define VF610_PAD_PTD24__NF_IO8 0x118 0x000 ALT2 0x0 +#define VF610_PAD_PTD24__FTM3_CH7 0x118 0x000 ALT4 0x0 +#define VF610_PAD_PTD24__DEBUG_OUT16 0x118 0x000 ALT7 0x0 +#define VF610_PAD_PTD23__GPIO_71 0x11C 0x000 ALT0 0x0 +#define VF610_PAD_PTD23__FB_AD23 0x11C 0x000 ALT1 0x0 +#define VF610_PAD_PTD23__NF_IO7 0x11C 0x000 ALT2 0x0 +#define VF610_PAD_PTD23__FTM2_CH0 0x11C 0x000 ALT3 0x0 +#define VF610_PAD_PTD23__ENET0_1588_TMR0 0x11C 0x304 ALT4 0x1 +#define VF610_PAD_PTD23__ESDHC0_DAT4 0x11C 0x000 ALT5 0x0 +#define VF610_PAD_PTD23__UART2_TX 0x11C 0x38C ALT6 0x1 +#define VF610_PAD_PTD23__DCU1_R3 0x11C 0x000 ALT7 0x0 +#define VF610_PAD_PTD22__GPIO_72 0x120 0x000 ALT0 0x0 +#define VF610_PAD_PTD22__FB_AD22 0x120 0x000 ALT1 0x0 +#define VF610_PAD_PTD22__NF_IO6 0x120 0x000 ALT2 0x0 +#define VF610_PAD_PTD22__FTM2_CH1 0x120 0x000 ALT3 0x0 +#define VF610_PAD_PTD22__ENET0_1588_TMR1 0x120 0x308 ALT4 0x1 +#define VF610_PAD_PTD22__ESDHC0_DAT5 0x120 0x000 ALT5 0x0 +#define VF610_PAD_PTD22__UART2_RX 0x120 0x388 ALT6 0x1 +#define VF610_PAD_PTD22__DCU1_R4 0x120 0x000 ALT7 0x0 +#define VF610_PAD_PTD21__GPIO_73 0x124 0x000 ALT0 0x0 +#define VF610_PAD_PTD21__FB_AD21 0x124 0x000 ALT1 0x0 +#define VF610_PAD_PTD21__NF_IO5 0x124 0x000 ALT2 0x0 +#define VF610_PAD_PTD21__ENET0_1588_TMR2 0x124 0x000 ALT4 0x0 +#define VF610_PAD_PTD21__ESDHC0_DAT6 0x124 0x000 ALT5 0x0 +#define VF610_PAD_PTD21__UART2_RTS 0x124 0x000 ALT6 0x0 +#define VF610_PAD_PTD21__DCU1_R5 0x124 0x000 ALT7 0x0 +#define VF610_PAD_PTD20__GPIO_74 0x128 0x000 ALT0 0x0 +#define VF610_PAD_PTD20__FB_AD20 0x128 0x000 ALT1 0x0 +#define VF610_PAD_PTD20__NF_IO4 0x128 0x000 ALT2 0x0 +#define VF610_PAD_PTD20__ENET0_1588_TMR3 0x128 0x000 ALT4 0x0 +#define VF610_PAD_PTD20__ESDHC0_DAT7 0x128 0x000 ALT5 0x0 +#define VF610_PAD_PTD20__UART2_CTS 0x128 0x384 ALT6 0x0 +#define VF610_PAD_PTD20__DCU1_R0 0x128 0x000 ALT7 0x0 +#define VF610_PAD_PTD19__GPIO_75 0x12C 0x000 ALT0 0x0 +#define VF610_PAD_PTD19__FB_AD19 0x12C 0x000 ALT1 0x0 +#define VF610_PAD_PTD19__NF_IO3 0x12C 0x000 ALT2 0x0 +#define VF610_PAD_PTD19__ESAI_SCKR 0x12C 0x000 ALT3 0x0 +#define VF610_PAD_PTD19__I2C0_SCL 0x12C 0x33C ALT4 0x2 +#define VF610_PAD_PTD19__FTM2_QD_PHA 0x12C 0x000 ALT5 0x0 +#define VF610_PAD_PTD19__DCU1_R1 0x12C 0x000 ALT7 0x0 +#define VF610_PAD_PTD18__GPIO_76 0x130 0x000 ALT0 0x0 +#define VF610_PAD_PTD18__FB_AD18 0x130 0x000 ALT1 0x0 +#define VF610_PAD_PTD18__NF_IO2 0x130 0x000 ALT2 0x0 +#define VF610_PAD_PTD18__ESAI_FSR 0x130 0x000 ALT3 0x0 +#define VF610_PAD_PTD18__I2C0_SDA 0x130 0x340 ALT4 0x2 +#define VF610_PAD_PTD18__FTM2_QD_PHB 0x130 0x000 ALT5 0x0 +#define VF610_PAD_PTD18__DCU1_G0 0x130 0x000 ALT7 0x0 +#define VF610_PAD_PTD17__GPIO_77 0x134 0x000 ALT0 0x0 +#define VF610_PAD_PTD17__FB_AD17 0x134 0x000 ALT1 0x0 +#define VF610_PAD_PTD17__NF_IO1 0x134 0x000 ALT2 0x0 +#define VF610_PAD_PTD17__ESAI_HCKR 0x134 0x000 ALT3 0x0 +#define VF610_PAD_PTD17__I2C1_SCL 0x134 0x344 ALT4 0x2 +#define VF610_PAD_PTD17__DCU1_G1 0x134 0x000 ALT7 0x0 +#define VF610_PAD_PTD16__GPIO_78 0x138 0x000 ALT0 0x0 +#define VF610_PAD_PTD16__FB_AD16 0x138 0x000 ALT1 0x0 +#define VF610_PAD_PTD16__NF_IO0 0x138 0x000 ALT2 0x0 +#define VF610_PAD_PTD16__ESAI_HCKT 0x138 0x000 ALT3 0x0 +#define VF610_PAD_PTD16__I2C1_SDA 0x138 0x348 ALT4 0x2 +#define VF610_PAD_PTD16__DCU1_G2 0x138 0x000 ALT7 0x0 +#define VF610_PAD_PTD0__GPIO_79 0x13C 0x000 ALT0 0x0 +#define VF610_PAD_PTD0__QSPI0_A_QSCK 0x13C 0x000 ALT1 0x0 +#define VF610_PAD_PTD0__UART2_TX 0x13C 0x38C ALT2 0x2 +#define VF610_PAD_PTD0__FB_AD15 0x13C 0x000 ALT4 0x0 +#define VF610_PAD_PTD0__SPDIF_EXTCLK 0x13C 0x000 ALT5 0x0 +#define VF610_PAD_PTD0__DEBUG_OUT17 0x13C 0x000 ALT7 0x0 +#define VF610_PAD_PTD1__GPIO_80 0x140 0x000 ALT0 0x0 +#define VF610_PAD_PTD1__QSPI0_A_CS0 0x140 0x000 ALT1 0x0 +#define VF610_PAD_PTD1__UART2_RX 0x140 0x388 ALT2 0x2 +#define VF610_PAD_PTD1__FB_AD14 0x140 0x000 ALT4 0x0 +#define VF610_PAD_PTD1__SPDIF_IN1 0x140 0x000 ALT5 0x0 +#define VF610_PAD_PTD1__DEBUG_OUT18 0x140 0x000 ALT7 0x0 +#define VF610_PAD_PTD2__GPIO_81 0x144 0x000 ALT0 0x0 +#define VF610_PAD_PTD2__QSPI0_A_DATA3 0x144 0x000 ALT1 0x0 +#define VF610_PAD_PTD2__UART2_RTS 0x144 0x000 ALT2 0x0 +#define VF610_PAD_PTD2__DSPI1_CS3 0x144 0x000 ALT3 0x0 +#define VF610_PAD_PTD2__FB_AD13 0x144 0x000 ALT4 0x0 +#define VF610_PAD_PTD2__SPDIF_OUT1 0x144 0x000 ALT5 0x0 +#define VF610_PAD_PTD2__DEBUG_OUT19 0x144 0x000 ALT7 0x0 +#define VF610_PAD_PTD3__GPIO_82 0x148 0x000 ALT0 0x0 +#define VF610_PAD_PTD3__QSPI0_A_DATA2 0x148 0x000 ALT1 0x0 +#define VF610_PAD_PTD3__UART2_CTS 0x148 0x384 ALT2 0x1 +#define VF610_PAD_PTD3__DSPI1_CS2 0x148 0x000 ALT3 0x0 +#define VF610_PAD_PTD3__FB_AD12 0x148 0x000 ALT4 0x0 +#define VF610_PAD_PTD3__SPDIF_PLOCK 0x148 0x000 ALT5 0x0 +#define VF610_PAD_PTD3__DEBUG_OUT20 0x148 0x000 ALT7 0x0 +#define VF610_PAD_PTD4__GPIO_83 0x14C 0x000 ALT0 0x0 +#define VF610_PAD_PTD4__QSPI0_A_DATA1 0x14C 0x000 ALT1 0x0 +#define VF610_PAD_PTD4__DSPI1_CS1 0x14C 0x000 ALT3 0x0 +#define VF610_PAD_PTD4__FB_AD11 0x14C 0x000 ALT4 0x0 +#define VF610_PAD_PTD4__SPDIF_SRCLK 0x14C 0x000 ALT5 0x0 +#define VF610_PAD_PTD4__DEBUG_OUT21 0x14C 0x000 ALT7 0x0 +#define VF610_PAD_PTD5__GPIO_84 0x150 0x000 ALT0 0x0 +#define VF610_PAD_PTD5__QSPI0_A_DATA0 0x150 0x000 ALT1 0x0 +#define VF610_PAD_PTD5__DSPI1_CS0 0x150 0x300 ALT3 0x1 +#define VF610_PAD_PTD5__FB_AD10 0x150 0x000 ALT4 0x0 +#define VF610_PAD_PTD5__DEBUG_OUT22 0x150 0x000 ALT7 0x0 +#define VF610_PAD_PTD6__GPIO_85 0x154 0x000 ALT0 0x0 +#define VF610_PAD_PTD6__QSPI1_A_DQS 0x154 0x000 ALT1 0x0 +#define VF610_PAD_PTD6__DSPI1_SIN 0x154 0x2FC ALT3 0x1 +#define VF610_PAD_PTD6__FB_AD9 0x154 0x000 ALT4 0x0 +#define VF610_PAD_PTD6__DEBUG_OUT23 0x154 0x000 ALT7 0x0 +#define VF610_PAD_PTD7__GPIO_86 0x158 0x000 ALT0 0x0 +#define VF610_PAD_PTD7__QSPI0_B_QSCK 0x158 0x000 ALT1 0x0 +#define VF610_PAD_PTD7__DSPI1_SOUT 0x158 0x000 ALT3 0x0 +#define VF610_PAD_PTD7__FB_AD8 0x158 0x000 ALT4 0x0 +#define VF610_PAD_PTD7__DEBUG_OUT24 0x158 0x000 ALT7 0x0 +#define VF610_PAD_PTD8__GPIO_87 0x15C 0x000 ALT0 0x0 +#define VF610_PAD_PTD8__QSPI0_B_CS0 0x15C 0x000 ALT1 0x0 +#define VF610_PAD_PTD8__FB_CLKOUT 0x15C 0x000 ALT2 0x0 +#define VF610_PAD_PTD8__DSPI1_SCK 0x15C 0x2F8 ALT3 0x1 +#define VF610_PAD_PTD8__FB_AD7 0x15C 0x000 ALT4 0x0 +#define VF610_PAD_PTD8__DEBUG_OUT25 0x15C 0x000 ALT7 0x0 +#define VF610_PAD_PTD9__GPIO_88 0x160 0x000 ALT0 0x0 +#define VF610_PAD_PTD9__QSPI0_B_DATA3 0x160 0x000 ALT1 0x0 +#define VF610_PAD_PTD9__DSPI3_CS1 0x160 0x000 ALT2 0x0 +#define VF610_PAD_PTD9__FB_AD6 0x160 0x000 ALT4 0x0 +#define VF610_PAD_PTD9__SAI1_TX_SYNC 0x160 0x360 ALT6 0x0 +#define VF610_PAD_PTD9__DCU1_B0 0x160 0x000 ALT7 0x0 +#define VF610_PAD_PTD10__GPIO_89 0x164 0x000 ALT0 0x0 +#define VF610_PAD_PTD10__QSPI0_B_DATA2 0x164 0x000 ALT1 0x0 +#define VF610_PAD_PTD10__DSPI3_CS0 0x164 0x000 ALT2 0x0 +#define VF610_PAD_PTD10__FB_AD5 0x164 0x000 ALT4 0x0 +#define VF610_PAD_PTD10__DCU1_B1 0x164 0x000 ALT7 0x0 +#define VF610_PAD_PTD11__GPIO_90 0x168 0x000 ALT0 0x0 +#define VF610_PAD_PTD11__QSPI0_B_DATA1 0x168 0x000 ALT1 0x0 +#define VF610_PAD_PTD11__DSPI3_SIN 0x168 0x000 ALT2 0x0 +#define VF610_PAD_PTD11__FB_AD4 0x168 0x000 ALT4 0x0 +#define VF610_PAD_PTD11__DEBUG_OUT26 0x168 0x000 ALT7 0x0 +#define VF610_PAD_PTD12__GPIO_91 0x16C 0x000 ALT0 0x0 +#define VF610_PAD_PTD12__QSPI0_B_DATA0 0x16C 0x000 ALT1 0x0 +#define VF610_PAD_PTD12__DSPI3_SOUT 0x16C 0x000 ALT2 0x0 +#define VF610_PAD_PTD12__FB_AD3 0x16C 0x000 ALT4 0x0 +#define VF610_PAD_PTD12__DEBUG_OUT27 0x16C 0x000 ALT7 0x0 +#define VF610_PAD_PTD13__GPIO_92 0x170 0x000 ALT0 0x0 +#define VF610_PAD_PTD13__QSPI0_B_DQS 0x170 0x000 ALT1 0x0 +#define VF610_PAD_PTD13__DSPI3_SCK 0x170 0x000 ALT2 0x0 +#define VF610_PAD_PTD13__FB_AD2 0x170 0x000 ALT4 0x0 +#define VF610_PAD_PTD13__DEBUG_OUT28 0x170 0x000 ALT7 0x0 +#define VF610_PAD_PTB23__GPIO_93 0x174 0x000 ALT0 0x0 +#define VF610_PAD_PTB23__SAI0_TX_BCLK 0x174 0x000 ALT1 0x0 +#define VF610_PAD_PTB23__UART1_TX 0x174 0x380 ALT2 0x2 +#define VF610_PAD_PTB23__SRC_RCON18 0x174 0x398 ALT3 0x1 +#define VF610_PAD_PTB23__FB_MUXED_ALE 0x174 0x000 ALT4 0x0 +#define VF610_PAD_PTB23__FB_TS_B 0x174 0x000 ALT5 0x0 +#define VF610_PAD_PTB23__UART3_RTS 0x174 0x000 ALT6 0x0 +#define VF610_PAD_PTB23__DCU1_G3 0x174 0x000 ALT7 0x0 +#define VF610_PAD_PTB24__GPIO_94 0x178 0x000 ALT0 0x0 +#define VF610_PAD_PTB24__SAI0_RX_BCLK 0x178 0x000 ALT1 0x0 +#define VF610_PAD_PTB24__UART1_RX 0x178 0x37C ALT2 0x2 +#define VF610_PAD_PTB24__SRC_RCON19 0x178 0x39C ALT3 0x1 +#define VF610_PAD_PTB24__FB_MUXED_TSIZ0 0x178 0x000 ALT4 0x0 +#define VF610_PAD_PTB24__NF_WE_B 0x178 0x000 ALT5 0x0 +#define VF610_PAD_PTB24__UART3_CTS 0x178 0x000 ALT6 0x0 +#define VF610_PAD_PTB24__DCU1_G4 0x178 0x000 ALT7 0x0 +#define VF610_PAD_PTB25__GPIO_95 0x17C 0x000 ALT0 0x0 +#define VF610_PAD_PTB25__SAI0_RX_DATA 0x17C 0x000 ALT1 0x0 +#define VF610_PAD_PTB25__UART1_RTS 0x17C 0x000 ALT2 0x0 +#define VF610_PAD_PTB25__SRC_RCON20 0x17C 0x3A0 ALT3 0x1 +#define VF610_PAD_PTB25__FB_CS1_B 0x17C 0x000 ALT4 0x0 +#define VF610_PAD_PTB25__NF_CE0_B 0x17C 0x000 ALT5 0x0 +#define VF610_PAD_PTB25__DCU1_G5 0x17C 0x000 ALT7 0x0 +#define VF610_PAD_PTB26__GPIO_96 0x180 0x000 ALT0 0x0 +#define VF610_PAD_PTB26__SAI0_TX_DATA 0x180 0x000 ALT1 0x0 +#define VF610_PAD_PTB26__UART1_CTS 0x180 0x378 ALT2 0x2 +#define VF610_PAD_PTB26__SRC_RCON21 0x180 0x000 ALT3 0x0 +#define VF610_PAD_PTB26__FB_CS0_B 0x180 0x000 ALT4 0x0 +#define VF610_PAD_PTB26__NF_CE1_B 0x180 0x000 ALT5 0x0 +#define VF610_PAD_PTB26__DCU1_G6 0x180 0x000 ALT7 0x0 +#define VF610_PAD_PTB27__GPIO_97 0x184 0x000 ALT0 0x0 +#define VF610_PAD_PTB27__SAI0_RX_SYNC 0x184 0x000 ALT1 0x0 +#define VF610_PAD_PTB27__SRC_RCON22 0x184 0x000 ALT3 0x0 +#define VF610_PAD_PTB27__FB_OE_B 0x184 0x000 ALT4 0x0 +#define VF610_PAD_PTB27__FB_MUXED_TBST_B 0x184 0x000 ALT5 0x0 +#define VF610_PAD_PTB27__NF_RE_B 0x184 0x000 ALT6 0x0 +#define VF610_PAD_PTB27__DCU1_G7 0x184 0x000 ALT7 0x0 +#define VF610_PAD_PTB28__GPIO_98 0x188 0x000 ALT0 0x0 +#define VF610_PAD_PTB28__SAI0_TX_SYNC 0x188 0x000 ALT1 0x0 +#define VF610_PAD_PTB28__SRC_RCON23 0x188 0x000 ALT3 0x0 +#define VF610_PAD_PTB28__FB_RW_B 0x188 0x000 ALT4 0x0 +#define VF610_PAD_PTB28__DCU1_B6 0x188 0x000 ALT7 0x0 +#define VF610_PAD_PTC26__GPIO_99 0x18C 0x000 ALT0 0x0 +#define VF610_PAD_PTC26__SAI1_TX_BCLK 0x18C 0x000 ALT1 0x0 +#define VF610_PAD_PTC26__DSPI0_CS5 0x18C 0x000 ALT2 0x0 +#define VF610_PAD_PTC26__SRC_RCON24 0x18C 0x000 ALT3 0x0 +#define VF610_PAD_PTC26__FB_TA_B 0x18C 0x000 ALT4 0x0 +#define VF610_PAD_PTC26__NF_RB_B 0x18C 0x000 ALT5 0x0 +#define VF610_PAD_PTC26__DCU1_B7 0x18C 0x000 ALT7 0x0 +#define VF610_PAD_PTC27__GPIO_100 0x190 0x000 ALT0 0x0 +#define VF610_PAD_PTC27__SAI1_RX_BCLK 0x190 0x000 ALT1 0x0 +#define VF610_PAD_PTC27__DSPI0_CS4 0x190 0x000 ALT2 0x0 +#define VF610_PAD_PTC27__SRC_RCON25 0x190 0x000 ALT3 0x0 +#define VF610_PAD_PTC27__FB_BE3_B 0x190 0x000 ALT4 0x0 +#define VF610_PAD_PTC27__FB_CS3_B 0x190 0x000 ALT5 0x0 +#define VF610_PAD_PTC27__NF_ALE 0x190 0x000 ALT6 0x0 +#define VF610_PAD_PTC27__DCU1_B2 0x190 0x000 ALT7 0x0 +#define VF610_PAD_PTC28__GPIO_101 0x194 0x000 ALT0 0x0 +#define VF610_PAD_PTC28__SAI1_RX_DATA 0x194 0x000 ALT1 0x0 +#define VF610_PAD_PTC28__DSPI0_CS3 0x194 0x000 ALT2 0x0 +#define VF610_PAD_PTC28__SRC_RCON26 0x194 0x000 ALT3 0x0 +#define VF610_PAD_PTC28__FB_BE2_B 0x194 0x000 ALT4 0x0 +#define VF610_PAD_PTC28__FB_CS2_B 0x194 0x000 ALT5 0x0 +#define VF610_PAD_PTC28__NF_CLE 0x194 0x000 ALT6 0x0 +#define VF610_PAD_PTC28__DCU1_B3 0x194 0x000 ALT7 0x0 +#define VF610_PAD_PTC29__GPIO_102 0x198 0x000 ALT0 0x0 +#define VF610_PAD_PTC29__SAI1_TX_DATA 0x198 0x000 ALT1 0x0 +#define VF610_PAD_PTC29__DSPI0_CS2 0x198 0x000 ALT2 0x0 +#define VF610_PAD_PTC29__SRC_RCON27 0x198 0x000 ALT3 0x0 +#define VF610_PAD_PTC29__FB_BE1_B 0x198 0x000 ALT4 0x0 +#define VF610_PAD_PTC29__FB_MUXED_TSIZE1 0x198 0x000 ALT5 0x0 +#define VF610_PAD_PTC29__DCU1_B4 0x198 0x000 ALT7 0x0 +#define VF610_PAD_PTC30__GPIO_103 0x19C 0x000 ALT0 0x0 +#define VF610_PAD_PTC30__SAI1_RX_SYNC 0x19C 0x000 ALT1 0x0 +#define VF610_PAD_PTC30__DSPI1_CS2 0x19C 0x000 ALT2 0x0 +#define VF610_PAD_PTC30__SRC_RCON28 0x19C 0x000 ALT3 0x0 +#define VF610_PAD_PTC30__FB_MUXED_BE0_B 0x19C 0x000 ALT4 0x0 +#define VF610_PAD_PTC30__FB_TSIZ0 0x19C 0x000 ALT5 0x0 +#define VF610_PAD_PTC30__ADC0_SE5 0x19C 0x000 ALT6 0x0 +#define VF610_PAD_PTC30__DCU1_B5 0x19C 0x000 ALT7 0x0 +#define VF610_PAD_PTC31__GPIO_104 0x1A0 0x000 ALT0 0x0 +#define VF610_PAD_PTC31__SAI1_TX_SYNC 0x1A0 0x360 ALT1 0x1 +#define VF610_PAD_PTC31__SRC_RCON29 0x1A0 0x000 ALT3 0x0 +#define VF610_PAD_PTC31__ADC1_SE5 0x1A0 0x000 ALT6 0x0 +#define VF610_PAD_PTC31__DCU1_B6 0x1A0 0x000 ALT7 0x0 +#define VF610_PAD_PTE0__GPIO_105 0x1A4 0x000 ALT0 0x0 +#define VF610_PAD_PTE0__DCU0_HSYNC 0x1A4 0x000 ALT1 0x0 +#define VF610_PAD_PTE0__SRC_BMODE1 0x1A4 0x000 ALT2 0x0 +#define VF610_PAD_PTE0__LCD0 0x1A4 0x000 ALT4 0x0 +#define VF610_PAD_PTE0__DEBUG_OUT29 0x1A4 0x000 ALT7 0x0 +#define VF610_PAD_PTE1__GPIO_106 0x1A8 0x000 ALT0 0x0 +#define VF610_PAD_PTE1__DCU0_VSYNC 0x1A8 0x000 ALT1 0x0 +#define VF610_PAD_PTE1__SRC_BMODE0 0x1A8 0x000 ALT2 0x0 +#define VF610_PAD_PTE1__LCD1 0x1A8 0x000 ALT4 0x0 +#define VF610_PAD_PTE1__DEBUG_OUT30 0x1A8 0x000 ALT7 0x0 +#define VF610_PAD_PTE2__GPIO_107 0x1AC 0x000 ALT0 0x0 +#define VF610_PAD_PTE2__DCU0_PCLK 0x1AC 0x000 ALT1 0x0 +#define VF610_PAD_PTE2__LCD2 0x1AC 0x000 ALT4 0x0 +#define VF610_PAD_PTE2__DEBUG_OUT31 0x1AC 0x000 ALT7 0x0 +#define VF610_PAD_PTE3__GPIO_108 0x1B0 0x000 ALT0 0x0 +#define VF610_PAD_PTE3__DCU0_TAG 0x1B0 0x000 ALT1 0x0 +#define VF610_PAD_PTE3__LCD3 0x1B0 0x000 ALT4 0x0 +#define VF610_PAD_PTE3__DEBUG_OUT32 0x1B0 0x000 ALT7 0x0 +#define VF610_PAD_PTE4__GPIO_109 0x1B4 0x000 ALT0 0x0 +#define VF610_PAD_PTE4__DCU0_DE 0x1B4 0x000 ALT1 0x0 +#define VF610_PAD_PTE4__LCD4 0x1B4 0x000 ALT4 0x0 +#define VF610_PAD_PTE4__DEBUG_OUT33 0x1B4 0x000 ALT7 0x0 +#define VF610_PAD_PTE5__GPIO_110 0x1B8 0x000 ALT0 0x0 +#define VF610_PAD_PTE5__DCU0_R0 0x1B8 0x000 ALT1 0x0 +#define VF610_PAD_PTE5__LCD5 0x1B8 0x000 ALT4 0x0 +#define VF610_PAD_PTE5__DEBUG_OUT34 0x1B8 0x000 ALT7 0x0 +#define VF610_PAD_PTE6__GPIO_111 0x1BC 0x000 ALT0 0x0 +#define VF610_PAD_PTE6__DCU0_R1 0x1BC 0x000 ALT1 0x0 +#define VF610_PAD_PTE6__LCD6 0x1BC 0x000 ALT4 0x0 +#define VF610_PAD_PTE6__DEBUG_OUT35 0x1BC 0x000 ALT7 0x0 +#define VF610_PAD_PTE7__GPIO_112 0x1C0 0x000 ALT0 0x0 +#define VF610_PAD_PTE7__DCU0_R2 0x1C0 0x000 ALT1 0x0 +#define VF610_PAD_PTE7__SRC_RCON0 0x1C0 0x000 ALT3 0x0 +#define VF610_PAD_PTE7__LCD7 0x1C0 0x000 ALT4 0x0 +#define VF610_PAD_PTE7__DEBUG_OUT36 0x1C0 0x000 ALT7 0x0 +#define VF610_PAD_PTE8__GPIO_113 0x1C4 0x000 ALT0 0x0 +#define VF610_PAD_PTE8__DCU0_R3 0x1C4 0x000 ALT1 0x0 +#define VF610_PAD_PTE8__SRC_RCON1 0x1C4 0x000 ALT3 0x0 +#define VF610_PAD_PTE8__LCD8 0x1C4 0x000 ALT4 0x0 +#define VF610_PAD_PTE8__DEBUG_OUT37 0x1C4 0x000 ALT7 0x0 +#define VF610_PAD_PTE9__GPIO_114 0x1C8 0x000 ALT0 0x0 +#define VF610_PAD_PTE9__DCU0_R4 0x1C8 0x000 ALT1 0x0 +#define VF610_PAD_PTE9__SRC_RCON2 0x1C8 0x000 ALT3 0x0 +#define VF610_PAD_PTE9__LCD9 0x1C8 0x000 ALT4 0x0 +#define VF610_PAD_PTE9__DEBUG_OUT38 0x1C8 0x000 ALT7 0x0 +#define VF610_PAD_PTE10__GPIO_115 0x1CC 0x000 ALT0 0x0 +#define VF610_PAD_PTE10__DCU0_R5 0x1CC 0x000 ALT1 0x0 +#define VF610_PAD_PTE10__SRC_RCON3 0x1CC 0x000 ALT3 0x0 +#define VF610_PAD_PTE10__LCD10 0x1CC 0x000 ALT4 0x0 +#define VF610_PAD_PTE10__DEBUG_OUT39 0x1CC 0x000 ALT7 0x0 +#define VF610_PAD_PTE11__GPIO_116 0x1D0 0x000 ALT0 0x0 +#define VF610_PAD_PTE11__DCU0_R6 0x1D0 0x000 ALT1 0x0 +#define VF610_PAD_PTE11__SRC_RCON4 0x1D0 0x000 ALT3 0x0 +#define VF610_PAD_PTE11__LCD11 0x1D0 0x000 ALT4 0x0 +#define VF610_PAD_PTE11__DEBUG_OUT40 0x1D0 0x000 ALT7 0x0 +#define VF610_PAD_PTE12__GPIO_117 0x1D4 0x000 ALT0 0x0 +#define VF610_PAD_PTE12__DCU0_R7 0x1D4 0x000 ALT1 0x0 +#define VF610_PAD_PTE12__DSPI1_CS3 0x1D4 0x000 ALT2 0x0 +#define VF610_PAD_PTE12__SRC_RCON5 0x1D4 0x000 ALT3 0x0 +#define VF610_PAD_PTE12__LCD12 0x1D4 0x000 ALT4 0x0 +#define VF610_PAD_PTE12__LPT_ALT0 0x1D4 0x000 ALT7 0x0 +#define VF610_PAD_PTE13__GPIO_118 0x1D8 0x000 ALT0 0x0 +#define VF610_PAD_PTE13__DCU0_G0 0x1D8 0x000 ALT1 0x0 +#define VF610_PAD_PTE13__LCD13 0x1D8 0x000 ALT4 0x0 +#define VF610_PAD_PTE13__DEBUG_OUT41 0x1D8 0x000 ALT7 0x0 +#define VF610_PAD_PTE14__GPIO_119 0x1DC 0x000 ALT0 0x0 +#define VF610_PAD_PTE14__DCU0_G1 0x1DC 0x000 ALT1 0x0 +#define VF610_PAD_PTE14__LCD14 0x1DC 0x000 ALT4 0x0 +#define VF610_PAD_PTE14__DEBUG_OUT42 0x1DC 0x000 ALT7 0x0 +#define VF610_PAD_PTE15__GPIO_120 0x1E0 0x000 ALT0 0x0 +#define VF610_PAD_PTE15__DCU0_G2 0x1E0 0x000 ALT1 0x0 +#define VF610_PAD_PTE15__SRC_RCON6 0x1E0 0x000 ALT3 0x0 +#define VF610_PAD_PTE15__LCD15 0x1E0 0x000 ALT4 0x0 +#define VF610_PAD_PTE15__DEBUG_OUT43 0x1E0 0x000 ALT7 0x0 +#define VF610_PAD_PTE16__GPIO_121 0x1E4 0x000 ALT0 0x0 +#define VF610_PAD_PTE16__DCU0_G3 0x1E4 0x000 ALT1 0x0 +#define VF610_PAD_PTE16__SRC_RCON7 0x1E4 0x000 ALT3 0x0 +#define VF610_PAD_PTE16__LCD16 0x1E4 0x000 ALT4 0x0 +#define VF610_PAD_PTE17__GPIO_122 0x1E8 0x000 ALT0 0x0 +#define VF610_PAD_PTE17__DCU0_G4 0x1E8 0x000 ALT1 0x0 +#define VF610_PAD_PTE17__SRC_RCON8 0x1E8 0x000 ALT3 0x0 +#define VF610_PAD_PTE17__LCD17 0x1E8 0x000 ALT4 0x0 +#define VF610_PAD_PTE18__GPIO_123 0x1EC 0x000 ALT0 0x0 +#define VF610_PAD_PTE18__DCU0_G5 0x1EC 0x000 ALT1 0x0 +#define VF610_PAD_PTE18__SRC_RCON9 0x1EC 0x000 ALT3 0x0 +#define VF610_PAD_PTE18__LCD18 0x1EC 0x000 ALT4 0x0 +#define VF610_PAD_PTE19__GPIO_124 0x1F0 0x000 ALT0 0x0 +#define VF610_PAD_PTE19__DCU0_G6 0x1F0 0x000 ALT1 0x0 +#define VF610_PAD_PTE19__SRC_RCON10 0x1F0 0x000 ALT3 0x0 +#define VF610_PAD_PTE19__LCD19 0x1F0 0x000 ALT4 0x0 +#define VF610_PAD_PTE19__I2C0_SCL 0x1F0 0x33C ALT5 0x3 +#define VF610_PAD_PTE20__GPIO_125 0x1F4 0x000 ALT0 0x0 +#define VF610_PAD_PTE20__DCU0_G7 0x1F4 0x000 ALT1 0x0 +#define VF610_PAD_PTE20__SRC_RCON11 0x1F4 0x000 ALT3 0x0 +#define VF610_PAD_PTE20__LCD20 0x1F4 0x000 ALT4 0x0 +#define VF610_PAD_PTE20__I2C0_SDA 0x1F4 0x340 ALT5 0x3 +#define VF610_PAD_PTE20__EWM_IN 0x1F4 0x000 ALT7 0x0 +#define VF610_PAD_PTE21__GPIO_126 0x1F8 0x000 ALT0 0x0 +#define VF610_PAD_PTE21__DCU0_B0 0x1F8 0x000 ALT1 0x0 +#define VF610_PAD_PTE21__LCD21 0x1F8 0x000 ALT4 0x0 +#define VF610_PAD_PTE22__GPIO_127 0x1FC 0x000 ALT0 0x0 +#define VF610_PAD_PTE22__DCU0_B1 0x1FC 0x000 ALT1 0x0 +#define VF610_PAD_PTE22__LCD22 0x1FC 0x000 ALT4 0x0 +#define VF610_PAD_PTE23__GPIO_128 0x200 0x000 ALT0 0x0 +#define VF610_PAD_PTE23__DCU0_B2 0x200 0x000 ALT1 0x0 +#define VF610_PAD_PTE23__SRC_RCON12 0x200 0x000 ALT3 0x0 +#define VF610_PAD_PTE23__LCD23 0x200 0x000 ALT4 0x0 +#define VF610_PAD_PTE24__GPIO_129 0x204 0x000 ALT0 0x0 +#define VF610_PAD_PTE24__DCU0_B3 0x204 0x000 ALT1 0x0 +#define VF610_PAD_PTE24__SRC_RCON13 0x204 0x000 ALT3 0x0 +#define VF610_PAD_PTE24__LCD24 0x204 0x000 ALT4 0x0 +#define VF610_PAD_PTE25__GPIO_130 0x208 0x000 ALT0 0x0 +#define VF610_PAD_PTE25__DCU0_B4 0x208 0x000 ALT1 0x0 +#define VF610_PAD_PTE25__SRC_RCON14 0x208 0x000 ALT3 0x0 +#define VF610_PAD_PTE25__LCD25 0x208 0x000 ALT4 0x0 +#define VF610_PAD_PTE26__GPIO_131 0x20C 0x000 ALT0 0x0 +#define VF610_PAD_PTE26__DCU0_B5 0x20C 0x000 ALT1 0x0 +#define VF610_PAD_PTE26__SRC_RCON15 0x20C 0x000 ALT3 0x0 +#define VF610_PAD_PTE26__LCD26 0x20C 0x000 ALT4 0x0 +#define VF610_PAD_PTE27__GPIO_132 0x210 0x000 ALT0 0x0 +#define VF610_PAD_PTE27__DCU0_B6 0x210 0x000 ALT1 0x0 +#define VF610_PAD_PTE27__SRC_RCON16 0x210 0x000 ALT3 0x0 +#define VF610_PAD_PTE27__LCD27 0x210 0x000 ALT4 0x0 +#define VF610_PAD_PTE27__I2C1_SCL 0x210 0x344 ALT5 0x3 +#define VF610_PAD_PTE28__GPIO_133 0x214 0x000 ALT0 0x0 +#define VF610_PAD_PTE28__DCU0_B7 0x214 0x000 ALT1 0x0 +#define VF610_PAD_PTE28__SRC_RCON17 0x214 0x000 ALT3 0x0 +#define VF610_PAD_PTE28__LCD28 0x214 0x000 ALT4 0x0 +#define VF610_PAD_PTE28__I2C1_SDA 0x214 0x348 ALT5 0x3 +#define VF610_PAD_PTE28__EWM_OUT 0x214 0x000 ALT7 0x0 +#define VF610_PAD_PTA7__GPIO_134 0x218 0x000 ALT0 0x0 +#define VF610_PAD_PTA7__VIU_PIX_CLK 0x218 0x3AC ALT1 0x1 + +#endif diff -Nur linux-3.10.30/arch/arm/boot/dts/vf610-twr.dts linux-3.10.30-cubox-i/arch/arm/boot/dts/vf610-twr.dts --- linux-3.10.30/arch/arm/boot/dts/vf610-twr.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vf610-twr.dts 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Freescale Semiconductor, 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. + */ + +/dts-v1/; +#include "vf610.dtsi" + +/ { + model = "VF610 Tower Board"; + compatible = "fsl,vf610-twr", "fsl,vf610"; + + chosen { + bootargs = "console=ttyLP1,115200"; + }; + + memory { + reg = <0x80000000 0x8000000>; + }; + + clocks { + audio_ext { + compatible = "fixed-clock"; + clock-frequency = <24576000>; + }; + + enet_ext { + compatible = "fixed-clock"; + clock-frequency = <50000000>; + }; + }; + +}; + +&fec0 { + phy-mode = "rmii"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec0_1>; + status = "okay"; +}; + +&fec1 { + phy-mode = "rmii"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec1_1>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_1>; + status = "okay"; +}; diff -Nur linux-3.10.30/arch/arm/boot/dts/vf610.dtsi linux-3.10.30-cubox-i/arch/arm/boot/dts/vf610.dtsi --- linux-3.10.30/arch/arm/boot/dts/vf610.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/boot/dts/vf610.dtsi 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,464 @@ +/* + * Copyright 2013 Freescale Semiconductor, 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. + */ + +#include "skeleton.dtsi" +#include "vf610-pinfunc.h" +#include + +/ { + aliases { + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + serial4 = &uart4; + serial5 = &uart5; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + gpio3 = &gpio4; + gpio4 = &gpio5; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a5"; + device_type = "cpu"; + reg = <0x0>; + next-level-cache = <&L2>; + }; + }; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + sxosc { + compatible = "fixed-clock"; + clock-frequency = <32768>; + }; + + fxosc { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + }; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&intc>; + ranges; + + aips0: aips-bus@40000000 { + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&intc>; + reg = <0x40000000 0x70000>; + ranges; + + intc: interrupt-controller@40002000 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <1>; + #size-cells = <1>; + interrupt-controller; + reg = <0x40003000 0x1000>, + <0x40002100 0x100>; + }; + + L2: l2-cache@40006000 { + compatible = "arm,pl310-cache"; + reg = <0x40006000 0x1000>; + cache-unified; + cache-level = <2>; + arm,data-latency = <1 1 1>; + arm,tag-latency = <2 2 2>; + }; + + uart0: serial@40027000 { + compatible = "fsl,vf610-lpuart"; + reg = <0x40027000 0x1000>; + interrupts = <0 61 0x00>; + clocks = <&clks VF610_CLK_UART0>; + clock-names = "ipg"; + status = "disabled"; + }; + + uart1: serial@40028000 { + compatible = "fsl,vf610-lpuart"; + reg = <0x40028000 0x1000>; + interrupts = <0 62 0x04>; + clocks = <&clks VF610_CLK_UART1>; + clock-names = "ipg"; + status = "disabled"; + }; + + uart2: serial@40029000 { + compatible = "fsl,vf610-lpuart"; + reg = <0x40029000 0x1000>; + interrupts = <0 63 0x04>; + clocks = <&clks VF610_CLK_UART2>; + clock-names = "ipg"; + status = "disabled"; + }; + + uart3: serial@4002a000 { + compatible = "fsl,vf610-lpuart"; + reg = <0x4002a000 0x1000>; + interrupts = <0 64 0x04>; + clocks = <&clks VF610_CLK_UART3>; + clock-names = "ipg"; + status = "disabled"; + }; + + sai2: sai@40031000 { + compatible = "fsl,vf610-sai"; + reg = <0x40031000 0x1000>; + interrupts = <0 86 0x04>; + clocks = <&clks VF610_CLK_SAI2>; + clock-names = "sai"; + status = "disabled"; + }; + + pit: pit@40037000 { + compatible = "fsl,vf610-pit"; + reg = <0x40037000 0x1000>; + interrupts = <0 39 0x04>; + clocks = <&clks VF610_CLK_PIT>; + clock-names = "pit"; + }; + + wdog@4003e000 { + compatible = "fsl,vf610-wdt", "fsl,imx21-wdt"; + reg = <0x4003e000 0x1000>; + clocks = <&clks VF610_CLK_WDT>; + clock-names = "wdog"; + }; + + qspi0: quadspi@40044000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,vf610-qspi"; + reg = <0x40044000 0x1000>; + interrupts = <0 24 0x04>; + clocks = <&clks VF610_CLK_QSPI0_EN>, + <&clks VF610_CLK_QSPI0>; + clock-names = "qspi_en", "qspi"; + status = "disabled"; + }; + + iomuxc: iomuxc@40048000 { + compatible = "fsl,vf610-iomuxc"; + reg = <0x40048000 0x1000>; + #gpio-range-cells = <2>; + + /* functions and groups pins */ + + dcu0 { + pinctrl_dcu0_1: dcu0grp_1 { + fsl,pins = < + VF610_PAD_PTB8__GPIO_30 0x42 + VF610_PAD_PTE0__DCU0_HSYNC 0x42 + VF610_PAD_PTE1__DCU0_VSYNC 0x42 + VF610_PAD_PTE2__DCU0_PCLK 0x42 + VF610_PAD_PTE4__DCU0_DE 0x42 + VF610_PAD_PTE5__DCU0_R0 0x42 + VF610_PAD_PTE6__DCU0_R1 0x42 + VF610_PAD_PTE7__DCU0_R2 0x42 + VF610_PAD_PTE8__DCU0_R3 0x42 + VF610_PAD_PTE9__DCU0_R4 0x42 + VF610_PAD_PTE10__DCU0_R5 0x42 + VF610_PAD_PTE11__DCU0_R6 0x42 + VF610_PAD_PTE12__DCU0_R7 0x42 + VF610_PAD_PTE13__DCU0_G0 0x42 + VF610_PAD_PTE14__DCU0_G1 0x42 + VF610_PAD_PTE15__DCU0_G2 0x42 + VF610_PAD_PTE16__DCU0_G3 0x42 + VF610_PAD_PTE17__DCU0_G4 0x42 + VF610_PAD_PTE18__DCU0_G5 0x42 + VF610_PAD_PTE19__DCU0_G6 0x42 + VF610_PAD_PTE20__DCU0_G7 0x42 + VF610_PAD_PTE21__DCU0_B0 0x42 + VF610_PAD_PTE22__DCU0_B1 0x42 + VF610_PAD_PTE23__DCU0_B2 0x42 + VF610_PAD_PTE24__DCU0_B3 0x42 + VF610_PAD_PTE25__DCU0_B4 0x42 + VF610_PAD_PTE26__DCU0_B5 0x42 + VF610_PAD_PTE27__DCU0_B6 0x42 + VF610_PAD_PTE28__DCU0_B7 0x42 + >; + }; + }; + + dspi0 { + pinctrl_dspi0_1: dspi0grp_1 { + fsl,pins = < + VF610_PAD_PTB19__DSPI0_CS0 0x1182 + VF610_PAD_PTB20__DSPI0_SIN 0x1181 + VF610_PAD_PTB21__DSPI0_SOUT 0x1182 + VF610_PAD_PTB22__DSPI0_SCK 0x1182 + >; + }; + }; + + esdhc1 { + pinctrl_esdhc1_1: esdhc1grp_1 { + fsl,pins = < + VF610_PAD_PTA24__ESDHC1_CLK 0x31ef + VF610_PAD_PTA25__ESDHC1_CMD 0x31ef + VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef + VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef + VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef + VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef + VF610_PAD_PTA7__GPIO_134 0x219d + >; + }; + }; + + fec0 { + pinctrl_fec0_1: fec0grp_1 { + fsl,pins = < + VF610_PAD_PTA6__RMII_CLKIN 0x30d1 + VF610_PAD_PTC0__ENET_RMII0_MDC 0x30d3 + VF610_PAD_PTC1__ENET_RMII0_MDIO 0x30d1 + VF610_PAD_PTC2__ENET_RMII0_CRS 0x30d1 + VF610_PAD_PTC3__ENET_RMII0_RXD1 0x30d1 + VF610_PAD_PTC4__ENET_RMII0_RXD0 0x30d1 + VF610_PAD_PTC5__ENET_RMII0_RXER 0x30d1 + VF610_PAD_PTC6__ENET_RMII0_TXD1 0x30d2 + VF610_PAD_PTC7__ENET_RMII0_TXD0 0x30d2 + VF610_PAD_PTC8__ENET_RMII0_TXEN 0x30d2 + >; + }; + }; + + fec1 { + pinctrl_fec1_1: fec1grp_1 { + fsl,pins = < + VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2 + VF610_PAD_PTC10__ENET_RMII1_MDIO 0x30d3 + VF610_PAD_PTC11__ENET_RMII1_CRS 0x30d1 + VF610_PAD_PTC12__ENET_RMII_RXD1 0x30d1 + VF610_PAD_PTC13__ENET_RMII1_RXD0 0x30d1 + VF610_PAD_PTC14__ENET_RMII1_RXER 0x30d1 + VF610_PAD_PTC15__ENET_RMII1_TXD1 0x30d2 + VF610_PAD_PTC16__ENET_RMII1_TXD0 0x30d2 + VF610_PAD_PTC17__ENET_RMII1_TXEN 0x30d2 + >; + }; + }; + + i2c0 { + pinctrl_i2c0_1: i2c0grp_1 { + fsl,pins = < + VF610_PAD_PTB14__I2C0_SCL 0x30d3 + VF610_PAD_PTB15__I2C0_SDA 0x30d3 + >; + }; + }; + + pwm0 { + pinctrl_pwm0_1: pwm0grp_1 { + fsl,pins = < + VF610_PAD_PTB0__FTM0_CH0 0x1582 + VF610_PAD_PTB1__FTM0_CH1 0x1582 + VF610_PAD_PTB2__FTM0_CH2 0x1582 + VF610_PAD_PTB3__FTM0_CH3 0x1582 + VF610_PAD_PTB6__FTM0_CH6 0x1582 + VF610_PAD_PTB7__FTM0_CH7 0x1582 + >; + }; + }; + + qspi0 { + pinctrl_qspi0_1: qspi0grp_1 { + fsl,pins = < + VF610_PAD_PTD0__QSPI0_A_QSCK 0x307b + VF610_PAD_PTD1__QSPI0_A_CS0 0x307f + VF610_PAD_PTD2__QSPI0_A_DATA3 0x3073 + VF610_PAD_PTD3__QSPI0_A_DATA2 0x3073 + VF610_PAD_PTD4__QSPI0_A_DATA1 0x3073 + VF610_PAD_PTD5__QSPI0_A_DATA0 0x307b + VF610_PAD_PTD7__QSPI0_B_QSCK 0x307b + VF610_PAD_PTD8__QSPI0_B_CS0 0x307f + VF610_PAD_PTD9__QSPI0_B_DATA3 0x3073 + VF610_PAD_PTD10__QSPI0_B_DATA2 0x3073 + VF610_PAD_PTD11__QSPI0_B_DATA1 0x3073 + VF610_PAD_PTD12__QSPI0_B_DATA0 0x307b + >; + }; + }; + + sai2 { + pinctrl_sai2_1: sai2grp_1 { + fsl,pins = < + VF610_PAD_PTA16__SAI2_TX_BCLK 0x02ed + VF610_PAD_PTA18__SAI2_TX_DATA 0x02ee + VF610_PAD_PTA19__SAI2_TX_SYNC 0x02ed + VF610_PAD_PTA21__SAI2_RX_BCLK 0x02ed + VF610_PAD_PTA22__SAI2_RX_DATA 0x02ed + VF610_PAD_PTA23__SAI2_RX_SYNC 0x02ed + VF610_PAD_PTB18__EXT_AUDIO_MCLK 0x02ed + >; + }; + }; + + uart1 { + pinctrl_uart1_1: uart1grp_1 { + fsl,pins = < + VF610_PAD_PTB4__UART1_TX 0x21a2 + VF610_PAD_PTB5__UART1_RX 0x21a1 + >; + }; + }; + + usbvbus { + pinctrl_usbvbus_1: usbvbusgrp_1 { + fsl,pins = < + VF610_PAD_PTA24__USB1_VBUS_EN 0x219c + VF610_PAD_PTA16__USB0_VBUS_EN 0x219c + >; + }; + }; + + }; + + gpio1: gpio@40049000 { + compatible = "fsl,vf610-gpio"; + reg = <0x40049000 0x1000 0x400ff000 0x40>; + interrupts = <0 107 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 0 32>; + }; + + gpio2: gpio@4004a000 { + compatible = "fsl,vf610-gpio"; + reg = <0x4004a000 0x1000 0x400ff040 0x40>; + interrupts = <0 108 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 32 32>; + }; + + gpio3: gpio@4004b000 { + compatible = "fsl,vf610-gpio"; + reg = <0x4004b000 0x1000 0x400ff080 0x40>; + interrupts = <0 109 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 64 32>; + }; + + gpio4: gpio@4004c000 { + compatible = "fsl,vf610-gpio"; + reg = <0x4004c000 0x1000 0x400ff0c0 0x40>; + interrupts = <0 110 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 96 32>; + }; + + gpio5: gpio@4004d000 { + compatible = "fsl,vf610-gpio"; + reg = <0x4004d000 0x1000 0x400ff100 0x40>; + interrupts = <0 111 0x04>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 128 7>; + }; + + anatop@40050000 { + compatible = "fsl,vf610-anatop"; + reg = <0x40050000 0x1000>; + }; + + i2c0: i2c@40066000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,vf610-i2c"; + reg = <0x40066000 0x1000>; + interrupts =<0 71 0x04>; + clocks = <&clks VF610_CLK_I2C0>; + clock-names = "ipg"; + status = "disabled"; + }; + + clks: ccm@4006b000 { + compatible = "fsl,vf610-ccm"; + reg = <0x4006b000 0x1000>; + #clock-cells = <1>; + }; + }; + + aips1: aips-bus@40080000 { + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x40080000 0x80000>; + ranges; + + uart4: serial@400a9000 { + compatible = "fsl,vf610-lpuart"; + reg = <0x400a9000 0x1000>; + interrupts = <0 65 0x04>; + clocks = <&clks VF610_CLK_UART4>; + clock-names = "ipg"; + status = "disabled"; + }; + + uart5: serial@400aa000 { + compatible = "fsl,vf610-lpuart"; + reg = <0x400aa000 0x1000>; + interrupts = <0 66 0x04>; + clocks = <&clks VF610_CLK_UART5>; + clock-names = "ipg"; + status = "disabled"; + }; + + fec0: ethernet@400d0000 { + compatible = "fsl,mvf600-fec"; + reg = <0x400d0000 0x1000>; + interrupts = <0 78 0x04>; + clocks = <&clks VF610_CLK_ENET0>, + <&clks VF610_CLK_ENET0>, + <&clks VF610_CLK_ENET>; + clock-names = "ipg", "ahb", "ptp"; + status = "disabled"; + }; + + fec1: ethernet@400d1000 { + compatible = "fsl,mvf600-fec"; + reg = <0x400d1000 0x1000>; + interrupts = <0 79 0x04>; + clocks = <&clks VF610_CLK_ENET1>, + <&clks VF610_CLK_ENET1>, + <&clks VF610_CLK_ENET>; + clock-names = "ipg", "ahb", "ptp"; + status = "disabled"; + }; + }; + }; +}; diff -Nur linux-3.10.30/arch/arm/common/Makefile linux-3.10.30-cubox-i/arch/arm/common/Makefile --- linux-3.10.30/arch/arm/common/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/common/Makefile 2014-03-08 20:32:53.000000000 +0100 @@ -14,5 +14,9 @@ obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_MCPM) += mcpm_head.o mcpm_entry.o mcpm_platsmp.o vlock.o +obj-$(CONFIG_BL_SWITCHER) += bL_switcher.o +obj-$(CONFIG_BL_SWITCHER_DUMMY_IF) += bL_switcher_dummy_if.o + AFLAGS_mcpm_head.o := -march=armv7-a AFLAGS_vlock.o := -march=armv7-a +CFLAGS_REMOVE_mcpm_entry.o = -pg diff -Nur linux-3.10.30/arch/arm/common/bL_switcher.c linux-3.10.30-cubox-i/arch/arm/common/bL_switcher.c --- linux-3.10.30/arch/arm/common/bL_switcher.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/common/bL_switcher.c 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,864 @@ +/* + * arch/arm/common/bL_switcher.c -- big.LITTLE cluster switcher core driver + * + * Created by: Nicolas Pitre, March 2012 + * Copyright: (C) 2012 Linaro Limited + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + + +/* + * Use our own MPIDR accessors as the generic ones in asm/cputype.h have + * __attribute_const__ and we don't want the compiler to assume any + * constness here as the value _does_ change along some code paths. + */ + +static int read_mpidr(void) +{ + unsigned int id; + asm volatile ("mrc\tp15, 0, %0, c0, c0, 5" : "=r" (id)); + return id & MPIDR_HWID_BITMASK; +} + +/* + * Get a global nanosecond time stamp for tracing. + */ +static s64 get_ns(void) +{ + struct timespec ts; + getnstimeofday(&ts); + return timespec_to_ns(&ts); +} + +/* + * bL switcher core code. + */ + +static void bL_do_switch(void *_arg) +{ + unsigned ib_mpidr, ib_cpu, ib_cluster; + long volatile handshake, **handshake_ptr = _arg; + + pr_debug("%s\n", __func__); + + ib_mpidr = cpu_logical_map(smp_processor_id()); + ib_cpu = MPIDR_AFFINITY_LEVEL(ib_mpidr, 0); + ib_cluster = MPIDR_AFFINITY_LEVEL(ib_mpidr, 1); + + /* Advertise our handshake location */ + if (handshake_ptr) { + handshake = 0; + *handshake_ptr = &handshake; + } else + handshake = -1; + + /* + * Our state has been saved at this point. Let's release our + * inbound CPU. + */ + mcpm_set_entry_vector(ib_cpu, ib_cluster, cpu_resume); + sev(); + + /* + * From this point, we must assume that our counterpart CPU might + * have taken over in its parallel world already, as if execution + * just returned from cpu_suspend(). It is therefore important to + * be very careful not to make any change the other guy is not + * expecting. This is why we need stack isolation. + * + * Fancy under cover tasks could be performed here. For now + * we have none. + */ + + /* + * Let's wait until our inbound is alive. + */ + while (!handshake) { + wfe(); + smp_mb(); + } + + /* Let's put ourself down. */ + mcpm_cpu_power_down(); + + /* should never get here */ + BUG(); +} + +/* + * Stack isolation. To ensure 'current' remains valid, we just use another + * piece of our thread's stack space which should be fairly lightly used. + * The selected area starts just above the thread_info structure located + * at the very bottom of the stack, aligned to a cache line, and indexed + * with the cluster number. + */ +#define STACK_SIZE 512 +extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); +static int bL_switchpoint(unsigned long _arg) +{ + unsigned int mpidr = read_mpidr(); + unsigned int clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 1); + void *stack = current_thread_info() + 1; + stack = PTR_ALIGN(stack, L1_CACHE_BYTES); + stack += clusterid * STACK_SIZE + STACK_SIZE; + call_with_stack(bL_do_switch, (void *)_arg, stack); + BUG(); +} + +/* + * Generic switcher interface + */ + +static unsigned int bL_gic_id[MAX_CPUS_PER_CLUSTER][MAX_NR_CLUSTERS]; +static int bL_switcher_cpu_pairing[NR_CPUS]; + +/* + * bL_switch_to - Switch to a specific cluster for the current CPU + * @new_cluster_id: the ID of the cluster to switch to. + * + * This function must be called on the CPU to be switched. + * Returns 0 on success, else a negative status code. + */ +static int bL_switch_to(unsigned int new_cluster_id) +{ + unsigned int mpidr, this_cpu, that_cpu; + unsigned int ob_mpidr, ob_cpu, ob_cluster, ib_mpidr, ib_cpu, ib_cluster; + struct completion inbound_alive; + struct tick_device *tdev; + enum clock_event_mode tdev_mode; + long volatile *handshake_ptr; + int ipi_nr, ret; + + this_cpu = smp_processor_id(); + ob_mpidr = read_mpidr(); + ob_cpu = MPIDR_AFFINITY_LEVEL(ob_mpidr, 0); + ob_cluster = MPIDR_AFFINITY_LEVEL(ob_mpidr, 1); + BUG_ON(cpu_logical_map(this_cpu) != ob_mpidr); + + if (new_cluster_id == ob_cluster) + return 0; + + that_cpu = bL_switcher_cpu_pairing[this_cpu]; + ib_mpidr = cpu_logical_map(that_cpu); + ib_cpu = MPIDR_AFFINITY_LEVEL(ib_mpidr, 0); + ib_cluster = MPIDR_AFFINITY_LEVEL(ib_mpidr, 1); + + pr_debug("before switch: CPU %d MPIDR %#x -> %#x\n", + this_cpu, ob_mpidr, ib_mpidr); + + this_cpu = smp_processor_id(); + + /* Close the gate for our entry vectors */ + mcpm_set_entry_vector(ob_cpu, ob_cluster, NULL); + mcpm_set_entry_vector(ib_cpu, ib_cluster, NULL); + + /* Install our "inbound alive" notifier. */ + init_completion(&inbound_alive); + ipi_nr = register_ipi_completion(&inbound_alive, this_cpu); + ipi_nr |= ((1 << 16) << bL_gic_id[ob_cpu][ob_cluster]); + mcpm_set_early_poke(ib_cpu, ib_cluster, gic_get_sgir_physaddr(), ipi_nr); + + /* + * Let's wake up the inbound CPU now in case it requires some delay + * to come online, but leave it gated in our entry vector code. + */ + ret = mcpm_cpu_power_up(ib_cpu, ib_cluster); + if (ret) { + pr_err("%s: mcpm_cpu_power_up() returned %d\n", __func__, ret); + return ret; + } + + /* + * Raise a SGI on the inbound CPU to make sure it doesn't stall + * in a possible WFI, such as in bL_power_down(). + */ + gic_send_sgi(bL_gic_id[ib_cpu][ib_cluster], 0); + + /* + * Wait for the inbound to come up. This allows for other + * tasks to be scheduled in the mean time. + */ + wait_for_completion(&inbound_alive); + mcpm_set_early_poke(ib_cpu, ib_cluster, 0, 0); + + /* + * From this point we are entering the switch critical zone + * and can't sleep/schedule anymore. + */ + local_irq_disable(); + local_fiq_disable(); + trace_cpu_migrate_begin(get_ns(), ob_mpidr); + + /* redirect GIC's SGIs to our counterpart */ + gic_migrate_target(bL_gic_id[ib_cpu][ib_cluster]); + + tdev = tick_get_device(this_cpu); + if (tdev && !cpumask_equal(tdev->evtdev->cpumask, cpumask_of(this_cpu))) + tdev = NULL; + if (tdev) { + tdev_mode = tdev->evtdev->mode; + clockevents_set_mode(tdev->evtdev, CLOCK_EVT_MODE_SHUTDOWN); + } + + ret = cpu_pm_enter(); + + /* we can not tolerate errors at this point */ + if (ret) + panic("%s: cpu_pm_enter() returned %d\n", __func__, ret); + + /* + * Swap the physical CPUs in the logical map for this logical CPU. + * This must be flushed to RAM as the resume code + * needs to access it while the caches are still disabled. + */ + cpu_logical_map(this_cpu) = ib_mpidr; + cpu_logical_map(that_cpu) = ob_mpidr; + sync_cache_w(&cpu_logical_map(this_cpu)); + + /* Let's do the actual CPU switch. */ + ret = cpu_suspend((unsigned long)&handshake_ptr, bL_switchpoint); + if (ret > 0) + panic("%s: cpu_suspend() returned %d\n", __func__, ret); + + /* We are executing on the inbound CPU at this point */ + mpidr = read_mpidr(); + pr_debug("after switch: CPU %d MPIDR %#x\n", this_cpu, mpidr); + BUG_ON(mpidr != ib_mpidr); + + mcpm_cpu_powered_up(); + + ret = cpu_pm_exit(); + + if (tdev) { + clockevents_set_mode(tdev->evtdev, tdev_mode); + clockevents_program_event(tdev->evtdev, + tdev->evtdev->next_event, 1); + } + + trace_cpu_migrate_finish(get_ns(), ib_mpidr); + local_fiq_enable(); + local_irq_enable(); + + *handshake_ptr = 1; + dsb_sev(); + + if (ret) + pr_err("%s exiting with error %d\n", __func__, ret); + return ret; +} + +struct bL_thread { + spinlock_t lock; + struct task_struct *task; + wait_queue_head_t wq; + int wanted_cluster; + struct completion started; + bL_switch_completion_handler completer; + void *completer_cookie; +}; + +static struct bL_thread bL_threads[NR_CPUS]; + +static int bL_switcher_thread(void *arg) +{ + struct bL_thread *t = arg; + struct sched_param param = { .sched_priority = 1 }; + int cluster; + bL_switch_completion_handler completer; + void *completer_cookie; + + sched_setscheduler_nocheck(current, SCHED_FIFO, ¶m); + complete(&t->started); + + do { + if (signal_pending(current)) + flush_signals(current); + wait_event_interruptible(t->wq, + t->wanted_cluster != -1 || + kthread_should_stop()); + + spin_lock(&t->lock); + cluster = t->wanted_cluster; + completer = t->completer; + completer_cookie = t->completer_cookie; + t->wanted_cluster = -1; + t->completer = NULL; + spin_unlock(&t->lock); + + if (cluster != -1) { + bL_switch_to(cluster); + + if (completer) + completer(completer_cookie); + } + } while (!kthread_should_stop()); + + return 0; +} + +static struct task_struct * bL_switcher_thread_create(int cpu, void *arg) +{ + struct task_struct *task; + + task = kthread_create_on_node(bL_switcher_thread, arg, + cpu_to_node(cpu), "kswitcher_%d", cpu); + if (!IS_ERR(task)) { + kthread_bind(task, cpu); + wake_up_process(task); + } else + pr_err("%s failed for CPU %d\n", __func__, cpu); + return task; +} + +/* + * bL_switch_request_cb - Switch to a specific cluster for the given CPU, + * with completion notification via a callback + * + * @cpu: the CPU to switch + * @new_cluster_id: the ID of the cluster to switch to. + * @completer: switch completion callback. if non-NULL, + * @completer(@completer_cookie) will be called on completion of + * the switch, in non-atomic context. + * @completer_cookie: opaque context argument for @completer. + * + * This function causes a cluster switch on the given CPU by waking up + * the appropriate switcher thread. This function may or may not return + * before the switch has occurred. + * + * If a @completer callback function is supplied, it will be called when + * the switch is complete. This can be used to determine asynchronously + * when the switch is complete, regardless of when bL_switch_request() + * returns. When @completer is supplied, no new switch request is permitted + * for the affected CPU until after the switch is complete, and @completer + * has returned. + */ +int bL_switch_request_cb(unsigned int cpu, unsigned int new_cluster_id, + bL_switch_completion_handler completer, + void *completer_cookie) +{ + struct bL_thread *t; + + if (cpu >= ARRAY_SIZE(bL_threads)) { + pr_err("%s: cpu %d out of bounds\n", __func__, cpu); + return -EINVAL; + } + + t = &bL_threads[cpu]; + + if (IS_ERR(t->task)) + return PTR_ERR(t->task); + if (!t->task) + return -ESRCH; + + spin_lock(&t->lock); + if (t->completer) { + spin_unlock(&t->lock); + return -EBUSY; + } + t->completer = completer; + t->completer_cookie = completer_cookie; + t->wanted_cluster = new_cluster_id; + spin_unlock(&t->lock); + wake_up(&t->wq); + return 0; +} + +EXPORT_SYMBOL_GPL(bL_switch_request_cb); + +/* + * Detach an outstanding switch request. + * + * The switcher will continue with the switch request in the background, + * but the completer function will not be called. + * + * This may be necessary if the completer is in a kernel module which is + * about to be unloaded. + */ +void bL_switch_request_detach(unsigned int cpu, + bL_switch_completion_handler completer) +{ + struct bL_thread *t; + + if (cpu >= ARRAY_SIZE(bL_threads)) { + pr_err("%s: cpu %d out of bounds\n", __func__, cpu); + return; + } + + t = &bL_threads[cpu]; + + if (IS_ERR(t->task) || !t->task) + return; + + spin_lock(&t->lock); + if (t->completer == completer) + t->completer = NULL; + spin_unlock(&t->lock); +} + +EXPORT_SYMBOL_GPL(bL_switch_request_detach); + +/* + * Activation and configuration code. + */ + +static DEFINE_MUTEX(bL_switcher_activation_lock); +static BLOCKING_NOTIFIER_HEAD(bL_activation_notifier); +static unsigned int bL_switcher_active; +static unsigned int bL_switcher_cpu_original_cluster[NR_CPUS]; +static cpumask_t bL_switcher_removed_logical_cpus; + +int bL_switcher_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&bL_activation_notifier, nb); +} +EXPORT_SYMBOL_GPL(bL_switcher_register_notifier); + +int bL_switcher_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&bL_activation_notifier, nb); +} +EXPORT_SYMBOL_GPL(bL_switcher_unregister_notifier); + +static int bL_activation_notify(unsigned long val) +{ + int ret; + + ret = blocking_notifier_call_chain(&bL_activation_notifier, val, NULL); + if (ret & NOTIFY_STOP_MASK) + pr_err("%s: notifier chain failed with status 0x%x\n", + __func__, ret); + return notifier_to_errno(ret); +} + +static void bL_switcher_restore_cpus(void) +{ + int i; + + for_each_cpu(i, &bL_switcher_removed_logical_cpus) + cpu_up(i); +} + +static int bL_switcher_halve_cpus(void) +{ + int i, j, cluster_0, gic_id, ret; + unsigned int cpu, cluster, mask; + cpumask_t available_cpus; + + /* First pass to validate what we have */ + mask = 0; + for_each_online_cpu(i) { + cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 0); + cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 1); + if (cluster >= 2) { + pr_err("%s: only dual cluster systems are supported\n", __func__); + return -EINVAL; + } + if (WARN_ON(cpu >= MAX_CPUS_PER_CLUSTER)) + return -EINVAL; + mask |= (1 << cluster); + } + if (mask != 3) { + pr_err("%s: no CPU pairing possible\n", __func__); + return -EINVAL; + } + + /* + * Now let's do the pairing. We match each CPU with another CPU + * from a different cluster. To get a uniform scheduling behavior + * without fiddling with CPU topology and compute capacity data, + * we'll use logical CPUs initially belonging to the same cluster. + */ + memset(bL_switcher_cpu_pairing, -1, sizeof(bL_switcher_cpu_pairing)); + cpumask_copy(&available_cpus, cpu_online_mask); + cluster_0 = -1; + for_each_cpu(i, &available_cpus) { + int match = -1; + cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 1); + if (cluster_0 == -1) + cluster_0 = cluster; + if (cluster != cluster_0) + continue; + cpumask_clear_cpu(i, &available_cpus); + for_each_cpu(j, &available_cpus) { + cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(j), 1); + /* + * Let's remember the last match to create "odd" + * pairing on purpose in order for other code not + * to assume any relation between physical and + * logical CPU numbers. + */ + if (cluster != cluster_0) + match = j; + } + if (match != -1) { + bL_switcher_cpu_pairing[i] = match; + cpumask_clear_cpu(match, &available_cpus); + pr_info("CPU%d paired with CPU%d\n", i, match); + } + } + + /* + * Now we disable the unwanted CPUs i.e. everything that has no + * pairing information (that includes the pairing counterparts). + */ + cpumask_clear(&bL_switcher_removed_logical_cpus); + for_each_online_cpu(i) { + cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 0); + cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 1); + + /* Let's take note of the GIC ID for this CPU */ + gic_id = gic_get_cpu_id(i); + if (gic_id < 0) { + pr_err("%s: bad GIC ID for CPU %d\n", __func__, i); + bL_switcher_restore_cpus(); + return -EINVAL; + } + bL_gic_id[cpu][cluster] = gic_id; + pr_info("GIC ID for CPU %u cluster %u is %u\n", + cpu, cluster, gic_id); + + if (bL_switcher_cpu_pairing[i] != -1) { + bL_switcher_cpu_original_cluster[i] = cluster; + continue; + } + + ret = cpu_down(i); + if (ret) { + bL_switcher_restore_cpus(); + return ret; + } + cpumask_set_cpu(i, &bL_switcher_removed_logical_cpus); + } + + return 0; +} + +/* Determine the logical CPU a given physical CPU is grouped on. */ +int bL_switcher_get_logical_index(u32 mpidr) +{ + int cpu; + + if (!bL_switcher_active) + return -EUNATCH; + + mpidr &= MPIDR_HWID_BITMASK; + for_each_online_cpu(cpu) { + int pairing = bL_switcher_cpu_pairing[cpu]; + if (pairing == -1) + continue; + if ((mpidr == cpu_logical_map(cpu)) || + (mpidr == cpu_logical_map(pairing))) + return cpu; + } + return -EINVAL; +} + +static void bL_switcher_trace_trigger_cpu(void *__always_unused info) +{ + trace_cpu_migrate_current(get_ns(), read_mpidr()); +} + +int bL_switcher_trace_trigger(void) +{ + int ret; + + preempt_disable(); + + bL_switcher_trace_trigger_cpu(NULL); + ret = smp_call_function(bL_switcher_trace_trigger_cpu, NULL, true); + + preempt_enable(); + + return ret; +} +EXPORT_SYMBOL_GPL(bL_switcher_trace_trigger); + +static int bL_switcher_enable(void) +{ + int cpu, ret; + + mutex_lock(&bL_switcher_activation_lock); + cpu_hotplug_driver_lock(); + if (bL_switcher_active) { + cpu_hotplug_driver_unlock(); + mutex_unlock(&bL_switcher_activation_lock); + return 0; + } + + pr_info("big.LITTLE switcher initializing\n"); + + ret = bL_activation_notify(BL_NOTIFY_PRE_ENABLE); + if (ret) + goto error; + + ret = bL_switcher_halve_cpus(); + if (ret) + goto error; + + bL_switcher_trace_trigger(); + + for_each_online_cpu(cpu) { + struct bL_thread *t = &bL_threads[cpu]; + spin_lock_init(&t->lock); + init_waitqueue_head(&t->wq); + init_completion(&t->started); + t->wanted_cluster = -1; + t->task = bL_switcher_thread_create(cpu, t); + } + + bL_switcher_active = 1; + bL_activation_notify(BL_NOTIFY_POST_ENABLE); + pr_info("big.LITTLE switcher initialized\n"); + goto out; + +error: + pr_warning("big.LITTLE switcher initialization failed\n"); + bL_activation_notify(BL_NOTIFY_POST_DISABLE); + +out: + cpu_hotplug_driver_unlock(); + mutex_unlock(&bL_switcher_activation_lock); + return ret; +} + +#ifdef CONFIG_SYSFS + +static void bL_switcher_disable(void) +{ + unsigned int cpu, cluster; + struct bL_thread *t; + struct task_struct *task; + + mutex_lock(&bL_switcher_activation_lock); + cpu_hotplug_driver_lock(); + + if (!bL_switcher_active) + goto out; + + if (bL_activation_notify(BL_NOTIFY_PRE_DISABLE) != 0) { + bL_activation_notify(BL_NOTIFY_POST_ENABLE); + goto out; + } + + bL_switcher_active = 0; + + /* + * To deactivate the switcher, we must shut down the switcher + * threads to prevent any other requests from being accepted. + * Then, if the final cluster for given logical CPU is not the + * same as the original one, we'll recreate a switcher thread + * just for the purpose of switching the CPU back without any + * possibility for interference from external requests. + */ + for_each_online_cpu(cpu) { + t = &bL_threads[cpu]; + task = t->task; + t->task = NULL; + if (!task || IS_ERR(task)) + continue; + kthread_stop(task); + /* no more switch may happen on this CPU at this point */ + cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1); + if (cluster == bL_switcher_cpu_original_cluster[cpu]) + continue; + init_completion(&t->started); + t->wanted_cluster = bL_switcher_cpu_original_cluster[cpu]; + task = bL_switcher_thread_create(cpu, t); + if (!IS_ERR(task)) { + wait_for_completion(&t->started); + kthread_stop(task); + cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1); + if (cluster == bL_switcher_cpu_original_cluster[cpu]) + continue; + } + /* If execution gets here, we're in trouble. */ + pr_crit("%s: unable to restore original cluster for CPU %d\n", + __func__, cpu); + pr_crit("%s: CPU %d can't be restored\n", + __func__, bL_switcher_cpu_pairing[cpu]); + cpumask_clear_cpu(bL_switcher_cpu_pairing[cpu], + &bL_switcher_removed_logical_cpus); + } + + bL_switcher_restore_cpus(); + bL_switcher_trace_trigger(); + + bL_activation_notify(BL_NOTIFY_POST_DISABLE); + +out: + cpu_hotplug_driver_unlock(); + mutex_unlock(&bL_switcher_activation_lock); +} + +static ssize_t bL_switcher_active_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", bL_switcher_active); +} + +static ssize_t bL_switcher_active_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret; + + switch (buf[0]) { + case '0': + bL_switcher_disable(); + ret = 0; + break; + case '1': + ret = bL_switcher_enable(); + break; + default: + ret = -EINVAL; + } + + return (ret >= 0) ? count : ret; +} + +static ssize_t bL_switcher_trace_trigger_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = bL_switcher_trace_trigger(); + + return ret ? ret : count; +} + +static struct kobj_attribute bL_switcher_active_attr = + __ATTR(active, 0644, bL_switcher_active_show, bL_switcher_active_store); + +static struct kobj_attribute bL_switcher_trace_trigger_attr = + __ATTR(trace_trigger, 0200, NULL, bL_switcher_trace_trigger_store); + +static struct attribute *bL_switcher_attrs[] = { + &bL_switcher_active_attr.attr, + &bL_switcher_trace_trigger_attr.attr, + NULL, +}; + +static struct attribute_group bL_switcher_attr_group = { + .attrs = bL_switcher_attrs, +}; + +static struct kobject *bL_switcher_kobj; + +static int __init bL_switcher_sysfs_init(void) +{ + int ret; + + bL_switcher_kobj = kobject_create_and_add("bL_switcher", kernel_kobj); + if (!bL_switcher_kobj) + return -ENOMEM; + ret = sysfs_create_group(bL_switcher_kobj, &bL_switcher_attr_group); + if (ret) + kobject_put(bL_switcher_kobj); + return ret; +} + +#endif /* CONFIG_SYSFS */ + +bool bL_switcher_get_enabled(void) +{ + mutex_lock(&bL_switcher_activation_lock); + + return bL_switcher_active; +} +EXPORT_SYMBOL_GPL(bL_switcher_get_enabled); + +void bL_switcher_put_enabled(void) +{ + mutex_unlock(&bL_switcher_activation_lock); +} +EXPORT_SYMBOL_GPL(bL_switcher_put_enabled); + +/* + * Veto any CPU hotplug operation while the switcher is active. + * We're just not ready to deal with that given the trickery involved. + */ +static int bL_switcher_hotplug_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action) { + case CPU_UP_PREPARE: + case CPU_DOWN_PREPARE: + if (bL_switcher_active) + return NOTIFY_BAD; + } + return NOTIFY_DONE; +} + +static struct notifier_block bL_switcher_hotplug_notifier = + { &bL_switcher_hotplug_callback, NULL, 0 }; + +#ifdef CONFIG_SCHED_HMP +static bool no_bL_switcher = true; +#else +static bool no_bL_switcher; +#endif +core_param(no_bL_switcher, no_bL_switcher, bool, 0644); + +static int __init bL_switcher_init(void) +{ + int ret; + + if (MAX_NR_CLUSTERS != 2) { + pr_err("%s: only dual cluster systems are supported\n", __func__); + return -EINVAL; + } + + register_cpu_notifier(&bL_switcher_hotplug_notifier); + + if (!no_bL_switcher) { + ret = bL_switcher_enable(); + if (ret) + return ret; + } + +#ifdef CONFIG_SYSFS + ret = bL_switcher_sysfs_init(); + if (ret) + pr_err("%s: unable to create sysfs entry\n", __func__); +#endif + + return 0; +} + +late_initcall(bL_switcher_init); diff -Nur linux-3.10.30/arch/arm/common/bL_switcher_dummy_if.c linux-3.10.30-cubox-i/arch/arm/common/bL_switcher_dummy_if.c --- linux-3.10.30/arch/arm/common/bL_switcher_dummy_if.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/common/bL_switcher_dummy_if.c 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,71 @@ +/* + * arch/arm/common/bL_switcher_dummy_if.c -- b.L switcher dummy interface + * + * Created by: Nicolas Pitre, November 2012 + * Copyright: (C) 2012 Linaro Limited + * + * Dummy interface to user space for debugging purpose only. + * + * 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 +#include +#include +#include +#include +#include + +static ssize_t bL_switcher_write(struct file *file, const char __user *buf, + size_t len, loff_t *pos) +{ + unsigned char val[3]; + unsigned int cpu, cluster; + int ret; + + pr_debug("%s\n", __func__); + + if (len < 3) + return -EINVAL; + + if (copy_from_user(val, buf, 3)) + return -EFAULT; + + /* format: , */ + if (val[0] < '0' || val[0] > '4' || + val[1] != ',' || + val[2] < '0' || val[2] > '1') + return -EINVAL; + + cpu = val[0] - '0'; + cluster = val[2] - '0'; + ret = bL_switch_request(cpu, cluster); + + return ret ? : len; +} + +static const struct file_operations bL_switcher_fops = { + .write = bL_switcher_write, + .owner = THIS_MODULE, +}; + +static struct miscdevice bL_switcher_device = { + MISC_DYNAMIC_MINOR, + "b.L_switcher", + &bL_switcher_fops +}; + +static int __init bL_switcher_dummy_if_init(void) +{ + return misc_register(&bL_switcher_device); +} + +static void __exit bL_switcher_dummy_if_exit(void) +{ + misc_deregister(&bL_switcher_device); +} + +module_init(bL_switcher_dummy_if_init); +module_exit(bL_switcher_dummy_if_exit); diff -Nur linux-3.10.30/arch/arm/common/mcpm_entry.c linux-3.10.30-cubox-i/arch/arm/common/mcpm_entry.c --- linux-3.10.30/arch/arm/common/mcpm_entry.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/common/mcpm_entry.c 2014-03-08 20:32:53.000000000 +0100 @@ -27,6 +27,18 @@ sync_cache_w(&mcpm_entry_vectors[cluster][cpu]); } +extern unsigned long mcpm_entry_early_pokes[MAX_NR_CLUSTERS][MAX_CPUS_PER_CLUSTER][2]; + +void mcpm_set_early_poke(unsigned cpu, unsigned cluster, + unsigned long poke_phys_addr, unsigned long poke_val) +{ + unsigned long *poke = &mcpm_entry_early_pokes[cluster][cpu][0]; + poke[0] = poke_phys_addr; + poke[1] = poke_val; + __cpuc_flush_dcache_area((void *)poke, 8); + outer_clean_range(__pa(poke), __pa(poke + 2)); +} + static const struct mcpm_platform_ops *platform_ops; int __init mcpm_platform_register(const struct mcpm_platform_ops *ops) diff -Nur linux-3.10.30/arch/arm/common/mcpm_head.S linux-3.10.30-cubox-i/arch/arm/common/mcpm_head.S --- linux-3.10.30/arch/arm/common/mcpm_head.S 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/common/mcpm_head.S 2014-03-08 20:32:53.000000000 +0100 @@ -71,12 +71,19 @@ * position independent way. */ adr r5, 3f - ldmia r5, {r6, r7, r8, r11} + ldmia r5, {r0, r6, r7, r8, r11} + add r0, r5, r0 @ r0 = mcpm_entry_early_pokes add r6, r5, r6 @ r6 = mcpm_entry_vectors ldr r7, [r5, r7] @ r7 = mcpm_power_up_setup_phys add r8, r5, r8 @ r8 = mcpm_sync add r11, r5, r11 @ r11 = first_man_locks + @ Perform an early poke, if any + add r0, r0, r4, lsl #3 + ldmia r0, {r0, r1} + teq r0, #0 + strne r1, [r0] + mov r0, #MCPM_SYNC_CLUSTER_SIZE mla r8, r0, r10, r8 @ r8 = sync cluster base @@ -195,7 +202,8 @@ .align 2 -3: .word mcpm_entry_vectors - . +3: .word mcpm_entry_early_pokes - . + .word mcpm_entry_vectors - 3b .word mcpm_power_up_setup_phys - 3b .word mcpm_sync - 3b .word first_man_locks - 3b @@ -214,6 +222,10 @@ ENTRY(mcpm_entry_vectors) .space 4 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER + .type mcpm_entry_early_pokes, #object +ENTRY(mcpm_entry_early_pokes) + .space 8 * MAX_NR_CLUSTERS * MAX_CPUS_PER_CLUSTER + .type mcpm_power_up_setup_phys, #object ENTRY(mcpm_power_up_setup_phys) .space 4 @ set by mcpm_sync_init() diff -Nur linux-3.10.30/arch/arm/configs/imx_v6_v7_defconfig linux-3.10.30-cubox-i/arch/arm/configs/imx_v6_v7_defconfig --- linux-3.10.30/arch/arm/configs/imx_v6_v7_defconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/configs/imx_v6_v7_defconfig 2014-03-08 20:32:53.000000000 +0100 @@ -1,4 +1,3 @@ -CONFIG_EXPERIMENTAL=y # CONFIG_LOCALVERSION_AUTO is not set CONFIG_KERNEL_LZO=y CONFIG_SYSVIPC=y @@ -17,10 +16,9 @@ CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y # CONFIG_BLK_DEV_BSG is not set -CONFIG_ARCH_MXC=y CONFIG_ARCH_MULTI_V6=y -CONFIG_ARCH_MULTI_V7=y -CONFIG_MACH_IMX31_DT=y +CONFIG_GPIO_PCA953X=y +CONFIG_ARCH_MXC=y CONFIG_MACH_MX31LILLY=y CONFIG_MACH_MX31LITE=y CONFIG_MACH_PCM037=y @@ -30,6 +28,7 @@ CONFIG_MACH_QONG=y CONFIG_MACH_ARMADILLO5X0=y CONFIG_MACH_KZM_ARM11_01=y +CONFIG_MACH_IMX31_DT=y CONFIG_MACH_PCM043=y CONFIG_MACH_MX35_3DS=y CONFIG_MACH_VPR200=y @@ -37,16 +36,26 @@ CONFIG_MACH_EUKREA_CPUIMX51SD=y CONFIG_SOC_IMX53=y CONFIG_SOC_IMX6Q=y -CONFIG_MXC_PWM=y +CONFIG_SOC_IMX6SL=y +CONFIG_SOC_VF610=y CONFIG_SMP=y CONFIG_VMSPLIT_2G=y CONFIG_PREEMPT_VOLUNTARY=y CONFIG_AEABI=y # CONFIG_OABI_COMPAT is not set CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_ARM_IMX6_CPUFREQ=y +CONFIG_CPU_IDLE=y CONFIG_VFP=y CONFIG_NEON=y CONFIG_BINFMT_MISC=m +CONFIG_PM_RUNTIME=y CONFIG_PM_DEBUG=y CONFIG_PM_TEST_SUSPEND=y CONFIG_NET=y @@ -65,16 +74,19 @@ CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y # CONFIG_STANDALONE is not set +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=256 +CONFIG_IMX_WEIM=y CONFIG_CONNECTOR=y CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_JEDECPROBE=y CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP_OF=y CONFIG_MTD_DATAFLASH=y CONFIG_MTD_M25P80=y CONFIG_MTD_SST25L=y @@ -95,10 +107,11 @@ CONFIG_SCSI_SCAN_ASYNC=y # CONFIG_SCSI_LOWLEVEL is not set CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_IMX=y CONFIG_PATA_IMX=y CONFIG_NETDEVICES=y # CONFIG_NET_VENDOR_BROADCOM is not set -# CONFIG_NET_VENDOR_CHELSIO is not set CONFIG_CS89x0=y CONFIG_CS89x0_PLATFORM=y # CONFIG_NET_VENDOR_FARADAY is not set @@ -112,6 +125,7 @@ CONFIG_SMC911X=y CONFIG_SMSC911X=y # CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_AT803X_PHY=y # CONFIG_WLAN is not set # CONFIG_INPUT_MOUSEDEV_PSAUX is not set CONFIG_INPUT_EVDEV=y @@ -121,22 +135,26 @@ CONFIG_MOUSE_PS2=m CONFIG_MOUSE_PS2_ELANTECH=y CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_EGALAX=y +CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH=y +CONFIG_TOUCHSCREEN_MAX11801=y CONFIG_TOUCHSCREEN_MC13783=y CONFIG_INPUT_MISC=y CONFIG_INPUT_MMA8450=y +CONFIG_INPUT_ISL29023=y CONFIG_SERIO_SERPORT=m CONFIG_VT_HW_CONSOLE_BINDING=y # CONFIG_LEGACY_PTYS is not set # CONFIG_DEVKMEM is not set CONFIG_SERIAL_IMX=y CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_MXC_RNGA=y -CONFIG_I2C=y # CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y # CONFIG_I2C_HELPER_AUTO is not set -CONFIG_I2C_ALGOBIT=m CONFIG_I2C_ALGOPCF=m CONFIG_I2C_ALGOPCA=m CONFIG_I2C_IMX=y @@ -144,7 +162,13 @@ CONFIG_SPI_IMX=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_MC9S08DZ60=y -# CONFIG_HWMON is not set +CONFIG_POWER_SUPPLY=y +CONFIG_SABRESD_MAX8903=y +CONFIG_SENSORS_MAG3110=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_DEVICE_THERMAL=y CONFIG_WATCHDOG=y CONFIG_IMX2_WDT=y CONFIG_MFD_DA9052_I2C=y @@ -156,20 +180,35 @@ CONFIG_REGULATOR_ANATOP=y CONFIG_REGULATOR_MC13783=y CONFIG_REGULATOR_MC13892=y +CONFIG_REGULATOR_PFUZE100=y CONFIG_MEDIA_SUPPORT=y -CONFIG_VIDEO_DEV=y -CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_MEDIA_RC_SUPPORT=y +CONFIG_RC_DEVICES=y +CONFIG_IR_GPIO_CIR=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MXC_OUTPUT=y +CONFIG_VIDEO_MXC_IPU_OUTPUT=y CONFIG_SOC_CAMERA=y +CONFIG_VIDEO_MX3=y CONFIG_SOC_CAMERA_OV2640=y CONFIG_DRM=y -CONFIG_VIDEO_MX3=y +CONFIG_DRM_VIVANTE=y CONFIG_FB=y -CONFIG_LCD_PLATFORM=y +CONFIG_FB_MXS=y CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_LCD_L4F00242T03=y +CONFIG_LCD_PLATFORM=y CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FB_MXC_SYNC_PANEL=y +CONFIG_FB_MXC_LDB=y +CONFIG_FB_MXC_MIPI_DSI=y +CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y +CONFIG_FB_MXC_HDMI=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y CONFIG_FONTS=y @@ -178,24 +217,36 @@ CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y +CONFIG_SND_USB_AUDIO=m CONFIG_SND_SOC=y CONFIG_SND_IMX_SOC=y CONFIG_SND_SOC_PHYCORE_AC97=y CONFIG_SND_SOC_EUKREA_TLV320=y +CONFIG_SND_SOC_IMX_CS42888=y +CONFIG_SND_SOC_IMX_WM8962=y CONFIG_SND_SOC_IMX_SGTL5000=y CONFIG_SND_SOC_IMX_MC13783=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_MXC=y +CONFIG_USB_STORAGE=y CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y CONFIG_USB_CHIPIDEA_HOST=y CONFIG_USB_PHY=y CONFIG_USB_MXS_PHY=y -CONFIG_USB_STORAGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MXC_IPU=y +CONFIG_MXC_GPU_VIV=y +CONFIG_MXC_ASRC=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_RTC_CLASS=y @@ -204,14 +255,14 @@ CONFIG_RTC_DRV_MXC=y CONFIG_RTC_DRV_SNVS=y CONFIG_DMADEVICES=y +CONFIG_MXC_PXP_V2=y CONFIG_IMX_SDMA=y CONFIG_MXS_DMA=y CONFIG_STAGING=y -CONFIG_DRM_IMX=y -CONFIG_DRM_IMX_IPUV3_CORE=y -CONFIG_DRM_IMX_IPUV3=y CONFIG_COMMON_CLK_DEBUG=y # CONFIG_IOMMU_SUPPORT is not set +CONFIG_PWM=y +CONFIG_PWM_IMX=y CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_POSIX_ACL=y @@ -234,7 +285,6 @@ CONFIG_MSDOS_FS=m CONFIG_VFAT_FS=y CONFIG_TMPFS=y -CONFIG_CONFIGFS_FS=m CONFIG_JFFS2_FS=y CONFIG_UBIFS_FS=y CONFIG_NFS_FS=y diff -Nur linux-3.10.30/arch/arm/configs/imx_v7_cubox-i_hummingboard_defconfig linux-3.10.30-cubox-i/arch/arm/configs/imx_v7_cubox-i_hummingboard_defconfig --- linux-3.10.30/arch/arm/configs/imx_v7_cubox-i_hummingboard_defconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/configs/imx_v7_cubox-i_hummingboard_defconfig 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,384 @@ +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_GPIO_PCA953X=y +CONFIG_ARCH_MXC=y +CONFIG_MXC_DEBUG_BOARD=y +CONFIG_HAVE_IMX_ANATOP=y +CONFIG_HAVE_IMX_GPC=y +CONFIG_HAVE_IMX_MMDC=y +CONFIG_HAVE_IMX_SRC=y +CONFIG_ARCH_MXC_IOMUX_V3=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +CONFIG_IMX_HAVE_PLATFORM_FEC=y +CONFIG_IMX_HAVE_PLATFORM_FSL_USB2_UDC=y +CONFIG_IMX_HAVE_PLATFORM_GPIO_KEYS=y +CONFIG_IMX_HAVE_PLATFORM_IMX2_WDT=y +CONFIG_IMX_HAVE_PLATFORM_IMX_I2C=y +CONFIG_IMX_HAVE_PLATFORM_IMX_SSI=y +CONFIG_IMX_HAVE_PLATFORM_IMX_UART=y +CONFIG_IMX_HAVE_PLATFORM_MXC_EHCI=y +CONFIG_IMX_HAVE_PLATFORM_MXC_NAND=y +CONFIG_IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX=y +CONFIG_IMX_HAVE_PLATFORM_SPI_IMX=y +CONFIG_PCI=y +CONFIG_PCI_IMX6=y +# CONFIG_SWP_EMULATE is not set +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_ARM_IMX6_CPUFREQ=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_BINFMT_MISC=m +CONFIG_PM_RUNTIME=y +CONFIG_PM_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_VLAN_8021Q=y +CONFIG_CAN=y +CONFIG_CAN_FLEXCAN=y +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIBTUSB=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIVHCI=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +CONFIG_CFG80211_DEFAULT_PS=y +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +CONFIG_LIB80211_CRYPT_WEP=y +CONFIG_LIB80211_CRYPT_CCMP=y +CONFIG_LIB80211_CRYPT_TKIP=y +CONFIG_RFKILL=y +CONFIG_RFKILL_LEDS=y +CONFIG_RFKILL_INPUT=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=256 +CONFIG_IMX_WEIM=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_GPMI_NAND=y +CONFIG_MTD_NAND_MXC=y +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_IMX=y +CONFIG_PATA_IMX=y +CONFIG_NETDEVICES=y +CONFIG_NET_VENDOR_FREESCALE=y +CONFIG_FEC=y +CONFIG_PHYLIB=y +CONFIG_AT803X_PHY=y +# CONFIG_NET_VENDOR_BROADCOM is not set +CONFIG_CS89x0=y +CONFIG_CS89x0_PLATFORM=y +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_SEEQ is not set +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_ATH_CARDS=y +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_BRCMUTIL=y +# CONFIG_BRCMSMAC is not set +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_SDIO=y +CONFIG_HOSTAP=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_IMX=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MMA8450=y +CONFIG_INPUT_ISL29023=y +CONFIG_SERIO_SERPORT=m +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_IMX=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_MXC_MMA8451=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_DEVICE_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_MC13XXX_SPI=y +CONFIG_MFD_MC13XXX_I2C=y +CONFIG_MFD_SI476X_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_DA9052=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_MC13783=y +CONFIG_REGULATOR_MC13892=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_RC_SUPPORT=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_MEDIA=y +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEOBUF2_MEMOPS=y +CONFIG_VIDEOBUF2_DMA_CONTIG=y +CONFIG_VIDEOBUF2_VMALLOC=m +CONFIG_VIDEO_V4L2_INT_DEVICE=y +CONFIG_RC_CORE=y +CONFIG_RC_MAP=y +CONFIG_RC_DECODERS=y +CONFIG_LIRC=y +CONFIG_IR_LIRC_CODEC=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_SANYO_DECODER=y +CONFIG_IR_MCE_KBD_DECODER=y +CONFIG_RC_DEVICES=y +CONFIG_IR_GPIO_CIR=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MXC_OUTPUT=y +CONFIG_VIDEO_MXC_CAPTURE=m +CONFIG_VIDEO_MXC_CSI_CAMERA=m +CONFIG_MXC_CAMERA_OV5640=m +CONFIG_MXC_CAMERA_OV5642=m +CONFIG_MXC_CAMERA_OV5640_MIPI=m +CONFIG_MXC_TVIN_ADV7180=m +CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m +CONFIG_VIDEO_MXC_IPU_OUTPUT=y +CONFIG_VIDEO_MXC_PXP_V4L2=y +CONFIG_SOC_CAMERA=y +CONFIG_VIDEO_MX3=y +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y +CONFIG_VIDEO_IR_I2C=y +CONFIG_DRM=y +CONFIG_DRM_VIVANTE=y +CONFIG_FB=y +CONFIG_FB_MXS=y +CONFIG_FB_MXC_SYNC_PANEL=y +CONFIG_FB_MXC_HDMI=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_IMX_SOC=y +CONFIG_SND_SOC_IMX_SPDIF=y +CONFIG_SND_SOC_IMX_HDMI=y +CONFIG_SND_SOC_FSL_SPDIF=y +CONFIG_SND_SOC_FSL_HDMI=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_PHY=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_GADGET=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MXC_IPU=y +CONFIG_MXC_GPU_VIV=y +CONFIG_MXC_ASRC=y +CONFIG_MXC_HDMI_CEC=y +CONFIG_MXC_VPU=y +CONFIG_MXC_MIPI_CSI2=y +CONFIG_MXC_MLB150=m +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_REGULATOR=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_INTF_DEV_UIE_EMUL=y +CONFIG_RTC_DRV_MC13XXX=y +CONFIG_RTC_DRV_MXC=y +CONFIG_RTC_DRV_SNVS=y +CONFIG_RTC_DRV_PCF8523=y +CONFIG_DMADEVICES=y +CONFIG_MXC_PXP_V2=y +CONFIG_IMX_SDMA=y +CONFIG_MXS_DMA=y +CONFIG_STAGING=y +CONFIG_STAGING_MEDIA=y +CONFIG_LIRC_STAGING=y +CONFIG_LIRC_SERIAL=y +CONFIG_LIRC_SERIAL_TRANSMITTER=y +CONFIG_COMMON_CLK_DEBUG=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_PWM=y +CONFIG_PWM_IMX=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_UTF8=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_FTRACE is not set +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_FSL_CAAM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m diff -Nur linux-3.10.30/arch/arm/configs/imx_v7_defconfig linux-3.10.30-cubox-i/arch/arm/configs/imx_v7_defconfig --- linux-3.10.30/arch/arm/configs/imx_v7_defconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/configs/imx_v7_defconfig 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,332 @@ +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_GPIO_PCA953X=y +CONFIG_ARCH_MXC=y +CONFIG_MXC_DEBUG_BOARD=y +CONFIG_MACH_IMX51_DT=y +CONFIG_MACH_EUKREA_CPUIMX51SD=y +CONFIG_SOC_IMX53=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +CONFIG_SOC_VF610=y +# CONFIG_SWP_EMULATE is not set +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_ARM_IMX6_CPUFREQ=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_BINFMT_MISC=m +CONFIG_PM_RUNTIME=y +CONFIG_PM_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_VLAN_8021Q=y +CONFIG_CAN=y +CONFIG_CAN_FLEXCAN=y +CONFIG_CFG80211=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=256 +CONFIG_IMX_WEIM=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_GPMI_NAND=y +CONFIG_MTD_NAND_MXC=y +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_IMX=y +CONFIG_PATA_IMX=y +CONFIG_NETDEVICES=y +# CONFIG_NET_VENDOR_BROADCOM is not set +CONFIG_CS89x0=y +CONFIG_CS89x0_PLATFORM=y +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_SEEQ is not set +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_ATH_CARDS=y +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_IMX=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_EGALAX=y +CONFIG_TOUCHSCREEN_ELAN=y +CONFIG_TOUCHSCREEN_MAX11801=y +CONFIG_TOUCHSCREEN_MC13783=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MMA8450=y +CONFIG_INPUT_ISL29023=y +CONFIG_SERIO_SERPORT=m +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_FSL_OTP=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_IMX=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_SABRESD_MAX8903=y +CONFIG_IMX6_USB_CHARGER=y +CONFIG_SENSORS_MAX17135=y +CONFIG_SENSORS_MAG3110=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_DEVICE_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_MC13XXX_SPI=y +CONFIG_MFD_MC13XXX_I2C=y +CONFIG_MFD_MAX17135=y +CONFIG_MFD_SI476X_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_DA9052=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_MC13783=y +CONFIG_REGULATOR_MC13892=y +CONFIG_REGULATOR_MAX17135=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_VIDEO_V4L2_INT_DEVICE=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MXC_OUTPUT=y +CONFIG_VIDEO_MXC_CAPTURE=m +CONFIG_VIDEO_MXC_CSI_CAMERA=m +CONFIG_MXC_CAMERA_OV5640=m +CONFIG_MXC_CAMERA_OV5642=m +CONFIG_MXC_CAMERA_OV5640_MIPI=m +CONFIG_MXC_TVIN_ADV7180=m +CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m +CONFIG_VIDEO_MXC_IPU_OUTPUT=y +CONFIG_VIDEO_MXC_PXP_V4L2=y +CONFIG_SOC_CAMERA=y +CONFIG_VIDEO_MX3=y +CONFIG_RADIO_SI476X=y +CONFIG_SOC_CAMERA_OV2640=y +CONFIG_DRM=y +CONFIG_DRM_VIVANTE=y +CONFIG_FB=y +CONFIG_FB_MXS=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_L4F00242T03=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FB_MXC_SYNC_PANEL=y +CONFIG_FB_MXC_LDB=y +CONFIG_FB_MXC_MIPI_DSI=y +CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y +CONFIG_FB_MXC_HDMI=y +CONFIG_FB_MXC_EINK_PANEL=y +CONFIG_FB_MXS_SII902X=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_IMX_SOC=y +CONFIG_SND_SOC_EUKREA_TLV320=y +CONFIG_SND_SOC_IMX_CS42888=y +CONFIG_SND_SOC_IMX_WM8962=y +CONFIG_SND_SOC_IMX_SGTL5000=y +CONFIG_SND_SOC_IMX_SPDIF=y +CONFIG_SND_SOC_IMX_MC13783=y +CONFIG_SND_SOC_IMX_HDMI=y +CONFIG_SND_SOC_IMX_SI476X=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_PHY=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_GADGET=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MXC_IPU=y +CONFIG_MXC_GPU_VIV=y +CONFIG_MXC_ASRC=y +CONFIG_MXC_MIPI_CSI2=y +CONFIG_MXC_MLB150=m +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_INTF_DEV_UIE_EMUL=y +CONFIG_RTC_DRV_MC13XXX=y +CONFIG_RTC_DRV_MXC=y +CONFIG_RTC_DRV_SNVS=y +CONFIG_DMADEVICES=y +CONFIG_MXC_PXP_V2=y +CONFIG_IMX_SDMA=y +CONFIG_MXS_DMA=y +CONFIG_STAGING=y +CONFIG_COMMON_CLK_DEBUG=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_PWM=y +CONFIG_PWM_IMX=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_UTF8=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_FTRACE is not set +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_FSL_CAAM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m diff -Nur linux-3.10.30/arch/arm/configs/imx_v7_mfg_defconfig linux-3.10.30-cubox-i/arch/arm/configs/imx_v7_mfg_defconfig --- linux-3.10.30/arch/arm/configs/imx_v7_mfg_defconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/configs/imx_v7_mfg_defconfig 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,341 @@ +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_GPIO_PCA953X=y +CONFIG_ARCH_MXC=y +CONFIG_MXC_DEBUG_BOARD=y +CONFIG_MACH_IMX51_DT=y +CONFIG_MACH_EUKREA_CPUIMX51SD=y +CONFIG_SOC_IMX53=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +CONFIG_SOC_VF610=y +# CONFIG_SWP_EMULATE is not set +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_ARM_IMX6_CPUFREQ=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_BINFMT_MISC=m +CONFIG_PM_RUNTIME=y +CONFIG_PM_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_VLAN_8021Q=y +CONFIG_CFG80211=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=256 +CONFIG_IMX_WEIM=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_GPMI_NAND=y +CONFIG_MTD_NAND_MXC=y +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_IMX=y +CONFIG_PATA_IMX=y +CONFIG_NETDEVICES=y +# CONFIG_NET_VENDOR_BROADCOM is not set +CONFIG_CS89x0=y +CONFIG_CS89x0_PLATFORM=y +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_SEEQ is not set +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_ATH_CARDS=y +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_IMX=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_EGALAX=y +CONFIG_TOUCHSCREEN_ELAN=y +CONFIG_TOUCHSCREEN_MAX11801=y +CONFIG_TOUCHSCREEN_MC13783=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MMA8450=y +CONFIG_INPUT_ISL29023=y +CONFIG_SERIO_SERPORT=m +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_FSL_OTP=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_IMX=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_SABRESD_MAX8903=y +CONFIG_SENSORS_MAX17135=y +CONFIG_SENSORS_MAG3110=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_DEVICE_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_MC13XXX_SPI=y +CONFIG_MFD_MC13XXX_I2C=y +CONFIG_MFD_MAX17135=y +CONFIG_MFD_SI476X_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_DA9052=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_MC13783=y +CONFIG_REGULATOR_MC13892=y +CONFIG_REGULATOR_MAX17135=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_VIDEO_V4L2_INT_DEVICE=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MXC_OUTPUT=y +CONFIG_VIDEO_MXC_CAPTURE=m +CONFIG_VIDEO_MXC_CSI_CAMERA=m +CONFIG_MXC_CAMERA_OV5640=m +CONFIG_MXC_CAMERA_OV5642=m +CONFIG_MXC_CAMERA_OV5640_MIPI=m +CONFIG_MXC_TVIN_ADV7180=m +CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m +CONFIG_VIDEO_MXC_IPU_OUTPUT=y +CONFIG_VIDEO_MXC_PXP_V4L2=y +CONFIG_SOC_CAMERA=y +CONFIG_VIDEO_MX3=y +CONFIG_RADIO_SI476X=y +CONFIG_SOC_CAMERA_OV2640=y +CONFIG_DRM=y +CONFIG_DRM_VIVANTE=y +CONFIG_FB=y +CONFIG_FB_MXS=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_L4F00242T03=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FB_MXC_SYNC_PANEL=y +CONFIG_FB_MXC_LDB=y +CONFIG_FB_MXC_MIPI_DSI=y +CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y +CONFIG_FB_MXC_HDMI=y +CONFIG_FB_MXC_EINK_PANEL=y +CONFIG_FB_MXS_SII902X=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_IMX_SOC=y +CONFIG_SND_SOC_EUKREA_TLV320=y +CONFIG_SND_SOC_IMX_CS42888=y +CONFIG_SND_SOC_IMX_WM8962=y +CONFIG_SND_SOC_IMX_SGTL5000=y +CONFIG_SND_SOC_IMX_SPDIF=y +CONFIG_SND_SOC_IMX_MC13783=y +CONFIG_SND_SOC_IMX_HDMI=y +CONFIG_SND_SOC_IMX_SI476X=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_PHY=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_GADGET=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_ETH 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=y +CONFIG_FSL_UTP=y +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER 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_USB_G_WEBCAM is not set +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MXC_IPU=y +CONFIG_MXC_GPU_VIV=y +CONFIG_MXC_ASRC=y +CONFIG_MXC_MIPI_CSI2=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_INTF_DEV_UIE_EMUL=y +CONFIG_RTC_DRV_MC13XXX=y +CONFIG_RTC_DRV_MXC=y +CONFIG_RTC_DRV_SNVS=y +CONFIG_DMADEVICES=y +CONFIG_MXC_PXP_V2=y +CONFIG_IMX_SDMA=y +CONFIG_MXS_DMA=y +CONFIG_STAGING=y +CONFIG_COMMON_CLK_DEBUG=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_PWM=y +CONFIG_PWM_IMX=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_UTF8=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_FTRACE is not set +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_FSL_CAAM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m diff -Nur linux-3.10.30/arch/arm/include/asm/arch_timer.h linux-3.10.30-cubox-i/arch/arm/include/asm/arch_timer.h --- linux-3.10.30/arch/arm/include/asm/arch_timer.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/arch_timer.h 2014-03-08 20:32:53.000000000 +0100 @@ -96,7 +96,7 @@ asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl)); /* disable user access to everything */ - cntkctl &= ~((3 << 8) | (7 << 0)); + cntkctl &= ~((3 << 8) | (3 << 0)); asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); } diff -Nur linux-3.10.30/arch/arm/include/asm/bL_switcher.h linux-3.10.30-cubox-i/arch/arm/include/asm/bL_switcher.h --- linux-3.10.30/arch/arm/include/asm/bL_switcher.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/bL_switcher.h 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,83 @@ +/* + * arch/arm/include/asm/bL_switcher.h + * + * Created by: Nicolas Pitre, April 2012 + * Copyright: (C) 2012 Linaro Limited + * + * 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 ASM_BL_SWITCHER_H +#define ASM_BL_SWITCHER_H + +#include +#include + +typedef void (*bL_switch_completion_handler)(void *cookie); + +int bL_switch_request_cb(unsigned int cpu, unsigned int new_cluster_id, + bL_switch_completion_handler completer, + void *completer_cookie); +static inline int bL_switch_request(unsigned int cpu, unsigned int new_cluster_id) +{ + return bL_switch_request_cb(cpu, new_cluster_id, NULL, NULL); +} + +/* + * Register here to be notified about runtime enabling/disabling of + * the switcher. + * + * The notifier chain is called with the switcher activation lock held: + * the switcher will not be enabled or disabled during callbacks. + * Callbacks must not call bL_switcher_{get,put}_enabled(). + */ +#define BL_NOTIFY_PRE_ENABLE 0 +#define BL_NOTIFY_POST_ENABLE 1 +#define BL_NOTIFY_PRE_DISABLE 2 +#define BL_NOTIFY_POST_DISABLE 3 + +#ifdef CONFIG_BL_SWITCHER + +void bL_switch_request_detach(unsigned int cpu, + bL_switch_completion_handler completer); + +int bL_switcher_register_notifier(struct notifier_block *nb); +int bL_switcher_unregister_notifier(struct notifier_block *nb); + +/* + * Use these functions to temporarily prevent enabling/disabling of + * the switcher. + * bL_switcher_get_enabled() returns true if the switcher is currently + * enabled. Each call to bL_switcher_get_enabled() must be followed + * by a call to bL_switcher_put_enabled(). These functions are not + * recursive. + */ +bool bL_switcher_get_enabled(void); +void bL_switcher_put_enabled(void); + +int bL_switcher_trace_trigger(void); +int bL_switcher_get_logical_index(u32 mpidr); + +#else +static void bL_switch_request_detach(unsigned int cpu, + bL_switch_completion_handler completer) { } + +static inline int bL_switcher_register_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline int bL_switcher_unregister_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline bool bL_switcher_get_enabled(void) { return false; } +static inline void bL_switcher_put_enabled(void) { } +static inline int bL_switcher_trace_trigger(void) { return 0; } +static inline int bL_switcher_get_logical_index(u32 mpidr) { return -EUNATCH; } +#endif /* CONFIG_BL_SWITCHER */ + +#endif diff -Nur linux-3.10.30/arch/arm/include/asm/cacheflush.h linux-3.10.30-cubox-i/arch/arm/include/asm/cacheflush.h --- linux-3.10.30/arch/arm/include/asm/cacheflush.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/cacheflush.h 2014-03-08 20:32:53.000000000 +0100 @@ -436,4 +436,50 @@ #define sync_cache_w(ptr) __sync_cache_range_w(ptr, sizeof *(ptr)) #define sync_cache_r(ptr) __sync_cache_range_r(ptr, sizeof *(ptr)) +/* + * Disabling cache access for one CPU in an ARMv7 SMP system is tricky. + * To do so we must: + * + * - Clear the SCTLR.C bit to prevent further cache allocations + * - Flush the desired level of cache + * - Clear the ACTLR "SMP" bit to disable local coherency + * + * ... and so without any intervening memory access in between those steps, + * not even to the stack. + * + * WARNING -- After this has been called: + * + * - No ldrex/strex (and similar) instructions must be used. + * - The CPU is obviously no longer coherent with the other CPUs. + * - This is unlikely to work as expected if Linux is running non-secure. + * + * Note: + * + * - This is known to apply to several ARMv7 processor implementations, + * however some exceptions may exist. Caveat emptor. + * + * - The clobber list is dictated by the call to v7_flush_dcache_*. + * fp is preserved to the stack explicitly prior disabling the cache + * since adding it to the clobber list is incompatible with having + * CONFIG_FRAME_POINTER=y. ip is saved as well if ever r12-clobbering + * trampoline are inserted by the linker and to keep sp 64-bit aligned. + */ +#define v7_exit_coherency_flush(level) \ + asm volatile( \ + "stmfd sp!, {fp, ip} \n\t" \ + "mrc p15, 0, r0, c1, c0, 0 @ get SCTLR \n\t" \ + "bic r0, r0, #"__stringify(CR_C)" \n\t" \ + "mcr p15, 0, r0, c1, c0, 0 @ set SCTLR \n\t" \ + "isb \n\t" \ + "bl v7_flush_dcache_"__stringify(level)" \n\t" \ + "clrex \n\t" \ + "mrc p15, 0, r0, c1, c0, 1 @ get ACTLR \n\t" \ + "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" \ + "mcr p15, 0, r0, c1, c0, 1 @ set ACTLR \n\t" \ + "isb \n\t" \ + "dsb \n\t" \ + "ldmfd sp!, {fp, ip}" \ + : : : "r0","r1","r2","r3","r4","r5","r6","r7", \ + "r9","r10","lr","memory" ) + #endif diff -Nur linux-3.10.30/arch/arm/include/asm/cp15.h linux-3.10.30-cubox-i/arch/arm/include/asm/cp15.h --- linux-3.10.30/arch/arm/include/asm/cp15.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/cp15.h 2014-03-08 20:32:53.000000000 +0100 @@ -61,6 +61,20 @@ isb(); } +static inline unsigned int get_auxcr(void) +{ + unsigned int val; + asm("mrc p15, 0, %0, c1, c0, 1 @ get AUXCR" : "=r" (val)); + return val; +} + +static inline void set_auxcr(unsigned int val) +{ + asm volatile("mcr p15, 0, %0, c1, c0, 1 @ set AUXCR" + : : "r" (val)); + isb(); +} + #ifndef CONFIG_SMP extern void adjust_cr(unsigned long mask, unsigned long set); #endif diff -Nur linux-3.10.30/arch/arm/include/asm/glue-cache.h linux-3.10.30-cubox-i/arch/arm/include/asm/glue-cache.h --- linux-3.10.30/arch/arm/include/asm/glue-cache.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/glue-cache.h 2014-03-08 20:32:53.000000000 +0100 @@ -102,19 +102,19 @@ #endif #if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V6K) -# ifdef _CACHE +//# ifdef _CACHE # define MULTI_CACHE 1 -# else -# define _CACHE v6 -# endif +//# else +//# define _CACHE v6 +//# endif #endif #if defined(CONFIG_CPU_V7) -# ifdef _CACHE +//# ifdef _CACHE # define MULTI_CACHE 1 -# else -# define _CACHE v7 -# endif +//# else +//# define _CACHE v7 +//# endif #endif #if !defined(_CACHE) && !defined(MULTI_CACHE) diff -Nur linux-3.10.30/arch/arm/include/asm/hardirq.h linux-3.10.30-cubox-i/arch/arm/include/asm/hardirq.h --- linux-3.10.30/arch/arm/include/asm/hardirq.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/hardirq.h 2014-03-08 20:32:53.000000000 +0100 @@ -5,7 +5,7 @@ #include #include -#define NR_IPI 6 +#define NR_IPI 7 typedef struct { unsigned int __softirq_pending; diff -Nur linux-3.10.30/arch/arm/include/asm/mach/arch.h linux-3.10.30-cubox-i/arch/arm/include/asm/mach/arch.h --- linux-3.10.30/arch/arm/include/asm/mach/arch.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/mach/arch.h 2014-03-08 20:32:53.000000000 +0100 @@ -8,6 +8,8 @@ * published by the Free Software Foundation. */ +#include + #ifndef __ASSEMBLY__ struct tag; @@ -16,8 +18,10 @@ struct smp_operations; #ifdef CONFIG_SMP #define smp_ops(ops) (&(ops)) +#define smp_init_ops(ops) (&(ops)) #else #define smp_ops(ops) (struct smp_operations *)NULL +#define smp_init_ops(ops) (bool (*)(void))NULL #endif struct machine_desc { @@ -41,6 +45,7 @@ unsigned char reserve_lp2 :1; /* never has lp2 */ char restart_mode; /* default restart mode */ struct smp_operations *smp; /* SMP operations */ + bool (*smp_init)(void); void (*fixup)(struct tag *, char **, struct meminfo *); void (*reserve)(void);/* reserve mem blocks */ diff -Nur linux-3.10.30/arch/arm/include/asm/mach/pci.h linux-3.10.30-cubox-i/arch/arm/include/asm/mach/pci.h --- linux-3.10.30/arch/arm/include/asm/mach/pci.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/mach/pci.h 2014-03-08 20:32:53.000000000 +0100 @@ -35,6 +35,8 @@ resource_size_t start, resource_size_t size, resource_size_t align); + void (*add_bus)(struct pci_bus *bus); + void (*remove_bus)(struct pci_bus *bus); }; /* @@ -62,6 +64,8 @@ resource_size_t start, resource_size_t size, resource_size_t align); + void (*add_bus)(struct pci_bus *bus); + void (*remove_bus)(struct pci_bus *bus); void *private_data; /* platform controller private data */ }; @@ -96,9 +100,4 @@ extern int via82c505_setup(int nr, struct pci_sys_data *); extern void via82c505_init(void *sysdata); -extern struct pci_ops pci_v3_ops; -extern int pci_v3_setup(int nr, struct pci_sys_data *); -extern void pci_v3_preinit(void); -extern void pci_v3_postinit(void); - #endif /* __ASM_MACH_PCI_H */ diff -Nur linux-3.10.30/arch/arm/include/asm/mcpm.h linux-3.10.30-cubox-i/arch/arm/include/asm/mcpm.h --- linux-3.10.30/arch/arm/include/asm/mcpm.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/mcpm.h 2014-03-08 20:32:53.000000000 +0100 @@ -42,6 +42,14 @@ void mcpm_set_entry_vector(unsigned cpu, unsigned cluster, void *ptr); /* + * This sets an early poke i.e a value to be poked into some address + * from very early assembly code before the CPU is ungated. The + * address must be physical, and if 0 then nothing will happen. + */ +void mcpm_set_early_poke(unsigned cpu, unsigned cluster, + unsigned long poke_phys_addr, unsigned long poke_val); + +/* * CPU/cluster power operations API for higher subsystems to use. */ diff -Nur linux-3.10.30/arch/arm/include/asm/pmu.h linux-3.10.30-cubox-i/arch/arm/include/asm/pmu.h --- linux-3.10.30/arch/arm/include/asm/pmu.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/pmu.h 2014-03-08 20:32:53.000000000 +0100 @@ -62,9 +62,19 @@ raw_spinlock_t pmu_lock; }; +struct cpupmu_regs { + u32 pmc; + u32 pmcntenset; + u32 pmuseren; + u32 pmintenset; + u32 pmxevttype[8]; + u32 pmxevtcnt[8]; +}; + struct arm_pmu { struct pmu pmu; cpumask_t active_irqs; + cpumask_t valid_cpus; char *name; irqreturn_t (*handle_irq)(int irq_num, void *dev); void (*enable)(struct perf_event *event); @@ -81,6 +91,8 @@ int (*request_irq)(struct arm_pmu *, irq_handler_t handler); void (*free_irq)(struct arm_pmu *); int (*map_event)(struct perf_event *event); + void (*save_regs)(struct arm_pmu *, struct cpupmu_regs *); + void (*restore_regs)(struct arm_pmu *, struct cpupmu_regs *); int num_events; atomic_t active_events; struct mutex reserve_mutex; diff -Nur linux-3.10.30/arch/arm/include/asm/psci.h linux-3.10.30-cubox-i/arch/arm/include/asm/psci.h --- linux-3.10.30/arch/arm/include/asm/psci.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/psci.h 2014-03-08 20:32:53.000000000 +0100 @@ -16,6 +16,10 @@ #define PSCI_POWER_STATE_TYPE_STANDBY 0 #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 +#define PSCI_POWER_STATE_AFFINITY_LEVEL0 0 +#define PSCI_POWER_STATE_AFFINITY_LEVEL1 1 +#define PSCI_POWER_STATE_AFFINITY_LEVEL2 2 +#define PSCI_POWER_STATE_AFFINITY_LEVEL3 3 struct psci_power_state { u16 id; @@ -32,5 +36,22 @@ }; extern struct psci_operations psci_ops; +extern struct smp_operations psci_smp_ops; +#ifdef CONFIG_ARM_PSCI +void psci_init(void); +bool psci_smp_available(void); +#else +static inline void psci_init(void) { } +static inline bool psci_smp_available(void) { return false; } +#endif + +#ifdef CONFIG_ARM_PSCI +extern int __init psci_probe(void); +#else +static inline int psci_probe(void) +{ + return -ENODEV; +} +#endif #endif /* __ASM_ARM_PSCI_H */ diff -Nur linux-3.10.30/arch/arm/include/asm/smp.h linux-3.10.30-cubox-i/arch/arm/include/asm/smp.h --- linux-3.10.30/arch/arm/include/asm/smp.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/smp.h 2014-03-08 20:32:53.000000000 +0100 @@ -81,6 +81,8 @@ extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask); +extern int register_ipi_completion(struct completion *completion, int cpu); + struct smp_operations { #ifdef CONFIG_SMP /* diff -Nur linux-3.10.30/arch/arm/include/asm/topology.h linux-3.10.30-cubox-i/arch/arm/include/asm/topology.h --- linux-3.10.30/arch/arm/include/asm/topology.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/asm/topology.h 2014-03-08 20:32:53.000000000 +0100 @@ -26,11 +26,45 @@ void init_cpu_topology(void); void store_cpu_topology(unsigned int cpuid); const struct cpumask *cpu_coregroup_mask(int cpu); +int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask); + +#ifdef CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE +/* Common values for CPUs */ +#ifndef SD_CPU_INIT +#define SD_CPU_INIT (struct sched_domain) { \ + .min_interval = 1, \ + .max_interval = 4, \ + .busy_factor = 64, \ + .imbalance_pct = 125, \ + .cache_nice_tries = 1, \ + .busy_idx = 2, \ + .idle_idx = 1, \ + .newidle_idx = 0, \ + .wake_idx = 0, \ + .forkexec_idx = 0, \ + \ + .flags = 0*SD_LOAD_BALANCE \ + | 1*SD_BALANCE_NEWIDLE \ + | 1*SD_BALANCE_EXEC \ + | 1*SD_BALANCE_FORK \ + | 0*SD_BALANCE_WAKE \ + | 1*SD_WAKE_AFFINE \ + | 0*SD_SHARE_CPUPOWER \ + | 0*SD_SHARE_PKG_RESOURCES \ + | 0*SD_SERIALIZE \ + , \ + .last_balance = jiffies, \ + .balance_interval = 1, \ +} +#endif +#endif /* CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE */ #else static inline void init_cpu_topology(void) { } static inline void store_cpu_topology(unsigned int cpuid) { } +static inline int cluster_to_logical_mask(unsigned int socket_id, + cpumask_t *cluster_mask) { return -EINVAL; } #endif diff -Nur linux-3.10.30/arch/arm/include/debug/imx-uart.h linux-3.10.30-cubox-i/arch/arm/include/debug/imx-uart.h --- linux-3.10.30/arch/arm/include/debug/imx-uart.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/debug/imx-uart.h 2014-03-08 20:32:53.000000000 +0100 @@ -65,6 +65,14 @@ #define IMX6Q_UART_BASE_ADDR(n) IMX6Q_UART##n##_BASE_ADDR #define IMX6Q_UART_BASE(n) IMX6Q_UART_BASE_ADDR(n) +#define IMX6SL_UART1_BASE_ADDR 0x02020000 +#define IMX6SL_UART2_BASE_ADDR 0x02024000 +#define IMX6SL_UART3_BASE_ADDR 0x02034000 +#define IMX6SL_UART4_BASE_ADDR 0x02038000 +#define IMX6SL_UART5_BASE_ADDR 0x02018000 +#define IMX6SL_UART_BASE_ADDR(n) IMX6SL_UART##n##_BASE_ADDR +#define IMX6SL_UART_BASE(n) IMX6SL_UART_BASE_ADDR(n) + #define IMX_DEBUG_UART_BASE(soc) soc##_UART_BASE(CONFIG_DEBUG_IMX_UART_PORT) #ifdef CONFIG_DEBUG_IMX1_UART @@ -83,6 +91,8 @@ #define UART_PADDR IMX_DEBUG_UART_BASE(IMX53) #elif defined(CONFIG_DEBUG_IMX6Q_UART) #define UART_PADDR IMX_DEBUG_UART_BASE(IMX6Q) +#elif defined(CONFIG_DEBUG_IMX6SL_UART) +#define UART_PADDR IMX_DEBUG_UART_BASE(IMX6SL) #endif #endif /* __DEBUG_IMX_UART_H */ diff -Nur linux-3.10.30/arch/arm/include/debug/vf.S linux-3.10.30-cubox-i/arch/arm/include/debug/vf.S --- linux-3.10.30/arch/arm/include/debug/vf.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/include/debug/vf.S 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + * + */ + + .macro addruart, rp, rv, tmp + ldr \rp, =0x40028000 @ physical + ldr \rv, =0xfe028000 @ virtual + .endm + + .macro senduart, rd, rx + strb \rd, [\rx, #0x7] @ Data Register + .endm + + .macro busyuart, rd, rx +1001: ldrb \rd, [\rx, #0x4] @ Status Register 1 + tst \rd, #1 << 6 @ TC + beq 1001b @ wait until transmit done + .endm + + .macro waituart,rd,rx + .endm diff -Nur linux-3.10.30/arch/arm/kernel/Makefile linux-3.10.30-cubox-i/arch/arm/kernel/Makefile --- linux-3.10.30/arch/arm/kernel/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/Makefile 2014-03-08 20:32:53.000000000 +0100 @@ -82,6 +82,9 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o -obj-$(CONFIG_ARM_PSCI) += psci.o +ifeq ($(CONFIG_ARM_PSCI),y) +obj-y += psci.o +obj-$(CONFIG_SMP) += psci_smp.o +endif extra-y := $(head-y) vmlinux.lds diff -Nur linux-3.10.30/arch/arm/kernel/bios32.c linux-3.10.30-cubox-i/arch/arm/kernel/bios32.c --- linux-3.10.30/arch/arm/kernel/bios32.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/bios32.c 2014-03-08 20:32:53.000000000 +0100 @@ -363,6 +363,20 @@ } EXPORT_SYMBOL(pcibios_fixup_bus); +void pcibios_add_bus(struct pci_bus *bus) +{ + struct pci_sys_data *sys = bus->sysdata; + if (sys->add_bus) + sys->add_bus(bus); +} + +void pcibios_remove_bus(struct pci_bus *bus) +{ + struct pci_sys_data *sys = bus->sysdata; + if (sys->remove_bus) + sys->remove_bus(bus); +} + /* * Swizzle the device pin each time we cross a bridge. If a platform does * not provide a swizzle function, we perform the standard PCI swizzling. @@ -463,6 +477,8 @@ sys->swizzle = hw->swizzle; sys->map_irq = hw->map_irq; sys->align_resource = hw->align_resource; + sys->add_bus = hw->add_bus; + sys->remove_bus = hw->remove_bus; INIT_LIST_HEAD(&sys->resources); if (hw->private_data) diff -Nur linux-3.10.30/arch/arm/kernel/head.S linux-3.10.30-cubox-i/arch/arm/kernel/head.S --- linux-3.10.30/arch/arm/kernel/head.S 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/head.S 2014-03-08 20:32:53.000000000 +0100 @@ -342,7 +342,6 @@ .long __turn_mmu_on_end #if defined(CONFIG_SMP) - __CPUINIT ENTRY(secondary_startup) /* * Common entry point for secondary CPUs. diff -Nur linux-3.10.30/arch/arm/kernel/hw_breakpoint.c linux-3.10.30-cubox-i/arch/arm/kernel/hw_breakpoint.c --- linux-3.10.30/arch/arm/kernel/hw_breakpoint.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/hw_breakpoint.c 2014-03-08 20:32:53.000000000 +0100 @@ -1049,7 +1049,8 @@ static void __init pm_init(void) { - cpu_pm_register_notifier(&dbg_cpu_pm_nb); + if (has_ossr) + cpu_pm_register_notifier(&dbg_cpu_pm_nb); } #else static inline void pm_init(void) diff -Nur linux-3.10.30/arch/arm/kernel/perf_event.c linux-3.10.30-cubox-i/arch/arm/kernel/perf_event.c --- linux-3.10.30/arch/arm/kernel/perf_event.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/perf_event.c 2014-03-08 20:32:53.000000000 +0100 @@ -12,6 +12,7 @@ */ #define pr_fmt(fmt) "hw perfevents: " fmt +#include #include #include #include @@ -86,6 +87,9 @@ return armpmu_map_cache_event(cache_map, config); case PERF_TYPE_RAW: return armpmu_map_raw_event(raw_event_mask, config); + default: + if (event->attr.type >= PERF_TYPE_MAX) + return armpmu_map_raw_event(raw_event_mask, config); } return -ENOENT; @@ -163,6 +167,8 @@ struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; + if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus)) + return; /* * ARM pmu always has to update the counter, so ignore * PERF_EF_UPDATE, see comments in armpmu_start(). @@ -179,6 +185,8 @@ struct arm_pmu *armpmu = to_arm_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; + if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus)) + return; /* * ARM pmu always has to reprogram the period, so ignore * PERF_EF_RELOAD, see the comment below. @@ -206,6 +214,9 @@ struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; + if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus)) + return; + armpmu_stop(event, PERF_EF_UPDATE); hw_events->events[idx] = NULL; clear_bit(idx, hw_events->used_mask); @@ -222,6 +233,10 @@ int idx; int err = 0; + /* An event following a process won't be stopped earlier */ + if (!cpumask_test_cpu(smp_processor_id(), &armpmu->valid_cpus)) + return 0; + perf_pmu_disable(event->pmu); /* If we don't have a space for the counter then finish early. */ @@ -424,6 +439,10 @@ int err = 0; atomic_t *active_events = &armpmu->active_events; + if (event->cpu != -1 && + !cpumask_test_cpu(event->cpu, &armpmu->valid_cpus)) + return -ENOENT; + /* does not support taken branch sampling */ if (has_branch_stack(event)) return -EOPNOTSUPP; diff -Nur linux-3.10.30/arch/arm/kernel/perf_event_cpu.c linux-3.10.30-cubox-i/arch/arm/kernel/perf_event_cpu.c --- linux-3.10.30/arch/arm/kernel/perf_event_cpu.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/perf_event_cpu.c 2014-03-08 20:32:53.000000000 +0100 @@ -19,6 +19,7 @@ #define pr_fmt(fmt) "CPU PMU: " fmt #include +#include #include #include #include @@ -31,33 +32,36 @@ #include /* Set at runtime when we know what CPU type we are. */ -static struct arm_pmu *cpu_pmu; +static DEFINE_PER_CPU(struct arm_pmu *, cpu_pmu); static DEFINE_PER_CPU(struct perf_event * [ARMPMU_MAX_HWEVENTS], hw_events); static DEFINE_PER_CPU(unsigned long [BITS_TO_LONGS(ARMPMU_MAX_HWEVENTS)], used_mask); static DEFINE_PER_CPU(struct pmu_hw_events, cpu_hw_events); +static DEFINE_PER_CPU(struct cpupmu_regs, cpu_pmu_regs); + /* * Despite the names, these two functions are CPU-specific and are used * by the OProfile/perf code. */ const char *perf_pmu_name(void) { - if (!cpu_pmu) + struct arm_pmu *pmu = per_cpu(cpu_pmu, 0); + if (!pmu) return NULL; - return cpu_pmu->name; + return pmu->name; } EXPORT_SYMBOL_GPL(perf_pmu_name); int perf_num_counters(void) { - int max_events = 0; + struct arm_pmu *pmu = per_cpu(cpu_pmu, 0); - if (cpu_pmu != NULL) - max_events = cpu_pmu->num_events; + if (!pmu) + return 0; - return max_events; + return pmu->num_events; } EXPORT_SYMBOL_GPL(perf_num_counters); @@ -75,11 +79,13 @@ { int i, irq, irqs; struct platform_device *pmu_device = cpu_pmu->plat_device; + int cpu = -1; irqs = min(pmu_device->num_resources, num_possible_cpus()); for (i = 0; i < irqs; ++i) { - if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) + cpu = cpumask_next(cpu, &cpu_pmu->valid_cpus); + if (!cpumask_test_and_clear_cpu(cpu, &cpu_pmu->active_irqs)) continue; irq = platform_get_irq(pmu_device, i); if (irq >= 0) @@ -91,6 +97,7 @@ { int i, err, irq, irqs; struct platform_device *pmu_device = cpu_pmu->plat_device; + int cpu = -1; if (!pmu_device) return -ENODEV; @@ -103,6 +110,7 @@ for (i = 0; i < irqs; ++i) { err = 0; + cpu = cpumask_next(cpu, &cpu_pmu->valid_cpus); irq = platform_get_irq(pmu_device, i); if (irq < 0) continue; @@ -112,7 +120,7 @@ * assume that we're running on a uniprocessor machine and * continue. Otherwise, continue without this interrupt. */ - if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { + if (irq_set_affinity(irq, cpumask_of(cpu)) && irqs > 1) { pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", irq, i); continue; @@ -126,7 +134,7 @@ return err; } - cpumask_set_cpu(i, &cpu_pmu->active_irqs); + cpumask_set_cpu(cpu, &cpu_pmu->active_irqs); } return 0; @@ -135,7 +143,7 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu) { int cpu; - for_each_possible_cpu(cpu) { + for_each_cpu_mask(cpu, cpu_pmu->valid_cpus) { struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu); events->events = per_cpu(hw_events, cpu); events->used_mask = per_cpu(used_mask, cpu); @@ -148,7 +156,7 @@ /* Ensure the PMU has sane values out of reset. */ if (cpu_pmu->reset) - on_each_cpu(cpu_pmu->reset, cpu_pmu, 1); + on_each_cpu_mask(&cpu_pmu->valid_cpus, cpu_pmu->reset, cpu_pmu, 1); } /* @@ -160,21 +168,46 @@ static int __cpuinit cpu_pmu_notify(struct notifier_block *b, unsigned long action, void *hcpu) { + struct arm_pmu *pmu = per_cpu(cpu_pmu, (long)hcpu); + if ((action & ~CPU_TASKS_FROZEN) != CPU_STARTING) return NOTIFY_DONE; - if (cpu_pmu && cpu_pmu->reset) - cpu_pmu->reset(cpu_pmu); + if (pmu && pmu->reset) + pmu->reset(pmu); else return NOTIFY_DONE; return NOTIFY_OK; } +static int cpu_pmu_pm_notify(struct notifier_block *b, + unsigned long action, void *hcpu) +{ + int cpu = smp_processor_id(); + struct arm_pmu *pmu = per_cpu(cpu_pmu, cpu); + struct cpupmu_regs *pmuregs = &per_cpu(cpu_pmu_regs, cpu); + + if (!pmu) + return NOTIFY_DONE; + + if (action == CPU_PM_ENTER && pmu->save_regs) { + pmu->save_regs(pmu, pmuregs); + } else if (action == CPU_PM_EXIT && pmu->restore_regs) { + pmu->restore_regs(pmu, pmuregs); + } + + return NOTIFY_OK; +} + static struct notifier_block __cpuinitdata cpu_pmu_hotplug_notifier = { .notifier_call = cpu_pmu_notify, }; +static struct notifier_block __cpuinitdata cpu_pmu_pm_notifier = { + .notifier_call = cpu_pmu_pm_notify, +}; + /* * PMU platform driver and devicetree bindings. */ @@ -246,6 +279,9 @@ } } + /* assume PMU support all the CPUs in this case */ + cpumask_setall(&pmu->valid_cpus); + put_cpu(); return ret; } @@ -253,15 +289,10 @@ static int cpu_pmu_device_probe(struct platform_device *pdev) { const struct of_device_id *of_id; - int (*init_fn)(struct arm_pmu *); struct device_node *node = pdev->dev.of_node; struct arm_pmu *pmu; - int ret = -ENODEV; - - if (cpu_pmu) { - pr_info("attempt to register multiple PMU devices!"); - return -ENOSPC; - } + int ret = 0; + int cpu; pmu = kzalloc(sizeof(struct arm_pmu), GFP_KERNEL); if (!pmu) { @@ -270,8 +301,28 @@ } if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) { - init_fn = of_id->data; - ret = init_fn(pmu); + smp_call_func_t init_fn = (smp_call_func_t)of_id->data; + struct device_node *ncluster; + int cluster = -1; + cpumask_t sibling_mask; + + ncluster = of_parse_phandle(node, "cluster", 0); + if (ncluster) { + int len; + const u32 *hwid; + hwid = of_get_property(ncluster, "reg", &len); + if (hwid && len == 4) + cluster = be32_to_cpup(hwid); + } + /* set sibling mask to all cpu mask if socket is not specified */ + if (cluster == -1 || + cluster_to_logical_mask(cluster, &sibling_mask)) + cpumask_setall(&sibling_mask); + + smp_call_function_any(&sibling_mask, init_fn, pmu, 1); + + /* now set the valid_cpus after init */ + cpumask_copy(&pmu->valid_cpus, &sibling_mask); } else { ret = probe_current_pmu(pmu); } @@ -281,10 +332,12 @@ goto out_free; } - cpu_pmu = pmu; - cpu_pmu->plat_device = pdev; - cpu_pmu_init(cpu_pmu); - ret = armpmu_register(cpu_pmu, PERF_TYPE_RAW); + for_each_cpu_mask(cpu, pmu->valid_cpus) + per_cpu(cpu_pmu, cpu) = pmu; + + pmu->plat_device = pdev; + cpu_pmu_init(pmu); + ret = armpmu_register(pmu, -1); if (!ret) return 0; @@ -313,9 +366,17 @@ if (err) return err; + err = cpu_pm_register_notifier(&cpu_pmu_pm_notifier); + if (err) { + unregister_cpu_notifier(&cpu_pmu_hotplug_notifier); + return err; + } + err = platform_driver_register(&cpu_pmu_driver); - if (err) + if (err) { + cpu_pm_unregister_notifier(&cpu_pmu_pm_notifier); unregister_cpu_notifier(&cpu_pmu_hotplug_notifier); + } return err; } diff -Nur linux-3.10.30/arch/arm/kernel/perf_event_v7.c linux-3.10.30-cubox-i/arch/arm/kernel/perf_event_v7.c --- linux-3.10.30/arch/arm/kernel/perf_event_v7.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/perf_event_v7.c 2014-03-08 20:32:53.000000000 +0100 @@ -950,6 +950,51 @@ } #endif +static void armv7pmu_save_regs(struct arm_pmu *cpu_pmu, + struct cpupmu_regs *regs) +{ + unsigned int cnt; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (regs->pmc)); + if (!(regs->pmc & ARMV7_PMNC_E)) + return; + + asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (regs->pmcntenset)); + asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r" (regs->pmuseren)); + asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (regs->pmintenset)); + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (regs->pmxevtcnt[0])); + for (cnt = ARMV7_IDX_COUNTER0; + cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) { + armv7_pmnc_select_counter(cnt); + asm volatile("mrc p15, 0, %0, c9, c13, 1" + : "=r"(regs->pmxevttype[cnt])); + asm volatile("mrc p15, 0, %0, c9, c13, 2" + : "=r"(regs->pmxevtcnt[cnt])); + } + return; +} + +static void armv7pmu_restore_regs(struct arm_pmu *cpu_pmu, + struct cpupmu_regs *regs) +{ + unsigned int cnt; + if (!(regs->pmc & ARMV7_PMNC_E)) + return; + + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (regs->pmcntenset)); + asm volatile("mcr p15, 0, %0, c9, c14, 0" : : "r" (regs->pmuseren)); + asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (regs->pmintenset)); + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (regs->pmxevtcnt[0])); + for (cnt = ARMV7_IDX_COUNTER0; + cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) { + armv7_pmnc_select_counter(cnt); + asm volatile("mcr p15, 0, %0, c9, c13, 1" + : : "r"(regs->pmxevttype[cnt])); + asm volatile("mcr p15, 0, %0, c9, c13, 2" + : : "r"(regs->pmxevtcnt[cnt])); + } + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (regs->pmc)); +} + static void armv7pmu_enable_event(struct perf_event *event) { unsigned long flags; @@ -1223,6 +1268,8 @@ cpu_pmu->start = armv7pmu_start; cpu_pmu->stop = armv7pmu_stop; cpu_pmu->reset = armv7pmu_reset; + cpu_pmu->save_regs = armv7pmu_save_regs; + cpu_pmu->restore_regs = armv7pmu_restore_regs; cpu_pmu->max_period = (1LLU << 32) - 1; }; @@ -1240,7 +1287,7 @@ static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) { armv7pmu_init(cpu_pmu); - cpu_pmu->name = "ARMv7 Cortex-A8"; + cpu_pmu->name = "ARMv7_Cortex_A8"; cpu_pmu->map_event = armv7_a8_map_event; cpu_pmu->num_events = armv7_read_num_pmnc_events(); return 0; @@ -1249,7 +1296,7 @@ static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu) { armv7pmu_init(cpu_pmu); - cpu_pmu->name = "ARMv7 Cortex-A9"; + cpu_pmu->name = "ARMv7_Cortex_A9"; cpu_pmu->map_event = armv7_a9_map_event; cpu_pmu->num_events = armv7_read_num_pmnc_events(); return 0; @@ -1258,7 +1305,7 @@ static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu) { armv7pmu_init(cpu_pmu); - cpu_pmu->name = "ARMv7 Cortex-A5"; + cpu_pmu->name = "ARMv7_Cortex_A5"; cpu_pmu->map_event = armv7_a5_map_event; cpu_pmu->num_events = armv7_read_num_pmnc_events(); return 0; @@ -1267,7 +1314,7 @@ static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu) { armv7pmu_init(cpu_pmu); - cpu_pmu->name = "ARMv7 Cortex-A15"; + cpu_pmu->name = "ARMv7_Cortex_A15"; cpu_pmu->map_event = armv7_a15_map_event; cpu_pmu->num_events = armv7_read_num_pmnc_events(); cpu_pmu->set_event_filter = armv7pmu_set_event_filter; @@ -1277,7 +1324,7 @@ static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu) { armv7pmu_init(cpu_pmu); - cpu_pmu->name = "ARMv7 Cortex-A7"; + cpu_pmu->name = "ARMv7_Cortex_A7"; cpu_pmu->map_event = armv7_a7_map_event; cpu_pmu->num_events = armv7_read_num_pmnc_events(); cpu_pmu->set_event_filter = armv7pmu_set_event_filter; diff -Nur linux-3.10.30/arch/arm/kernel/process.c linux-3.10.30-cubox-i/arch/arm/kernel/process.c --- linux-3.10.30/arch/arm/kernel/process.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/process.c 2014-03-08 20:32:53.000000000 +0100 @@ -170,8 +170,10 @@ */ void arch_cpu_idle(void) { + idle_notifier_call_chain(IDLE_START); if (cpuidle_idle_call()) default_idle(); + idle_notifier_call_chain(IDLE_END); } static char reboot_mode = 'h'; diff -Nur linux-3.10.30/arch/arm/kernel/psci.c linux-3.10.30-cubox-i/arch/arm/kernel/psci.c --- linux-3.10.30/arch/arm/kernel/psci.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/psci.c 2014-03-08 20:32:53.000000000 +0100 @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -26,6 +27,11 @@ struct psci_operations psci_ops; +/* Type of psci support. Currently can only be enabled or disabled */ +#define PSCI_SUP_DISABLED 0 +#define PSCI_SUP_ENABLED 1 + +static unsigned int psci; static int (*invoke_psci_fn)(u32, u32, u32, u32); enum psci_function { @@ -42,6 +48,7 @@ #define PSCI_RET_EOPNOTSUPP -1 #define PSCI_RET_EINVAL -2 #define PSCI_RET_EPERM -3 +#define PSCI_RET_EALREADYON -4 static int psci_to_linux_errno(int errno) { @@ -54,6 +61,8 @@ return -EINVAL; case PSCI_RET_EPERM: return -EPERM; + case PSCI_RET_EALREADYON: + return -EAGAIN; }; return -EINVAL; @@ -158,15 +167,18 @@ {}, }; -static int __init psci_init(void) +void __init psci_init(void) { struct device_node *np; const char *method; u32 id; + if (psci == PSCI_SUP_DISABLED) + return; + np = of_find_matching_node(NULL, psci_of_match); if (!np) - return 0; + return; pr_info("probing function IDs from device-tree\n"); @@ -206,6 +218,35 @@ out_put_node: of_node_put(np); - return 0; + return; +} + +int __init psci_probe(void) +{ + struct device_node *np; + int ret = -ENODEV; + + if (psci == PSCI_SUP_ENABLED) { + np = of_find_matching_node(NULL, psci_of_match); + if (np) + ret = 0; + } + + of_node_put(np); + return ret; +} + +static int __init early_psci(char *val) +{ + int ret = 0; + + if (strcmp(val, "enable") == 0) + psci = PSCI_SUP_ENABLED; + else if (strcmp(val, "disable") == 0) + psci = PSCI_SUP_DISABLED; + else + ret = -EINVAL; + + return ret; } -early_initcall(psci_init); +early_param("psci", early_psci); diff -Nur linux-3.10.30/arch/arm/kernel/psci_smp.c linux-3.10.30-cubox-i/arch/arm/kernel/psci_smp.c --- linux-3.10.30/arch/arm/kernel/psci_smp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/psci_smp.c 2014-03-08 20:32:53.000000000 +0100 @@ -0,0 +1,84 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + * + * Author: Will Deacon + */ + +#include +#include +#include +#include + +#include +#include + +/* + * psci_smp assumes that the following is true about PSCI: + * + * cpu_suspend Suspend the execution on a CPU + * @state we don't currently describe affinity levels, so just pass 0. + * @entry_point the first instruction to be executed on return + * returns 0 success, < 0 on failure + * + * cpu_off Power down a CPU + * @state we don't currently describe affinity levels, so just pass 0. + * no return on successful call + * + * cpu_on Power up a CPU + * @cpuid cpuid of target CPU, as from MPIDR + * @entry_point the first instruction to be executed on return + * returns 0 success, < 0 on failure + * + * migrate Migrate the context to a different CPU + * @cpuid cpuid of target CPU, as from MPIDR + * returns 0 success, < 0 on failure + * + */ + +extern void secondary_startup(void); + +static int __cpuinit psci_boot_secondary(unsigned int cpu, + struct task_struct *idle) +{ + if (psci_ops.cpu_on) + return psci_ops.cpu_on(cpu_logical_map(cpu), + __pa(secondary_startup)); + return -ENODEV; +} + +#ifdef CONFIG_HOTPLUG_CPU +void __ref psci_cpu_die(unsigned int cpu) +{ + const struct psci_power_state ps = { + .type = PSCI_POWER_STATE_TYPE_POWER_DOWN, + }; + + if (psci_ops.cpu_off) + psci_ops.cpu_off(ps); + + /* We should never return */ + panic("psci: cpu %d failed to shutdown\n", cpu); +} +#else +#define psci_cpu_die NULL +#endif + +bool __init psci_smp_available(void) +{ + /* is cpu_on available at least? */ + return (psci_ops.cpu_on != NULL); +} + +struct smp_operations __initdata psci_smp_ops = { + .smp_boot_secondary = psci_boot_secondary, + .cpu_die = psci_cpu_die, +}; diff -Nur linux-3.10.30/arch/arm/kernel/setup.c linux-3.10.30-cubox-i/arch/arm/kernel/setup.c --- linux-3.10.30/arch/arm/kernel/setup.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/setup.c 2014-03-08 20:32:53.000000000 +0100 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -261,6 +262,19 @@ int aliasing_icache; unsigned int id_reg, num_sets, line_size; +#ifdef CONFIG_BIG_LITTLE + /* + * We expect a combination of Cortex-A15 and Cortex-A7 cores. + * A7 = VIPT aliasing I-cache + * A15 = PIPT (non-aliasing) I-cache + * To cater for this discrepancy, let's assume aliasing I-cache + * all the time. This means unneeded extra work on the A15 but + * only ptrace is affected which is not performance critical. + */ + if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc0f0) + return 1; +#endif + /* PIPT caches never alias. */ if (icache_is_pipt()) return 0; @@ -796,9 +810,15 @@ unflatten_device_tree(); arm_dt_init_cpu_maps(); + psci_init(); #ifdef CONFIG_SMP if (is_smp()) { - smp_set_ops(mdesc->smp); + if (!mdesc->smp_init || !mdesc->smp_init()) { + if (psci_smp_available()) + smp_set_ops(&psci_smp_ops); + else if (mdesc->smp) + smp_set_ops(mdesc->smp); + } smp_init_cpus(); } #endif diff -Nur linux-3.10.30/arch/arm/kernel/sleep.S linux-3.10.30-cubox-i/arch/arm/kernel/sleep.S --- linux-3.10.30/arch/arm/kernel/sleep.S 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/sleep.S 2014-03-08 20:32:53.000000000 +0100 @@ -4,6 +4,7 @@ #include #include #include +#include "entry-header.S" .text /* @@ -30,9 +31,8 @@ mov r2, r5 @ virtual SP ldr r3, =sleep_save_sp #ifdef CONFIG_SMP - ALT_SMP(mrc p15, 0, lr, c0, c0, 5) - ALT_UP(mov lr, #0) - and lr, lr, #15 + get_thread_info r5 + ldr lr, [r5, #TI_CPU] @ cpu logical index add r3, r3, lr, lsl #2 #endif bl __cpu_suspend_save @@ -82,10 +82,13 @@ .align ENTRY(cpu_resume) #ifdef CONFIG_SMP + mov r1, #0 @ fall-back logical index for UP + ALT_SMP(mrc p15, 0, r0, c0, c0, 5) + ALT_UP_B(1f) + bic r0, #0xff000000 + bl cpu_logical_index @ return logical index in r1 +1: adr r0, sleep_save_sp - ALT_SMP(mrc p15, 0, r1, c0, c0, 5) - ALT_UP(mov r1, #0) - and r1, r1, #15 ldr r0, [r0, r1, lsl #2] @ stack phys addr #else ldr r0, sleep_save_sp @ stack phys addr @@ -102,3 +105,20 @@ .rept CONFIG_NR_CPUS .long 0 @ preserve stack phys ptr here .endr + +#ifdef CONFIG_SMP +cpu_logical_index: + adr r3, cpu_map_ptr + ldr r2, [r3] + add r3, r3, r2 @ virt_to_phys(__cpu_logical_map) + mov r1, #0 +1: + ldr r2, [r3, r1, lsl #2] + cmp r2, r0 + moveq pc, lr + add r1, r1, #1 + b 1b + +cpu_map_ptr: + .long __cpu_logical_map - . +#endif diff -Nur linux-3.10.30/arch/arm/kernel/smp.c linux-3.10.30-cubox-i/arch/arm/kernel/smp.c --- linux-3.10.30/arch/arm/kernel/smp.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/smp.c 2014-03-08 20:32:53.000000000 +0100 @@ -46,6 +46,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + /* * as from 2.5, kernels no longer have an init_tasks structure * so we need some other way of telling a new secondary core @@ -57,7 +60,7 @@ * control for which core is the next to come out of the secondary * boot "holding pen" */ -volatile int __cpuinitdata pen_release = -1; +volatile int pen_release = -1; enum ipi_msg_type { IPI_WAKEUP, @@ -66,6 +69,7 @@ IPI_CALL_FUNC, IPI_CALL_FUNC_SINGLE, IPI_CPU_STOP, + IPI_COMPLETION, }; static DECLARE_COMPLETION(cpu_running); @@ -463,6 +467,7 @@ S(IPI_CALL_FUNC, "Function call interrupts"), S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"), S(IPI_CPU_STOP, "CPU stop interrupts"), + S(IPI_COMPLETION, "completion interrupts"), }; void show_ipi_list(struct seq_file *p, int prec) @@ -588,6 +593,19 @@ cpu_relax(); } +static DEFINE_PER_CPU(struct completion *, cpu_completion); + +int register_ipi_completion(struct completion *completion, int cpu) +{ + per_cpu(cpu_completion, cpu) = completion; + return IPI_COMPLETION; +} + +static void ipi_complete(unsigned int cpu) +{ + complete(per_cpu(cpu_completion, cpu)); +} + /* * Main handler for inter-processor interrupts */ @@ -604,6 +622,7 @@ if (ipinr < NR_IPI) __inc_irq_stat(cpu, ipi_irqs[ipinr]); + trace_arm_ipi_entry(ipinr); switch (ipinr) { case IPI_WAKEUP: break; @@ -638,11 +657,18 @@ irq_exit(); break; + case IPI_COMPLETION: + irq_enter(); + ipi_complete(cpu); + irq_exit(); + break; + default: printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); break; } + trace_arm_ipi_exit(ipinr); set_irq_regs(old_regs); } diff -Nur linux-3.10.30/arch/arm/kernel/topology.c linux-3.10.30-cubox-i/arch/arm/kernel/topology.c --- linux-3.10.30/arch/arm/kernel/topology.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/topology.c 2014-03-08 20:32:53.000000000 +0100 @@ -23,6 +23,7 @@ #include #include +#include #include /* @@ -289,6 +290,140 @@ cpu_topology[cpuid].socket_id, mpidr); } + +#ifdef CONFIG_SCHED_HMP + +static const char * const little_cores[] = { + "arm,cortex-a7", + NULL, +}; + +static bool is_little_cpu(struct device_node *cn) +{ + const char * const *lc; + for (lc = little_cores; *lc; lc++) + if (of_device_is_compatible(cn, *lc)) + return true; + return false; +} + +void __init arch_get_fast_and_slow_cpus(struct cpumask *fast, + struct cpumask *slow) +{ + struct device_node *cn = NULL; + int cpu; + + cpumask_clear(fast); + cpumask_clear(slow); + + /* + * Use the config options if they are given. This helps testing + * HMP scheduling on systems without a big.LITTLE architecture. + */ + if (strlen(CONFIG_HMP_FAST_CPU_MASK) && strlen(CONFIG_HMP_SLOW_CPU_MASK)) { + if (cpulist_parse(CONFIG_HMP_FAST_CPU_MASK, fast)) + WARN(1, "Failed to parse HMP fast cpu mask!\n"); + if (cpulist_parse(CONFIG_HMP_SLOW_CPU_MASK, slow)) + WARN(1, "Failed to parse HMP slow cpu mask!\n"); + return; + } + + /* + * Else, parse device tree for little cores. + */ + while ((cn = of_find_node_by_type(cn, "cpu"))) { + + const u32 *mpidr; + int len; + + mpidr = of_get_property(cn, "reg", &len); + if (!mpidr || len != 4) { + pr_err("* %s missing reg property\n", cn->full_name); + continue; + } + + cpu = get_logical_index(be32_to_cpup(mpidr)); + if (cpu == -EINVAL) { + pr_err("couldn't get logical index for mpidr %x\n", + be32_to_cpup(mpidr)); + break; + } + + if (is_little_cpu(cn)) + cpumask_set_cpu(cpu, slow); + else + cpumask_set_cpu(cpu, fast); + } + + if (!cpumask_empty(fast) && !cpumask_empty(slow)) + return; + + /* + * We didn't find both big and little cores so let's call all cores + * fast as this will keep the system running, with all cores being + * treated equal. + */ + cpumask_setall(fast); + cpumask_clear(slow); +} + +struct cpumask hmp_slow_cpu_mask; + +void __init arch_get_hmp_domains(struct list_head *hmp_domains_list) +{ + struct cpumask hmp_fast_cpu_mask; + struct hmp_domain *domain; + + arch_get_fast_and_slow_cpus(&hmp_fast_cpu_mask, &hmp_slow_cpu_mask); + + /* + * Initialize hmp_domains + * Must be ordered with respect to compute capacity. + * Fastest domain at head of list. + */ + if(!cpumask_empty(&hmp_slow_cpu_mask)) { + domain = (struct hmp_domain *) + kmalloc(sizeof(struct hmp_domain), GFP_KERNEL); + cpumask_copy(&domain->possible_cpus, &hmp_slow_cpu_mask); + cpumask_and(&domain->cpus, cpu_online_mask, &domain->possible_cpus); + list_add(&domain->hmp_domains, hmp_domains_list); + } + domain = (struct hmp_domain *) + kmalloc(sizeof(struct hmp_domain), GFP_KERNEL); + cpumask_copy(&domain->possible_cpus, &hmp_fast_cpu_mask); + cpumask_and(&domain->cpus, cpu_online_mask, &domain->possible_cpus); + list_add(&domain->hmp_domains, hmp_domains_list); +} +#endif /* CONFIG_SCHED_HMP */ + + +/* + * cluster_to_logical_mask - return cpu logical mask of CPUs in a cluster + * @socket_id: cluster HW identifier + * @cluster_mask: the cpumask location to be initialized, modified by the + * function only if return value == 0 + * + * Return: + * + * 0 on success + * -EINVAL if cluster_mask is NULL or there is no record matching socket_id + */ +int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask) +{ + int cpu; + + if (!cluster_mask) + return -EINVAL; + + for_each_online_cpu(cpu) + if (socket_id == topology_physical_package_id(cpu)) { + cpumask_copy(cluster_mask, topology_core_cpumask(cpu)); + return 0; + } + + return -EINVAL; +} + /* * init_cpu_topology is called at boot when only one cpu is running * which prevent simultaneous write access to cpu_topology array diff -Nur linux-3.10.30/arch/arm/kernel/traps.c linux-3.10.30-cubox-i/arch/arm/kernel/traps.c --- linux-3.10.30/arch/arm/kernel/traps.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/kernel/traps.c 2014-03-08 20:32:53.000000000 +0100 @@ -61,7 +61,7 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame) { #ifdef CONFIG_KALLSYMS - printk("[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from); + printk("[<%08lx>] (%ps) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from); #else printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); #endif diff -Nur linux-3.10.30/arch/arm/lib/backtrace.S linux-3.10.30-cubox-i/arch/arm/lib/backtrace.S --- linux-3.10.30/arch/arm/lib/backtrace.S 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/lib/backtrace.S 2014-03-08 20:32:53.000000000 +0100 @@ -80,14 +80,14 @@ ldr r1, [sv_pc, #-4] @ if stmfd sp!, {args} exists, ldr r3, .Ldsi+4 - teq r3, r1, lsr #10 + teq r3, r1, lsr #11 ldreq r0, [frame, #-8] @ get sp subeq r0, r0, #4 @ point at the last arg bleq .Ldumpstm @ dump saved registers 1004: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, ip, lr, pc} ldr r3, .Ldsi @ instruction exists, - teq r3, r1, lsr #10 + teq r3, r1, lsr #11 subeq r0, frame, #16 bleq .Ldumpstm @ dump saved registers @@ -128,11 +128,11 @@ beq 2f add r7, r7, #1 teq r7, #6 - moveq r7, #1 - moveq r1, #'\n' - movne r1, #' ' - ldr r3, [stack], #-4 - mov r2, reg + moveq r7, #0 + adr r3, .Lcr + addne r3, r3, #1 @ skip newline + ldr r2, [stack], #-4 + mov r1, reg adr r0, .Lfp bl printk 2: subs reg, reg, #1 @@ -142,11 +142,11 @@ blne printk ldmfd sp!, {instr, reg, stack, r7, pc} -.Lfp: .asciz "%cr%d:%08x" +.Lfp: .asciz " r%d:%08x%s" .Lcr: .asciz "\n" .Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n" .align -.Ldsi: .word 0xe92dd800 >> 10 @ stmfd sp!, {... fp, ip, lr, pc} - .word 0xe92d0000 >> 10 @ stmfd sp!, {} +.Ldsi: .word 0xe92dd800 >> 11 @ stmfd sp!, {... fp, ip, lr, pc} + .word 0xe92d0000 >> 11 @ stmfd sp!, {} #endif diff -Nur linux-3.10.30/arch/arm/mach-exynos/mach-exynos5-dt.c linux-3.10.30-cubox-i/arch/arm/mach-exynos/mach-exynos5-dt.c --- linux-3.10.30/arch/arm/mach-exynos/mach-exynos5-dt.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-exynos/mach-exynos5-dt.c 2014-03-08 20:32:54.000000000 +0100 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -23,11 +24,31 @@ #include "common.h" +static u64 dma_mask64 = DMA_BIT_MASK(64); + static void __init exynos5_dt_map_io(void) { exynos_init_io(NULL, 0); } +static int exynos5250_platform_notifier(struct notifier_block *nb, + unsigned long event, void *__dev) +{ + struct device *dev = __dev; + + if (event != BUS_NOTIFY_ADD_DEVICE) + return NOTIFY_DONE; + + dev->dma_mask = &dma_mask64; + dev->coherent_dma_mask = DMA_BIT_MASK(64); + + return NOTIFY_OK; +} + +static struct notifier_block exynos5250_platform_nb = { + .notifier_call = exynos5250_platform_notifier, +}; + static void __init exynos5_dt_machine_init(void) { struct device_node *i2c_np; @@ -52,6 +73,11 @@ } } + if (config_enabled(CONFIG_ARM_LPAE) && + of_machine_is_compatible("samsung,exynos5250")) + bus_register_notifier(&platform_bus_type, + &exynos5250_platform_nb); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff -Nur linux-3.10.30/arch/arm/mach-imx/Kconfig linux-3.10.30-cubox-i/arch/arm/mach-imx/Kconfig --- linux-3.10.30/arch/arm/mach-imx/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/Kconfig 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,6 @@ config ARCH_MXC bool "Freescale i.MX family" if ARCH_MULTI_V4_V5 || ARCH_MULTI_V6_V7 + select ARCH_HAS_RESET_CONTROLLER select ARCH_REQUIRE_GPIOLIB select ARM_PATCH_PHYS_VIRT select AUTO_ZRELADDR if !ZBOOT_ROM @@ -8,8 +9,11 @@ select GENERIC_ALLOCATOR select GENERIC_CLOCKEVENTS select GENERIC_IRQ_CHIP + select MIGHT_HAVE_CACHE_L2X0 if ARCH_MULTI_V6_V7 select MULTI_IRQ_HANDLER + select SOC_BUS select SPARSE_IRQ + select SRAM select USE_OF help Support for Freescale MXC/iMX-based family of processors @@ -73,7 +77,6 @@ config HAVE_IMX_SRC def_bool y if SMP - select ARCH_HAS_RESET_CONTROLLER config IMX_HAVE_IOMUX_V1 bool @@ -806,16 +809,54 @@ select HAVE_IMX_SRC select HAVE_SMP select MFD_SYSCON + select MIGHT_HAVE_PCI + select PCI_DOMAINS if PCI + select ARCH_SUPPORTS_MSI select PINCTRL select PINCTRL_IMX6Q select PL310_ERRATA_588369 if CACHE_PL310 select PL310_ERRATA_727915 if CACHE_PL310 select PL310_ERRATA_769419 if CACHE_PL310 select PM_OPP if PM + select ZONE_DMA help This enables support for Freescale i.MX6 Quad processor. +config SOC_IMX6SL + bool "i.MX6 SoloLite support" + select ARM_ERRATA_754322 + select ARM_ERRATA_775420 + select ARM_GIC + select CPU_V7 + select HAVE_IMX_ANATOP + select HAVE_IMX_GPC + select HAVE_IMX_MMDC + select HAVE_IMX_SRC + select PINCTRL + select PINCTRL_IMX6SL + select PL310_ERRATA_588369 if CACHE_PL310 + select PL310_ERRATA_727915 if CACHE_PL310 + select PL310_ERRATA_769419 if CACHE_PL310 + + help + This enables support for Freescale i.MX6 SoloLite processor. + +config SOC_VF610 + bool "Vybrid Family VF610 support" + select CPU_V7 + select ARM_GIC + select CLKSRC_OF + select PINCTRL + select PINCTRL_VF610 + select VF_PIT_TIMER + select PL310_ERRATA_588369 if CACHE_PL310 + select PL310_ERRATA_727915 if CACHE_PL310 + select PL310_ERRATA_769419 if CACHE_PL310 + + help + This enable support for Freescale Vybrid VF610 processor. + endif source "arch/arm/mach-imx/devices/Kconfig" diff -Nur linux-3.10.30/arch/arm/mach-imx/Makefile linux-3.10.30-cubox-i/arch/arm/mach-imx/Makefile --- linux-3.10.30/arch/arm/mach-imx/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/Makefile 2014-03-08 20:32:54.000000000 +0100 @@ -15,7 +15,8 @@ obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clk-imx51-imx53.o ehci-imx5.o $(imx5-pm-y) obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-gate2.o \ - clk-pfd.o clk-busy.o clk.o + clk-pfd.o clk-busy.o clk.o \ + clk-fixup-div.o clk-fixup-mux.o obj-$(CONFIG_IMX_HAVE_IOMUX_V1) += iomux-v1.o obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o @@ -30,6 +31,7 @@ ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o +obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o endif ifdef CONFIG_SND_IMX_SOC @@ -98,11 +100,20 @@ obj-$(CONFIG_SMP) += headsmp.o platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o +obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o -ifeq ($(CONFIG_PM),y) -obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o +AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a +obj-$(CONFIG_PM) += pm-imx6.o headsmp.o suspend-imx6.o + +ifeq ($(CONFIG_ARM_IMX6_CPUFREQ),y) +obj-y += busfreq-imx6.o +obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o +AFLAGS_lpddr2_freq_imx6.o := -march=armv7-a +AFLAGS_imx6sl_wfi.o := -march=armv7-a +obj-$(CONFIG_SOC_IMX6SL) += lpddr2_freq_imx6.o busfreq_lpddr2.o imx6sl_wfi.o endif + # i.MX5 based machines obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o obj-$(CONFIG_MACH_EUKREA_CPUIMX51SD) += mach-cpuimx51sd.o @@ -111,4 +122,6 @@ obj-$(CONFIG_MACH_IMX51_DT) += imx51-dt.o obj-$(CONFIG_SOC_IMX53) += mach-imx53.o +obj-$(CONFIG_SOC_VF610) += clk-vf610.o mach-vf610.o + obj-y += devices/ diff -Nur linux-3.10.30/arch/arm/mach-imx/anatop.c linux-3.10.30-cubox-i/arch/arm/mach-imx/anatop.c --- linux-3.10.30/arch/arm/mach-imx/anatop.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/anatop.c 2014-03-08 20:32:54.000000000 +0100 @@ -9,6 +9,7 @@ * http://www.gnu.org/copyleft/gpl.html */ +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include "common.h" +#include "hardware.h" #define REG_SET 0x4 #define REG_CLR 0x8 @@ -26,13 +28,20 @@ #define ANADIG_USB1_CHRG_DETECT 0x1b0 #define ANADIG_USB2_CHRG_DETECT 0x210 #define ANADIG_DIGPROG 0x260 +#define ANADIG_DIGPROG_IMX6SL 0x280 #define BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG 0x40000 +#define BM_ANADIG_REG_2P5_ENABLE_PULLDOWN 0x8 #define BM_ANADIG_REG_CORE_FET_ODRIVE 0x20000000 #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG 0x1000 +#define BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS 0x2000 #define BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B 0x80000 #define BM_ANADIG_USB_CHRG_DETECT_EN_B 0x100000 +#define ANADIG_REG_TARG_MASK 0x1f +#define ANADIG_REG1_TARG_SHIFT 9 /* VDDPU */ +#define ANADIG_REG2_TARG_SHIFT 18 /* VDDSOC */ + static struct regmap *anatop; static void imx_anatop_enable_weak2p5(bool enable) @@ -48,25 +57,48 @@ regmap_write(anatop, reg, BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG); } -static void imx_anatop_enable_fet_odrive(bool enable) +static inline void imx_anatop_enable_2p5_pulldown(bool enable) +{ + regmap_write(anatop, ANADIG_REG_2P5 + (enable ? REG_SET : REG_CLR), + BM_ANADIG_REG_2P5_ENABLE_PULLDOWN); +} + +static inline void imx_anatop_enable_fet_odrive(bool enable) { regmap_write(anatop, ANADIG_REG_CORE + (enable ? REG_SET : REG_CLR), BM_ANADIG_REG_CORE_FET_ODRIVE); } +static inline void imx_anatop_disconnect_high_snvs(bool enable) +{ + regmap_write(anatop, ANADIG_ANA_MISC0 + (enable ? REG_SET : REG_CLR), + BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS); +} + void imx_anatop_pre_suspend(void) { - imx_anatop_enable_weak2p5(true); + if (cpu_is_imx6sl()) { + imx_anatop_enable_2p5_pulldown(true); + imx_anatop_disconnect_high_snvs(true); + } else { + imx_anatop_enable_weak2p5(true); + } + imx_anatop_enable_fet_odrive(true); } void imx_anatop_post_resume(void) { imx_anatop_enable_fet_odrive(false); - imx_anatop_enable_weak2p5(false); + if (cpu_is_imx6sl()) { + imx_anatop_enable_2p5_pulldown(false); + imx_anatop_disconnect_high_snvs(false); + } else { + imx_anatop_enable_weak2p5(false); + } } -void imx_anatop_usb_chrg_detect_disable(void) +static void imx_anatop_usb_chrg_detect_disable(void) { regmap_write(anatop, ANADIG_USB1_CHRG_DETECT, BM_ANADIG_USB_CHRG_DETECT_EN_B @@ -76,21 +108,70 @@ BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B); } -u32 imx_anatop_get_digprog(void) +void imx_anatop_pu_enable(bool enable) +{ + u32 val; + + regmap_read(anatop, ANADIG_REG_CORE, &val); + val &= ANADIG_REG_TARG_MASK << ANADIG_REG2_TARG_SHIFT; + /* + * set pu regulator only in LDO_BYPASS mode(know by VDDSOC reg 0x1f), + * else handled by anatop regulator driver. + */ + if (((val >> (ANADIG_REG2_TARG_SHIFT)) & ANADIG_REG_TARG_MASK) + == ANADIG_REG_TARG_MASK) { + if (enable) { + regmap_write(anatop, ANADIG_REG_CORE + REG_SET, + ANADIG_REG_TARG_MASK << ANADIG_REG1_TARG_SHIFT); + udelay(70); /* bypass need 70us to be stable */ + } else { + regmap_write(anatop, ANADIG_REG_CORE + REG_CLR, + ANADIG_REG_TARG_MASK << ANADIG_REG1_TARG_SHIFT); + } + } +} +void __init imx_init_revision_from_anatop(void) { struct device_node *np; void __iomem *anatop_base; - static u32 digprog; - - if (digprog) - return digprog; + unsigned int revision; + u32 digprog; + u16 offset = ANADIG_DIGPROG; np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); anatop_base = of_iomap(np, 0); WARN_ON(!anatop_base); - digprog = readl_relaxed(anatop_base + ANADIG_DIGPROG); + if (of_device_is_compatible(np, "fsl,imx6sl-anatop")) + offset = ANADIG_DIGPROG_IMX6SL; + digprog = readl_relaxed(anatop_base + offset); + iounmap(anatop_base); + + switch (digprog & 0xff) { + case 0: + revision = IMX_CHIP_REVISION_1_0; + break; + case 1: + revision = IMX_CHIP_REVISION_1_1; + break; + case 2: + revision = IMX_CHIP_REVISION_1_2; + break; + case 3: + revision = IMX_CHIP_REVISION_1_3; + break; + case 4: + revision = IMX_CHIP_REVISION_1_4; + break; + case 5: + revision = IMX_CHIP_REVISION_1_5; + break; + default: + revision = IMX_CHIP_REVISION_UNKNOWN; + } - return digprog; + mxc_set_cpu_type(digprog >> 16 & 0xff); + mxc_set_system_rev(((digprog >> 16 & 0xff) << 12) | ((digprog & 0xff) + 0x10)); + imx_set_soc_revision(revision); } void __init imx_anatop_init(void) @@ -100,4 +181,6 @@ pr_err("%s: failed to find imx6q-anatop regmap!\n", __func__); return; } + + imx_anatop_usb_chrg_detect_disable(); } diff -Nur linux-3.10.30/arch/arm/mach-imx/busfreq-imx6.c linux-3.10.30-cubox-i/arch/arm/mach-imx/busfreq-imx6.c --- linux-3.10.30/arch/arm/mach-imx/busfreq-imx6.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/busfreq-imx6.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! + * @file busfreq-imx6.c + * + * @brief A common API for the Freescale Semiconductor iMX6 Busfreq API + * + * The APIs are for setting bus frequency to different values based on the + * highest freqeuncy requested. + * + * @ingroup PM + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hardware.h" + +#define LPAPM_CLK 24000000 +#define DDR3_AUDIO_CLK 50000000 +#define LPDDR2_AUDIO_CLK 100000000 + +int high_bus_freq_mode; +int med_bus_freq_mode; +int audio_bus_freq_mode; +int low_bus_freq_mode; +int ultra_low_bus_freq_mode; +unsigned int ddr_med_rate; +unsigned int ddr_normal_rate; + +static int bus_freq_scaling_initialized; +static struct device *busfreq_dev; +static int busfreq_suspended; +static u32 org_arm_rate; +static int bus_freq_scaling_is_active; +static int high_bus_count, med_bus_count, audio_bus_count, low_bus_count; +static unsigned int ddr_low_rate; + +extern int init_mmdc_lpddr2_settings(struct platform_device *dev); +extern int init_mmdc_ddr3_settings(struct platform_device *dev); +extern int update_ddr_freq(int ddr_rate); +extern int update_lpddr2_freq(int ddr_rate); + +DEFINE_MUTEX(bus_freq_mutex); +static DEFINE_SPINLOCK(freq_lock); + +static struct clk *pll2_400; +static struct clk *periph_clk; +static struct clk *periph_pre_clk; +static struct clk *periph_clk2_sel; +static struct clk *periph_clk2; +static struct clk *osc_clk; +static struct clk *cpu_clk; +static struct clk *pll3; +static struct clk *pll2; +static struct clk *pll2_200; +static struct clk *pll1_sys; +static struct clk *periph2_clk; +static struct clk *ocram_clk; +static struct clk *ahb_clk; +static struct clk *pll1_sw_clk; +static struct clk *periph2_pre_clk; +static struct clk *periph2_clk2_sel; +static struct clk *periph2_clk2; +static struct clk *step_clk; +static struct clk *axi_sel_clk; +static struct clk *pll3_pfd1_540m; + +static u32 pll2_org_rate; +static struct delayed_work low_bus_freq_handler; +static struct delayed_work bus_freq_daemon; + +static void enter_lpm_imx6sl(void) +{ + unsigned long flags; + + if (high_bus_freq_mode) { + pll2_org_rate = clk_get_rate(pll2); + /* Set periph_clk to be sourced from OSC_CLK */ + clk_set_parent(periph_clk2_sel, osc_clk); + clk_set_parent(periph_clk, periph_clk2); + /* Ensure AHB/AXI clks are at 24MHz. */ + clk_set_rate(ahb_clk, LPAPM_CLK); + clk_set_rate(ocram_clk, LPAPM_CLK); + } + if (audio_bus_count) { + /* Set AHB to 8MHz to lower pwer.*/ + clk_set_rate(ahb_clk, LPAPM_CLK / 3); + + /* Set up DDR to 100MHz. */ + spin_lock_irqsave(&freq_lock, flags); + update_lpddr2_freq(LPDDR2_AUDIO_CLK); + spin_unlock_irqrestore(&freq_lock, flags); + + /* Fix the clock tree in kernel */ + clk_set_rate(pll2, pll2_org_rate); + clk_set_parent(periph2_pre_clk, pll2_200); + clk_set_parent(periph2_clk, periph2_pre_clk); + + if (low_bus_freq_mode || ultra_low_bus_freq_mode) { + /* + * Swtich ARM to run off PLL2_PFD2_400MHz + * since DDR is anyway at 100MHz. + */ + clk_set_parent(step_clk, pll2_400); + clk_set_parent(pll1_sw_clk, step_clk); + /* + * Ensure that the clock will be + * at original speed. + */ + clk_set_rate(cpu_clk, org_arm_rate); + } + low_bus_freq_mode = 0; + ultra_low_bus_freq_mode = 0; + audio_bus_freq_mode = 1; + } else { + u32 arm_div, pll1_rate; + org_arm_rate = clk_get_rate(cpu_clk); + if (low_bus_freq_mode && low_bus_count == 0) { + /* + * We are already in DDR @ 24MHz state, but + * no one but ARM needs the DDR. In this case, + * we can lower the DDR freq to 1MHz when ARM + * enters WFI in this state. Keep track of this state. + */ + ultra_low_bus_freq_mode = 1; + low_bus_freq_mode = 0; + audio_bus_freq_mode = 0; + } else { + if (!ultra_low_bus_freq_mode && !low_bus_freq_mode) { + /* + * Set DDR to 24MHz. + * Since we are going to bypass PLL2, + * we need to move ARM clk off PLL2_PFD2 + * to PLL1. Make sure the PLL1 is running + * at the lowest possible freq. + */ + clk_set_rate(pll1_sys, + clk_round_rate(pll1_sys, org_arm_rate)); + pll1_rate = clk_get_rate(pll1_sys); + arm_div = pll1_rate / org_arm_rate + 1; + /* + * Ensure ARM CLK is lower before + * changing the parent. + */ + clk_set_rate(cpu_clk, org_arm_rate / arm_div); + /* Now set the ARM clk parent to PLL1_SYS. */ + clk_set_parent(pll1_sw_clk, pll1_sys); + + /* + * Set STEP_CLK back to OSC to save power and + * also to maintain the parent.The WFI iram code + * will switch step_clk to osc, but the clock API + * is not aware of the change and when a new request + * to change the step_clk parent to pll2_pfd2_400M + * is requested sometime later, the change is ignored. + */ + clk_set_parent(step_clk, osc_clk); + /* Now set DDR to 24MHz. */ + spin_lock_irqsave(&freq_lock, flags); + update_lpddr2_freq(LPAPM_CLK); + spin_unlock_irqrestore(&freq_lock, flags); + + /* + * Fix the clock tree in kernel. + * Make sure PLL2 rate is updated as it gets + * bypassed in the DDR freq change code. + */ + clk_set_rate(pll2, LPAPM_CLK); + clk_set_parent(periph2_clk2_sel, pll2); + clk_set_parent(periph2_clk, periph2_clk2_sel); + + } + if (low_bus_count == 0) { + ultra_low_bus_freq_mode = 1; + low_bus_freq_mode = 0; + } else { + ultra_low_bus_freq_mode = 0; + low_bus_freq_mode = 1; + } + audio_bus_freq_mode = 0; + } + } +} + +static void exit_lpm_imx6sl(void) +{ + unsigned long flags; + + spin_lock_irqsave(&freq_lock, flags); + /* Change DDR freq in IRAM. */ + update_lpddr2_freq(ddr_normal_rate); + spin_unlock_irqrestore(&freq_lock, flags); + + /* + * Fix the clock tree in kernel. + * Make sure PLL2 rate is updated as it gets + * un-bypassed in the DDR freq change code. + */ + clk_set_rate(pll2, pll2_org_rate); + clk_set_parent(periph2_pre_clk, pll2_400); + clk_set_parent(periph2_clk, periph2_pre_clk); + + /* Ensure that periph_clk is sourced from PLL2_400. */ + clk_set_parent(periph_pre_clk, pll2_400); + /* + * Before switching the perhiph_clk, ensure that the + * AHB/AXI will not be too fast. + */ + clk_set_rate(ahb_clk, LPAPM_CLK / 3); + clk_set_rate(ocram_clk, LPAPM_CLK / 2); + clk_set_parent(periph_clk, periph_pre_clk); + + if (low_bus_freq_mode || ultra_low_bus_freq_mode) { + /* Move ARM from PLL1_SW_CLK to PLL2_400. */ + clk_set_parent(step_clk, pll2_400); + clk_set_parent(pll1_sw_clk, step_clk); + clk_set_rate(cpu_clk, org_arm_rate); + ultra_low_bus_freq_mode = 0; + } +} + +int reduce_bus_freq(void) +{ + int ret = 0; + clk_prepare_enable(pll3); + if (cpu_is_imx6sl()) + enter_lpm_imx6sl(); + else { + if (cpu_is_imx6dl() && (clk_get_parent(axi_sel_clk) + != periph_clk)) + /* Set axi to periph_clk */ + clk_set_parent(axi_sel_clk, periph_clk); + + if (audio_bus_count) { + /* Need to ensure that PLL2_PFD_400M is kept ON. */ + clk_prepare_enable(pll2_400); + update_ddr_freq(DDR3_AUDIO_CLK); + /* Make sure periph clk's parent also got updated */ + ret = clk_set_parent(periph_clk2_sel, pll3); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + ret = clk_set_parent(periph_pre_clk, pll2_200); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + ret = clk_set_parent(periph_clk, periph_pre_clk); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + audio_bus_freq_mode = 1; + low_bus_freq_mode = 0; + } else { + update_ddr_freq(LPAPM_CLK); + /* Make sure periph clk's parent also got updated */ + ret = clk_set_parent(periph_clk2_sel, osc_clk); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + /* Set periph_clk parent to OSC via periph_clk2_sel */ + ret = clk_set_parent(periph_clk, periph_clk2); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + if (audio_bus_freq_mode) + clk_disable_unprepare(pll2_400); + low_bus_freq_mode = 1; + audio_bus_freq_mode = 0; + } + } + clk_disable_unprepare(pll3); + + med_bus_freq_mode = 0; + high_bus_freq_mode = 0; + + if (audio_bus_freq_mode) + dev_dbg(busfreq_dev, "Bus freq set to audio mode. Count:\ + high %d, med %d, audio %d\n", + high_bus_count, med_bus_count, audio_bus_count); + if (low_bus_freq_mode) + dev_dbg(busfreq_dev, "Bus freq set to low mode. Count:\ + high %d, med %d, audio %d\n", + high_bus_count, med_bus_count, audio_bus_count); + + return ret; +} + +static void reduce_bus_freq_handler(struct work_struct *work) +{ + mutex_lock(&bus_freq_mutex); + + reduce_bus_freq(); + + mutex_unlock(&bus_freq_mutex); +} + +/* + * Set the DDR, AHB to 24MHz. + * This mode will be activated only when none of the modules that + * need a higher DDR or AHB frequency are active. + */ +int set_low_bus_freq(void) +{ + if (busfreq_suspended) + return 0; + + if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) + return 0; + + /* + * Check to see if we need to got from + * low bus freq mode to audio bus freq mode. + * If so, the change needs to be done immediately. + */ + if (audio_bus_count && (low_bus_freq_mode || ultra_low_bus_freq_mode)) + reduce_bus_freq(); + else + /* + * Don't lower the frequency immediately. Instead + * scheduled a delayed work and drop the freq if + * the conditions still remain the same. + */ + schedule_delayed_work(&low_bus_freq_handler, + usecs_to_jiffies(3000000)); + return 0; +} + +/* + * Set the DDR to either 528MHz or 400MHz for iMX6qd + * or 400MHz for iMX6dl. + */ +int set_high_bus_freq(int high_bus_freq) +{ + int ret = 0; + struct clk *periph_clk_parent; + + if (bus_freq_scaling_initialized && bus_freq_scaling_is_active) + cancel_delayed_work_sync(&low_bus_freq_handler); + + if (busfreq_suspended) + return 0; + + if (cpu_is_imx6q()) + periph_clk_parent = pll2; + else + periph_clk_parent = pll2_400; + + if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) + return 0; + + if (high_bus_freq_mode) + return 0; + + /* medium bus freq is only supported for MX6DQ */ + if (med_bus_freq_mode && !high_bus_freq) + return 0; + + clk_prepare_enable(pll3); + if (cpu_is_imx6sl()) + exit_lpm_imx6sl(); + else { + if (high_bus_freq) { + update_ddr_freq(ddr_normal_rate); + /* Make sure periph clk's parent also got updated */ + ret = clk_set_parent(periph_clk2_sel, pll3); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + ret = clk_set_parent(periph_pre_clk, periph_clk_parent); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + ret = clk_set_parent(periph_clk, periph_pre_clk); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + if (cpu_is_imx6dl() && (clk_get_parent(axi_sel_clk) + != pll3_pfd1_540m)) + /* Set axi to pll3_pfd1_540m */ + clk_set_parent(axi_sel_clk, pll3_pfd1_540m); + } else { + update_ddr_freq(ddr_med_rate); + /* Make sure periph clk's parent also got updated */ + ret = clk_set_parent(periph_clk2_sel, pll3); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + ret = clk_set_parent(periph_pre_clk, pll2_400); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + ret = clk_set_parent(periph_clk, periph_pre_clk); + if (ret) + dev_WARN(busfreq_dev, + "%s: %d: clk set parent fail!\n", + __func__, __LINE__); + } + if (audio_bus_freq_mode) + clk_disable_unprepare(pll2_400); + } + + high_bus_freq_mode = 1; + med_bus_freq_mode = 0; + low_bus_freq_mode = 0; + audio_bus_freq_mode = 0; + + clk_disable_unprepare(pll3); + + if (high_bus_freq_mode) + dev_dbg(busfreq_dev, "Bus freq set to high mode. Count:\ + high %d, med %d, audio %d\n", + high_bus_count, med_bus_count, audio_bus_count); + if (med_bus_freq_mode) + dev_dbg(busfreq_dev, "Bus freq set to med mode. Count:\ + high %d, med %d, audio %d\n", + high_bus_count, med_bus_count, audio_bus_count); + + return 0; +} + +void request_bus_freq(enum bus_freq_mode mode) +{ + mutex_lock(&bus_freq_mutex); + + if (mode == BUS_FREQ_HIGH) + high_bus_count++; + else if (mode == BUS_FREQ_MED) + med_bus_count++; + else if (mode == BUS_FREQ_AUDIO) + audio_bus_count++; + else if (mode == BUS_FREQ_LOW) + low_bus_count++; + + if (busfreq_suspended || !bus_freq_scaling_initialized || + !bus_freq_scaling_is_active) { + mutex_unlock(&bus_freq_mutex); + return; + } + cancel_delayed_work_sync(&low_bus_freq_handler); + + if (cpu_is_imx6dl()) { + /* No support for medium setpoint on MX6DL. */ + if (mode == BUS_FREQ_MED) { + high_bus_count++; + mode = BUS_FREQ_HIGH; + } + } + + if ((mode == BUS_FREQ_HIGH) && (!high_bus_freq_mode)) { + set_high_bus_freq(1); + mutex_unlock(&bus_freq_mutex); + return; + } + + if ((mode == BUS_FREQ_MED) && (!high_bus_freq_mode) && + (!med_bus_freq_mode)) { + set_high_bus_freq(0); + mutex_unlock(&bus_freq_mutex); + return; + } + if ((mode == BUS_FREQ_AUDIO) && (!high_bus_freq_mode) && + (!med_bus_freq_mode) && (!audio_bus_freq_mode)) { + set_low_bus_freq(); + mutex_unlock(&bus_freq_mutex); + return; + } + mutex_unlock(&bus_freq_mutex); + return; +} +EXPORT_SYMBOL(request_bus_freq); + +void release_bus_freq(enum bus_freq_mode mode) +{ + mutex_lock(&bus_freq_mutex); + + if (mode == BUS_FREQ_HIGH) { + if (high_bus_count == 0) { + dev_err(busfreq_dev, "high bus count mismatch!\n"); + dump_stack(); + mutex_unlock(&bus_freq_mutex); + return; + } + high_bus_count--; + } else if (mode == BUS_FREQ_MED) { + if (med_bus_count == 0) { + dev_err(busfreq_dev, "med bus count mismatch!\n"); + dump_stack(); + mutex_unlock(&bus_freq_mutex); + return; + } + med_bus_count--; + } else if (mode == BUS_FREQ_AUDIO) { + if (audio_bus_count == 0) { + dev_err(busfreq_dev, "audio bus count mismatch!\n"); + dump_stack(); + mutex_unlock(&bus_freq_mutex); + return; + } + audio_bus_count--; + } else if (mode == BUS_FREQ_LOW) { + if (low_bus_count == 0) { + dev_err(busfreq_dev, "low bus count mismatch!\n"); + dump_stack(); + mutex_unlock(&bus_freq_mutex); + return; + } + low_bus_count--; + } + + if (busfreq_suspended || !bus_freq_scaling_initialized || + !bus_freq_scaling_is_active) { + mutex_unlock(&bus_freq_mutex); + return; + } + + if (cpu_is_imx6dl()) { + /* No support for medium setpoint on MX6DL. */ + if (mode == BUS_FREQ_MED) { + high_bus_count--; + mode = BUS_FREQ_HIGH; + } + } + + if ((!audio_bus_freq_mode) && (high_bus_count == 0) && + (med_bus_count == 0) && (audio_bus_count != 0)) { + set_low_bus_freq(); + mutex_unlock(&bus_freq_mutex); + return; + } + if ((!low_bus_freq_mode) && (high_bus_count == 0) && + (med_bus_count == 0) && (audio_bus_count == 0) && + (low_bus_count != 0)) { + set_low_bus_freq(); + mutex_unlock(&bus_freq_mutex); + return; + } + if ((!ultra_low_bus_freq_mode) && (high_bus_count == 0) && + (med_bus_count == 0) && (audio_bus_count == 0) && + (low_bus_count == 0)) { + set_low_bus_freq(); + mutex_unlock(&bus_freq_mutex); + return; + } + + mutex_unlock(&bus_freq_mutex); + return; +} +EXPORT_SYMBOL(release_bus_freq); + +static void bus_freq_daemon_handler(struct work_struct *work) +{ + mutex_lock(&bus_freq_mutex); + if ((!low_bus_freq_mode) && (high_bus_count == 0) && + (med_bus_count == 0) && (audio_bus_count == 0)) + set_low_bus_freq(); + mutex_unlock(&bus_freq_mutex); +} + +static ssize_t bus_freq_scaling_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (bus_freq_scaling_is_active) + return sprintf(buf, "Bus frequency scaling is enabled\n"); + else + return sprintf(buf, "Bus frequency scaling is disabled\n"); +} + +static ssize_t bus_freq_scaling_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + if (strncmp(buf, "1", 1) == 0) { + bus_freq_scaling_is_active = 1; + set_high_bus_freq(1); + /* + * We set bus freq to highest at the beginning, + * so we use this daemon thread to make sure system + * can enter low bus mode if + * there is no high bus request pending + */ + schedule_delayed_work(&bus_freq_daemon, + usecs_to_jiffies(5000000)); + } else if (strncmp(buf, "0", 1) == 0) { + if (bus_freq_scaling_is_active) + set_high_bus_freq(1); + bus_freq_scaling_is_active = 0; + } + return size; +} + +static int bus_freq_pm_notify(struct notifier_block *nb, unsigned long event, + void *dummy) +{ + mutex_lock(&bus_freq_mutex); + + if (event == PM_SUSPEND_PREPARE) { + high_bus_count++; + set_high_bus_freq(1); + busfreq_suspended = 1; + } else if (event == PM_POST_SUSPEND) { + busfreq_suspended = 0; + high_bus_count--; + schedule_delayed_work(&bus_freq_daemon, + usecs_to_jiffies(5000000)); + } + + mutex_unlock(&bus_freq_mutex); + + return NOTIFY_OK; +} + +static int busfreq_reboot_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + /* System is rebooting. Set the system into high_bus_freq_mode. */ + request_bus_freq(BUS_FREQ_HIGH); + + return 0; +} + +static struct notifier_block imx_bus_freq_pm_notifier = { + .notifier_call = bus_freq_pm_notify, +}; + +static struct notifier_block imx_busfreq_reboot_notifier = { + .notifier_call = busfreq_reboot_notifier_event, +}; + + +static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show, + bus_freq_scaling_enable_store); + +/*! + * This is the probe routine for the bus frequency driver. + * + * @param pdev The platform device structure + * + * @return The function returns 0 on success + * + */ + +static int busfreq_probe(struct platform_device *pdev) +{ + u32 err; + + busfreq_dev = &pdev->dev; + + pll2_400 = devm_clk_get(&pdev->dev, "pll2_pfd2_396m"); + if (IS_ERR(pll2_400)) { + dev_err(busfreq_dev, "%s: failed to get pll2_pfd2_396m\n", + __func__); + return PTR_ERR(pll2_400); + } + + pll2_200 = devm_clk_get(&pdev->dev, "pll2_198m"); + if (IS_ERR(pll2_200)) { + dev_err(busfreq_dev, "%s: failed to get pll2_198m\n", + __func__); + return PTR_ERR(pll2_200); + } + + pll2 = devm_clk_get(&pdev->dev, "pll2_bus"); + if (IS_ERR(pll2)) { + dev_err(busfreq_dev, "%s: failed to get pll2_bus\n", + __func__); + return PTR_ERR(pll2); + } + + cpu_clk = devm_clk_get(&pdev->dev, "arm"); + if (IS_ERR(cpu_clk)) { + dev_err(busfreq_dev, "%s: failed to get cpu_clk\n", + __func__); + return PTR_ERR(cpu_clk); + } + + pll3 = devm_clk_get(&pdev->dev, "pll3_usb_otg"); + if (IS_ERR(pll3)) { + dev_err(busfreq_dev, "%s: failed to get pll3_usb_otg\n", + __func__); + return PTR_ERR(pll3); + } + + periph_clk = devm_clk_get(&pdev->dev, "periph"); + if (IS_ERR(periph_clk)) { + dev_err(busfreq_dev, "%s: failed to get periph\n", + __func__); + return PTR_ERR(periph_clk); + } + + periph_pre_clk = devm_clk_get(&pdev->dev, "periph_pre"); + if (IS_ERR(periph_pre_clk)) { + dev_err(busfreq_dev, "%s: failed to get periph_pre\n", + __func__); + return PTR_ERR(periph_pre_clk); + } + + periph_clk2 = devm_clk_get(&pdev->dev, "periph_clk2"); + if (IS_ERR(periph_clk2)) { + dev_err(busfreq_dev, "%s: failed to get periph_clk2\n", + __func__); + return PTR_ERR(periph_clk2); + } + + periph_clk2_sel = devm_clk_get(&pdev->dev, "periph_clk2_sel"); + if (IS_ERR(periph_clk2_sel)) { + dev_err(busfreq_dev, "%s: failed to get periph_clk2_sel\n", + __func__); + return PTR_ERR(periph_clk2_sel); + } + + osc_clk = devm_clk_get(&pdev->dev, "osc"); + if (IS_ERR(osc_clk)) { + dev_err(busfreq_dev, "%s: failed to get osc_clk\n", + __func__); + return PTR_ERR(osc_clk); + } + + if (cpu_is_imx6dl()) { + axi_sel_clk = devm_clk_get(&pdev->dev, "axi_sel"); + if (IS_ERR(axi_sel_clk)) { + dev_err(busfreq_dev, "%s: failed to get axi_sel_clk\n", + __func__); + return PTR_ERR(axi_sel_clk); + } + + pll3_pfd1_540m = devm_clk_get(&pdev->dev, "pll3_pfd1_540m"); + if (IS_ERR(pll3_pfd1_540m)) { + dev_err(busfreq_dev, + "%s: failed to get pll3_pfd1_540m\n", __func__); + return PTR_ERR(pll3_pfd1_540m); + } + } + + if (cpu_is_imx6sl()) { + pll1_sys = devm_clk_get(&pdev->dev, "pll1_sys"); + if (IS_ERR(pll1_sys)) { + dev_err(busfreq_dev, "%s: failed to get pll1_sys\n", + __func__); + return PTR_ERR(pll1_sys); + } + + ahb_clk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(ahb_clk)) { + dev_err(busfreq_dev, "%s: failed to get ahb_clk\n", + __func__); + return PTR_ERR(ahb_clk); + } + + ocram_clk = devm_clk_get(&pdev->dev, "ocram"); + if (IS_ERR(ocram_clk)) { + dev_err(busfreq_dev, "%s: failed to get ocram_clk\n", + __func__); + return PTR_ERR(ocram_clk); + } + + pll1_sw_clk = devm_clk_get(&pdev->dev, "pll1_sw"); + if (IS_ERR(pll1_sw_clk)) { + dev_err(busfreq_dev, "%s: failed to get pll1_sw_clk\n", + __func__); + return PTR_ERR(pll1_sw_clk); + } + + periph2_clk = devm_clk_get(&pdev->dev, "periph2"); + if (IS_ERR(periph2_clk)) { + dev_err(busfreq_dev, "%s: failed to get periph2\n", + __func__); + return PTR_ERR(periph2_clk); + } + + periph2_pre_clk = devm_clk_get(&pdev->dev, "periph2_pre"); + if (IS_ERR(periph2_pre_clk)) { + dev_err(busfreq_dev, + "%s: failed to get periph2_pre_clk\n", + __func__); + return PTR_ERR(periph2_pre_clk); + } + + periph2_clk2 = devm_clk_get(&pdev->dev, "periph2_clk2"); + if (IS_ERR(periph2_clk2)) { + dev_err(busfreq_dev, + "%s: failed to get periph2_clk2\n", + __func__); + return PTR_ERR(periph2_clk2); + } + + periph2_clk2_sel = devm_clk_get(&pdev->dev, "periph2_clk2_sel"); + if (IS_ERR(periph2_clk2_sel)) { + dev_err(busfreq_dev, + "%s: failed to get periph2_clk2_sel\n", + __func__); + return PTR_ERR(periph2_clk2_sel); + } + + step_clk = devm_clk_get(&pdev->dev, "step"); + if (IS_ERR(step_clk)) { + dev_err(busfreq_dev, + "%s: failed to get step_clk\n", + __func__); + return PTR_ERR(periph2_clk2_sel); + } + + } + + err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr); + if (err) { + dev_err(busfreq_dev, + "Unable to register sysdev entry for BUSFREQ"); + return err; + } + + if (of_property_read_u32(pdev->dev.of_node, "fsl,max_ddr_freq", + &ddr_normal_rate)) { + dev_err(busfreq_dev, "max_ddr_freq entry missing\n"); + return -EINVAL; + } + + high_bus_freq_mode = 1; + med_bus_freq_mode = 0; + low_bus_freq_mode = 0; + audio_bus_freq_mode = 0; + ultra_low_bus_freq_mode = 0; + + bus_freq_scaling_is_active = 1; + bus_freq_scaling_initialized = 1; + + ddr_low_rate = LPAPM_CLK; + if (cpu_is_imx6q()) { + if (of_property_read_u32(pdev->dev.of_node, "fsl,med_ddr_freq", + &ddr_med_rate)) { + dev_info(busfreq_dev, + "DDR medium rate not supported.\n"); + ddr_med_rate = ddr_normal_rate; + } + } + + INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler); + INIT_DELAYED_WORK(&bus_freq_daemon, bus_freq_daemon_handler); + register_pm_notifier(&imx_bus_freq_pm_notifier); + register_reboot_notifier(&imx_busfreq_reboot_notifier); + + if (cpu_is_imx6sl()) + err = init_mmdc_lpddr2_settings(pdev); + else + err = init_mmdc_ddr3_settings(pdev); + if (err) { + dev_err(busfreq_dev, "Busfreq init of MMDC failed\n"); + return err; + } + return 0; +} + +static const struct of_device_id imx6_busfreq_ids[] = { + { .compatible = "fsl,imx6_busfreq", }, + { /* sentinel */ } +}; + +static struct platform_driver busfreq_driver = { + .driver = { + .name = "imx6_busfreq", + .owner = THIS_MODULE, + .of_match_table = imx6_busfreq_ids, + }, + .probe = busfreq_probe, +}; + +/*! + * Initialise the busfreq_driver. + * + * @return The function always returns 0. + */ + +static int __init busfreq_init(void) +{ + if (platform_driver_register(&busfreq_driver) != 0) + return -ENODEV; + + printk(KERN_INFO "Bus freq driver module loaded\n"); + + return 0; +} + +static void __exit busfreq_cleanup(void) +{ + sysfs_remove_file(&busfreq_dev->kobj, &dev_attr_enable.attr); + + /* Unregister the device structure */ + platform_driver_unregister(&busfreq_driver); + bus_freq_scaling_initialized = 0; +} + +module_init(busfreq_init); +module_exit(busfreq_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("BusFreq driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/arch/arm/mach-imx/busfreq_ddr3.c linux-3.10.30-cubox-i/arch/arm/mach-imx/busfreq_ddr3.c --- linux-3.10.30/arch/arm/mach-imx/busfreq_ddr3.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/busfreq_ddr3.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file busfreq_ddr3.c + * + * @brief iMX6 DDR3 frequency change specific file. + * + * @ingroup PM + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hardware.h" + +/* DDR settings */ +static unsigned long (*iram_ddr_settings)[2]; +static unsigned long (*normal_mmdc_settings)[2]; +static unsigned long (*iram_iomux_settings)[2]; + +static void __iomem *mmdc_base; +static void __iomem *iomux_base; +static void __iomem *ccm_base; +static void __iomem *l2_base; +static void __iomem *gic_dist_base; +static u32 *irqs_used; + +static void *ddr_freq_change_iram_base; +static int ddr_settings_size; +static int iomux_settings_size; +static volatile unsigned int cpus_in_wfe; +static volatile bool wait_for_ddr_freq_update; +static int curr_ddr_rate; + +void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings, + bool dll_mode, void *iomux_offsets) = NULL; + +extern unsigned int ddr_med_rate; +extern unsigned int ddr_normal_rate; +extern int low_bus_freq_mode; +extern int audio_bus_freq_mode; +extern void mx6_ddr3_freq_change(u32 freq, void *ddr_settings, + bool dll_mode, void *iomux_offsets); + +#define MIN_DLL_ON_FREQ 333000000 +#define MAX_DLL_OFF_FREQ 125000000 +#define DDR_FREQ_CHANGE_SIZE 0x2000 + +unsigned long ddr3_dll_mx6q[][2] = { + {0x0c, 0x0}, + {0x10, 0x0}, + {0x1C, 0x04088032}, + {0x1C, 0x0408803a}, + {0x1C, 0x08408030}, + {0x1C, 0x08408038}, + {0x818, 0x0}, +}; + +unsigned long ddr3_calibration[][2] = { + {0x83c, 0x0}, + {0x840, 0x0}, + {0x483c, 0x0}, + {0x4840, 0x0}, + {0x848, 0x0}, + {0x4848, 0x0}, + {0x850, 0x0}, + {0x4850, 0x0}, +}; + +unsigned long ddr3_dll_mx6dl[][2] = { + {0x0c, 0x0}, + {0x10, 0x0}, + {0x1C, 0x04008032}, + {0x1C, 0x0400803a}, + {0x1C, 0x07208030}, + {0x1C, 0x07208038}, + {0x818, 0x0}, +}; + +unsigned long iomux_offsets_mx6q[][2] = { + {0x5A8, 0x0}, + {0x5B0, 0x0}, + {0x524, 0x0}, + {0x51C, 0x0}, + {0x518, 0x0}, + {0x50C, 0x0}, + {0x5B8, 0x0}, + {0x5C0, 0x0}, +}; + +unsigned long iomux_offsets_mx6dl[][2] = { + {0x4BC, 0x0}, + {0x4C0, 0x0}, + {0x4C4, 0x0}, + {0x4C8, 0x0}, + {0x4CC, 0x0}, + {0x4D0, 0x0}, + {0x4D4, 0x0}, + {0x4D8, 0x0}, +}; + +unsigned long ddr3_400[][2] = { + {0x83c, 0x42490249}, + {0x840, 0x02470247}, + {0x483c, 0x42570257}, + {0x4840, 0x02400240}, + {0x848, 0x4039363C}, + {0x4848, 0x3A39333F}, + {0x850, 0x38414441}, + {0x4850, 0x472D4833} +}; + +int can_change_ddr_freq(void) +{ + return 1; +} + +/* + * each active core apart from the one changing + * the DDR frequency will execute this function. + * the rest of the cores have to remain in WFE + * state until the frequency is changed. + */ +irqreturn_t wait_in_wfe_irq(int irq, void *dev_id) +{ + u32 me = smp_processor_id(); + + *((char *)(&cpus_in_wfe) + (u8)me) = 0xff; + + while (wait_for_ddr_freq_update) + wfe(); + + *((char *)(&cpus_in_wfe) + (u8)me) = 0; + + return IRQ_HANDLED; +} + +/* change the DDR frequency. */ +int update_ddr_freq(int ddr_rate) +{ + int i, j; + unsigned int reg; + bool dll_off = false; + unsigned int online_cpus = 0; + int cpu = 0; + int me; + + if (!can_change_ddr_freq()) + return -1; + + if (ddr_rate == curr_ddr_rate) + return 0; + + printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate); + + if (low_bus_freq_mode || audio_bus_freq_mode) + dll_off = true; + + iram_ddr_settings[0][0] = ddr_settings_size; + iram_iomux_settings[0][0] = iomux_settings_size; + if (ddr_rate == ddr_med_rate && cpu_is_imx6q()) { + for (i = 0; i < ARRAY_SIZE(ddr3_dll_mx6q); i++) { + iram_ddr_settings[i + 1][0] = + normal_mmdc_settings[i][0]; + iram_ddr_settings[i + 1][1] = + normal_mmdc_settings[i][1]; + } + for (j = 0, i = ARRAY_SIZE(ddr3_dll_mx6q); + i < iram_ddr_settings[0][0]; j++, i++) { + iram_ddr_settings[i + 1][0] = + ddr3_400[j][0]; + iram_ddr_settings[i + 1][1] = + ddr3_400[j][1]; + } + } else if (ddr_rate == ddr_normal_rate) { + for (i = 0; i < iram_ddr_settings[0][0]; i++) { + iram_ddr_settings[i + 1][0] = + normal_mmdc_settings[i][0]; + iram_ddr_settings[i + 1][1] = + normal_mmdc_settings[i][1]; + } + } + + /* ensure that all Cores are in WFE. */ + local_irq_disable(); + + me = smp_processor_id(); + + *((char *)(&cpus_in_wfe) + (u8)me) = 0xff; + wait_for_ddr_freq_update = true; + for_each_online_cpu(cpu) { + *((char *)(&online_cpus) + (u8)cpu) = 0xff; + if (cpu != me) { + /* set the interrupt to be pending in the GIC. */ + reg = 1 << (irqs_used[cpu] % 32); + writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET + + (irqs_used[cpu] / 32) * 4); + } + } + while (cpus_in_wfe != online_cpus) + udelay(5); + + /* + * Flush the TLB, to ensure no TLB maintenance occurs + * when DDR is in self-refresh. + */ + local_flush_tlb_all(); + /* Now we can change the DDR frequency. */ + mx6_change_ddr_freq(ddr_rate, iram_ddr_settings, + dll_off, iram_iomux_settings); + + curr_ddr_rate = ddr_rate; + + /* DDR frequency change is done . */ + wait_for_ddr_freq_update = false; + + /* wake up all the cores. */ + sev(); + + *((char *)(&cpus_in_wfe) + (u8)me) = 0; + + local_irq_enable(); + + printk(KERN_DEBUG "Bus freq set to %d done!\n", ddr_rate); + + return 0; +} + +int init_mmdc_ddr3_settings(struct platform_device *busfreq_pdev) +{ + struct device *dev = &busfreq_pdev->dev; + struct platform_device *ocram_dev; + unsigned int iram_paddr; + int i, err; + u32 cpu; + struct device_node *node; + struct gen_pool *iram_pool; + void *iram_addr; + + node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-mmdc-combine"); + if (!node) { + printk(KERN_ERR "failed to find imx6q-mmdc device tree data!\n"); + return -EINVAL; + } + mmdc_base = of_iomap(node, 0); + WARN(!mmdc_base, "unable to map mmdc registers\n"); + + node = NULL; + if (cpu_is_imx6q()) + node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-iomuxc"); + if (cpu_is_imx6dl()) + node = of_find_compatible_node(NULL, NULL, + "fsl,imx6dl-iomuxc"); + if (!node) { + printk(KERN_ERR "failed to find imx6q-iomux device tree data!\n"); + return -EINVAL; + } + iomux_base = of_iomap(node, 0); + WARN(!iomux_base, "unable to map iomux registers\n"); + + node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ccm"); + if (!node) { + printk(KERN_ERR "failed to find imx6q-ccm device tree data!\n"); + return -EINVAL; + } + ccm_base = of_iomap(node, 0); + WARN(!ccm_base, "unable to map mmdc registers\n"); + + node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); + if (!node) { + printk(KERN_ERR "failed to find imx6q-pl310-cache device tree data!\n"); + return -EINVAL; + } + l2_base = of_iomap(node, 0); + WARN(!ccm_base, "unable to map mmdc registers\n"); + + node = NULL; + node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic"); + if (!node) { + printk(KERN_ERR "failed to find imx6q-a9-gic device tree data!\n"); + return -EINVAL; + } + gic_dist_base = of_iomap(node, 0); + WARN(!gic_dist_base, "unable to map gic dist registers\n"); + + if (cpu_is_imx6q()) + ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6q) + + ARRAY_SIZE(ddr3_calibration); + if (cpu_is_imx6dl()) + ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6dl) + + ARRAY_SIZE(ddr3_calibration); + + normal_mmdc_settings = kmalloc((ddr_settings_size * 8), GFP_KERNEL); + if (cpu_is_imx6q()) { + memcpy(normal_mmdc_settings, ddr3_dll_mx6q, + sizeof(ddr3_dll_mx6q)); + memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6q)), + ddr3_calibration, sizeof(ddr3_calibration)); + } + if (cpu_is_imx6dl()) { + memcpy(normal_mmdc_settings, ddr3_dll_mx6dl, + sizeof(ddr3_dll_mx6dl)); + memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6dl)), + ddr3_calibration, sizeof(ddr3_calibration)); + } + /* store the original DDR settings at boot. */ + for (i = 0; i < ddr_settings_size; i++) { + /* + * writes via command mode register cannot be read back. + * hence hardcode them in the initial static array. + * this may require modification on a per customer basis. + */ + if (normal_mmdc_settings[i][0] != 0x1C) + normal_mmdc_settings[i][1] = + readl_relaxed(mmdc_base + + normal_mmdc_settings[i][0]); + } + + irqs_used = devm_kzalloc(dev, sizeof(u32) * num_present_cpus(), + GFP_KERNEL); + + for_each_online_cpu(cpu) { + int irq; + + /* + * set up a reserved interrupt to get all + * the active cores into a WFE state + * before changing the DDR frequency. + */ + irq = platform_get_irq(busfreq_pdev, cpu); + err = request_irq(irq, wait_in_wfe_irq, + IRQF_PERCPU, "mmdc_1", NULL); + if (err) { + dev_err(dev, + "Busfreq:request_irq failed %d, err = %d\n", + irq, err); + return err; + } + err = irq_set_affinity(irq, cpumask_of(cpu)); + if (err) { + dev_err(dev, + "Busfreq: Cannot set irq affinity irq=%d,\n", + irq); + return err; + } + irqs_used[cpu] = irq; + } + + node = NULL; + node = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!node) { + dev_err(dev, "%s: failed to find ocram node\n", + __func__); + return -EINVAL; + } + + ocram_dev = of_find_device_by_node(node); + if (!ocram_dev) { + dev_err(dev, "failed to find ocram device!\n"); + return -EINVAL; + } + + iram_pool = dev_get_gen_pool(&ocram_dev->dev); + if (!iram_pool) { + dev_err(dev, "iram pool unavailable!\n"); + return -EINVAL; + } + + iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6q); + iram_addr = (void *)gen_pool_alloc(iram_pool, + (iomux_settings_size * 8) + 8); + iram_iomux_settings = iram_addr; + if (!iram_iomux_settings) { + dev_err(dev, "unable to alloc iram for IOMUX settings!\n"); + return -ENOMEM; + } + + /* + * Allocate extra space to store the number of entries in the + * ddr_settings plus 4 extra regsiter information that needs + * to be passed to the frequency change code. + * sizeof(iram_ddr_settings) = sizeof(ddr_settings) + + * entries in ddr_settings + 16. + * The last 4 enties store the addresses of the registers: + * CCM_BASE_ADDR + * MMDC_BASE_ADDR + * IOMUX_BASE_ADDR + * L2X0_BASE_ADDR + */ + iram_addr = (void *)gen_pool_alloc(iram_pool, + (ddr_settings_size * 8) + 8 + 32); + iram_ddr_settings = iram_addr; + if (!iram_ddr_settings) { + dev_err(dev, "unable to alloc iram for ddr settings!\n"); + return -ENOMEM; + } + + i = ddr_settings_size + 1; + iram_ddr_settings[i][0] = (unsigned long)mmdc_base; + iram_ddr_settings[i+1][0] = (unsigned long)ccm_base; + iram_ddr_settings[i+2][0] = (unsigned long)iomux_base; + iram_ddr_settings[i+3][0] = (unsigned long)l2_base; + + if (cpu_is_imx6q()) { + /* store the IOMUX settings at boot. */ + for (i = 0; i < iomux_settings_size; i++) { + iomux_offsets_mx6q[i][1] = + readl_relaxed(iomux_base + + iomux_offsets_mx6q[i][0]); + iram_iomux_settings[i+1][0] = iomux_offsets_mx6q[i][0]; + iram_iomux_settings[i+1][1] = iomux_offsets_mx6q[i][1]; + } + } + + if (cpu_is_imx6dl()) { + for (i = 0; i < iomux_settings_size; i++) { + iomux_offsets_mx6dl[i][1] = + readl_relaxed(iomux_base + + iomux_offsets_mx6dl[i][0]); + iram_iomux_settings[i+1][0] = iomux_offsets_mx6dl[i][0]; + iram_iomux_settings[i+1][1] = iomux_offsets_mx6dl[i][1]; + } + } + + ddr_freq_change_iram_base = (void *)gen_pool_alloc(iram_pool, + DDR_FREQ_CHANGE_SIZE); + if (!ddr_freq_change_iram_base) { + dev_err(dev, "Cannot alloc iram for ddr freq change code!\n"); + return -ENOMEM; + } + + iram_paddr = gen_pool_virt_to_phys(iram_pool, + (unsigned long)ddr_freq_change_iram_base); + /* + * Need to remap the area here since we want + * the memory region to be executable. + */ + ddr_freq_change_iram_base = __arm_ioremap(iram_paddr, + DDR_FREQ_CHANGE_SIZE, + MT_MEMORY_NONCACHED); + mx6_change_ddr_freq = (void *)fncpy(ddr_freq_change_iram_base, + &mx6_ddr3_freq_change, DDR_FREQ_CHANGE_SIZE); + + curr_ddr_rate = ddr_normal_rate; + + return 0; +} diff -Nur linux-3.10.30/arch/arm/mach-imx/busfreq_lpddr2.c linux-3.10.30-cubox-i/arch/arm/mach-imx/busfreq_lpddr2.c --- linux-3.10.30/arch/arm/mach-imx/busfreq_lpddr2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/busfreq_lpddr2.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file busfreq_lpddr2.c + * + * @brief iMX6 LPDDR2 frequency change specific file. + * + * @ingroup PM + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hardware.h" + +/* DDR settings */ +static void __iomem *mmdc_base; +static void __iomem *anatop_base; +static void __iomem *ccm_base; +static void __iomem *l2_base; +static struct device *busfreq_dev; +static void *ddr_freq_change_iram_base; +static int curr_ddr_rate; + +unsigned long reg_addrs[4]; + +void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode, + void *iram_addr) = NULL; + +extern unsigned int ddr_normal_rate; +extern int low_bus_freq_mode; +extern int ultra_low_bus_freq_mode; +extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode, + void *iram_addr); + + +#define LPDDR2_FREQ_CHANGE_SIZE 0x1000 + + +/* change the DDR frequency. */ +int update_lpddr2_freq(int ddr_rate) +{ + if (ddr_rate == curr_ddr_rate) + return 0; + + dev_dbg(busfreq_dev, "\nBus freq set to %d start...\n", ddr_rate); + + /* + * Flush the TLB, to ensure no TLB maintenance occurs + * when DDR is in self-refresh. + */ + local_flush_tlb_all(); + /* Now change DDR frequency. */ + mx6_change_lpddr2_freq(ddr_rate, + (low_bus_freq_mode | ultra_low_bus_freq_mode), + reg_addrs); + + curr_ddr_rate = ddr_rate; + + dev_dbg(busfreq_dev, "\nBus freq set to %d done...\n", ddr_rate); + + return 0; +} + +int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev) +{ + struct platform_device *ocram_dev; + unsigned int iram_paddr; + struct device_node *node; + struct gen_pool *iram_pool; + + busfreq_dev = &busfreq_pdev->dev; + node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-mmdc"); + if (!node) { + printk(KERN_ERR "failed to find imx6sl-mmdc device tree data!\n"); + return -EINVAL; + } + mmdc_base = of_iomap(node, 0); + WARN(!mmdc_base, "unable to map mmdc registers\n"); + + node = NULL; + node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-ccm"); + if (!node) { + printk(KERN_ERR "failed to find imx6sl-ccm device tree data!\n"); + return -EINVAL; + } + ccm_base = of_iomap(node, 0); + WARN(!ccm_base, "unable to map ccm registers\n"); + + node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); + if (!node) { + printk(KERN_ERR "failed to find imx6sl-pl310-cache device tree data!\n"); + return -EINVAL; + } + l2_base = of_iomap(node, 0); + WARN(!l2_base, "unable to map PL310 registers\n"); + + node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop"); + if (!node) { + printk(KERN_ERR "failed to find imx6sl-pl310-cache device tree data!\n"); + return -EINVAL; + } + anatop_base = of_iomap(node, 0); + WARN(!anatop_base, "unable to map anatop registers\n"); + + node = NULL; + node = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!node) { + dev_err(busfreq_dev, "%s: failed to find ocram node\n", + __func__); + return -EINVAL; + } + + ocram_dev = of_find_device_by_node(node); + if (!ocram_dev) { + dev_err(busfreq_dev, "failed to find ocram device!\n"); + return -EINVAL; + } + + iram_pool = dev_get_gen_pool(&ocram_dev->dev); + if (!iram_pool) { + dev_err(busfreq_dev, "iram pool unavailable!\n"); + return -EINVAL; + } + + reg_addrs[0] = (unsigned long)anatop_base; + reg_addrs[1] = (unsigned long)ccm_base; + reg_addrs[2] = (unsigned long)mmdc_base; + reg_addrs[3] = (unsigned long)l2_base; + + ddr_freq_change_iram_base = (void *)gen_pool_alloc(iram_pool, + LPDDR2_FREQ_CHANGE_SIZE); + if (!ddr_freq_change_iram_base) { + dev_err(busfreq_dev, + "Cannot alloc iram for ddr freq change code!\n"); + return -ENOMEM; + } + + iram_paddr = gen_pool_virt_to_phys(iram_pool, + (unsigned long)ddr_freq_change_iram_base); + /* + * Need to remap the area here since we want + * the memory region to be executable. + */ + ddr_freq_change_iram_base = __arm_ioremap(iram_paddr, + LPDDR2_FREQ_CHANGE_SIZE, + MT_MEMORY_NONCACHED); + mx6_change_lpddr2_freq = (void *)fncpy(ddr_freq_change_iram_base, + &mx6_lpddr2_freq_change, LPDDR2_FREQ_CHANGE_SIZE); + + curr_ddr_rate = ddr_normal_rate; + + return 0; +} diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-fixup-div.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-fixup-div.c --- linux-3.10.30/arch/arm/mach-imx/clk-fixup-div.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-fixup-div.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include "clk.h" + +#define to_clk_div(_hw) container_of(_hw, struct clk_divider, hw) +#define div_mask(d) ((1 << (d->width)) - 1) + +/** + * struct clk_fixup_div - imx integer fixup divider clock + * @divider: the parent class + * @ops: pointer to clk_ops of parent class + * @fixup: a hook to fixup the write value + * + * The imx fixup divider clock is a subclass of basic clk_divider + * with an addtional fixup hook. + */ +struct clk_fixup_div { + struct clk_divider divider; + const struct clk_ops *ops; + void (*fixup)(u32 *val); +}; + +static inline struct clk_fixup_div *to_clk_fixup_div(struct clk_hw *hw) +{ + struct clk_divider *divider = to_clk_div(hw); + + return container_of(divider, struct clk_fixup_div, divider); +} + +static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); + + return fixup_div->ops->recalc_rate(&fixup_div->divider.hw, parent_rate); +} + +static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); + + return fixup_div->ops->round_rate(&fixup_div->divider.hw, rate, prate); +} + +static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); + struct clk_divider *div = to_clk_div(hw); + unsigned int divider, value; + unsigned long flags = 0; + u32 val; + + divider = parent_rate / rate; + + /* Zero based divider */ + value = divider - 1; + + if (value > div_mask(div)) + value = div_mask(div); + + spin_lock_irqsave(div->lock, flags); + + val = readl(div->reg); + val &= ~(div_mask(div) << div->shift); + val |= value << div->shift; + fixup_div->fixup(&val); + writel(val, div->reg); + + spin_unlock_irqrestore(div->lock, flags); + + return 0; +} + +static const struct clk_ops clk_fixup_div_ops = { + .recalc_rate = clk_fixup_div_recalc_rate, + .round_rate = clk_fixup_div_round_rate, + .set_rate = clk_fixup_div_set_rate, +}; + +struct clk *imx_clk_fixup_divider(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width, + void (*fixup)(u32 *val)) +{ + struct clk_fixup_div *fixup_div; + struct clk *clk; + struct clk_init_data init; + + if (!fixup) + return ERR_PTR(-EINVAL); + + fixup_div = kzalloc(sizeof(*fixup_div), GFP_KERNEL); + if (!fixup_div) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_fixup_div_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent ? &parent : NULL; + init.num_parents = parent ? 1 : 0; + + fixup_div->divider.reg = reg; + fixup_div->divider.shift = shift; + fixup_div->divider.width = width; + fixup_div->divider.lock = &imx_ccm_lock; + fixup_div->divider.hw.init = &init; + fixup_div->ops = &clk_divider_ops; + fixup_div->fixup = fixup; + + clk = clk_register(NULL, &fixup_div->divider.hw); + if (IS_ERR(clk)) + kfree(fixup_div); + + return clk; +} diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-fixup-mux.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-fixup-mux.c --- linux-3.10.30/arch/arm/mach-imx/clk-fixup-mux.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-fixup-mux.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include "clk.h" + +#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) + +/** + * struct clk_fixup_mux - imx integer fixup multiplexer clock + * @mux: the parent class + * @ops: pointer to clk_ops of parent class + * @fixup: a hook to fixup the write value + * + * The imx fixup multiplexer clock is a subclass of basic clk_mux + * with an addtional fixup hook. + */ +struct clk_fixup_mux { + struct clk_mux mux; + const struct clk_ops *ops; + void (*fixup)(u32 *val); +}; + +static inline struct clk_fixup_mux *to_clk_fixup_mux(struct clk_hw *hw) +{ + struct clk_mux *mux = to_clk_mux(hw); + + return container_of(mux, struct clk_fixup_mux, mux); +} + +static u8 clk_fixup_mux_get_parent(struct clk_hw *hw) +{ + struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw); + + return fixup_mux->ops->get_parent(&fixup_mux->mux.hw); +} + +static int clk_fixup_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw); + struct clk_mux *mux = to_clk_mux(hw); + unsigned long flags = 0; + u32 val; + + spin_lock_irqsave(mux->lock, flags); + + val = readl(mux->reg); + val &= ~(mux->mask << mux->shift); + val |= index << mux->shift; + fixup_mux->fixup(&val); + writel(val, mux->reg); + + spin_unlock_irqrestore(mux->lock, flags); + + return 0; +} + +static const struct clk_ops clk_fixup_mux_ops = { + .get_parent = clk_fixup_mux_get_parent, + .set_parent = clk_fixup_mux_set_parent, +}; + +struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char **parents, + int num_parents, void (*fixup)(u32 *val)) +{ + struct clk_fixup_mux *fixup_mux; + struct clk *clk; + struct clk_init_data init; + + if (!fixup) + return ERR_PTR(-EINVAL); + + fixup_mux = kzalloc(sizeof(*fixup_mux), GFP_KERNEL); + if (!fixup_mux) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_fixup_mux_ops; + init.parent_names = parents; + init.num_parents = num_parents; + + fixup_mux->mux.reg = reg; + fixup_mux->mux.shift = shift; + fixup_mux->mux.mask = BIT(width) - 1; + fixup_mux->mux.lock = &imx_ccm_lock; + fixup_mux->mux.hw.init = &init; + fixup_mux->ops = &clk_mux_ops; + fixup_mux->fixup = fixup; + + clk = clk_register(NULL, &fixup_mux->mux.hw); + if (IS_ERR(clk)) + kfree(fixup_mux); + + return clk; +} diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-gate2.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-gate2.c --- linux-3.10.30/arch/arm/mach-imx/clk-gate2.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-gate2.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,6 +1,7 @@ /* * Copyright (C) 2010-2011 Canonical Ltd * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd + * Copyright (C) 2013 Freescale Semiconductor, 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 @@ -72,7 +73,7 @@ reg = readl(gate->reg); - if (((reg >> gate->bit_idx) & 3) == 3) + if (((reg >> gate->bit_idx) & 1) == 1) return 1; return 0; diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-imx51-imx53.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-imx51-imx53.c --- linux-3.10.30/arch/arm/mach-imx/clk-imx51-imx53.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-imx51-imx53.c 2014-03-08 20:32:54.000000000 +0100 @@ -123,11 +123,13 @@ { int i; + of_clk_init(NULL); + clk[dummy] = imx_clk_fixed("dummy", 0); - clk[ckil] = imx_clk_fixed("ckil", rate_ckil); - clk[osc] = imx_clk_fixed("osc", rate_osc); - clk[ckih1] = imx_clk_fixed("ckih1", rate_ckih1); - clk[ckih2] = imx_clk_fixed("ckih2", rate_ckih2); + clk[ckil] = imx_obtain_fixed_clock("ckil", rate_ckil); + clk[osc] = imx_obtain_fixed_clock("osc", rate_osc); + clk[ckih1] = imx_obtain_fixed_clock("ckih1", rate_ckih1); + clk[ckih2] = imx_obtain_fixed_clock("ckih2", rate_ckih2); clk[lp_apm] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 9, 1, lp_apm_sel, ARRAY_SIZE(lp_apm_sel)); @@ -542,42 +544,12 @@ return 0; } -#ifdef CONFIG_OF -static void __init clk_get_freq_dt(unsigned long *ckil, unsigned long *osc, - unsigned long *ckih1, unsigned long *ckih2) -{ - struct device_node *np; - - /* retrieve the freqency of fixed clocks from device tree */ - for_each_compatible_node(np, NULL, "fixed-clock") { - u32 rate; - if (of_property_read_u32(np, "clock-frequency", &rate)) - continue; - - if (of_device_is_compatible(np, "fsl,imx-ckil")) - *ckil = rate; - else if (of_device_is_compatible(np, "fsl,imx-osc")) - *osc = rate; - else if (of_device_is_compatible(np, "fsl,imx-ckih1")) - *ckih1 = rate; - else if (of_device_is_compatible(np, "fsl,imx-ckih2")) - *ckih2 = rate; - } -} - int __init mx51_clocks_init_dt(void) { - unsigned long ckil, osc, ckih1, ckih2; - - clk_get_freq_dt(&ckil, &osc, &ckih1, &ckih2); - return mx51_clocks_init(ckil, osc, ckih1, ckih2); + return mx51_clocks_init(0, 0, 0, 0); } int __init mx53_clocks_init_dt(void) { - unsigned long ckil, osc, ckih1, ckih2; - - clk_get_freq_dt(&ckil, &osc, &ckih1, &ckih2); - return mx53_clocks_init(ckil, osc, ckih1, ckih2); + return mx53_clocks_init(0, 0, 0, 0); } -#endif diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-imx6q.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-imx6q.c --- linux-3.10.30/arch/arm/mach-imx/clk-imx6q.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-imx6q.c 2014-03-08 20:32:54.000000000 +0100 @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -25,154 +24,7 @@ #include "common.h" #include "hardware.h" -#define CCR 0x0 -#define BM_CCR_WB_COUNT (0x7 << 16) -#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21) -#define BM_CCR_RBC_EN (0x1 << 27) - -#define CCGR0 0x68 -#define CCGR1 0x6c -#define CCGR2 0x70 -#define CCGR3 0x74 -#define CCGR4 0x78 -#define CCGR5 0x7c -#define CCGR6 0x80 -#define CCGR7 0x84 - -#define CLPCR 0x54 -#define BP_CLPCR_LPM 0 -#define BM_CLPCR_LPM (0x3 << 0) -#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2) -#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) -#define BM_CLPCR_SBYOS (0x1 << 6) -#define BM_CLPCR_DIS_REF_OSC (0x1 << 7) -#define BM_CLPCR_VSTBY (0x1 << 8) -#define BP_CLPCR_STBY_COUNT 9 -#define BM_CLPCR_STBY_COUNT (0x3 << 9) -#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11) -#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16) -#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17) -#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19) -#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21) -#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22) -#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23) -#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24) -#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25) -#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26) -#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27) - -#define CGPR 0x64 -#define BM_CGPR_CHICKEN_BIT (0x1 << 17) - -static void __iomem *ccm_base; - -void imx6q_set_chicken_bit(void) -{ - u32 val = readl_relaxed(ccm_base + CGPR); - - val |= BM_CGPR_CHICKEN_BIT; - writel_relaxed(val, ccm_base + CGPR); -} - -static void imx6q_enable_rbc(bool enable) -{ - u32 val; - static bool last_rbc_mode; - - if (last_rbc_mode == enable) - return; - /* - * need to mask all interrupts in GPC before - * operating RBC configurations - */ - imx_gpc_mask_all(); - - /* configure RBC enable bit */ - val = readl_relaxed(ccm_base + CCR); - val &= ~BM_CCR_RBC_EN; - val |= enable ? BM_CCR_RBC_EN : 0; - writel_relaxed(val, ccm_base + CCR); - - /* configure RBC count */ - val = readl_relaxed(ccm_base + CCR); - val &= ~BM_CCR_RBC_BYPASS_COUNT; - val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0; - writel(val, ccm_base + CCR); - - /* - * need to delay at least 2 cycles of CKIL(32K) - * due to hardware design requirement, which is - * ~61us, here we use 65us for safe - */ - udelay(65); - - /* restore GPC interrupt mask settings */ - imx_gpc_restore_all(); - - last_rbc_mode = enable; -} - -static void imx6q_enable_wb(bool enable) -{ - u32 val; - static bool last_wb_mode; - - if (last_wb_mode == enable) - return; - - /* configure well bias enable bit */ - val = readl_relaxed(ccm_base + CLPCR); - val &= ~BM_CLPCR_WB_PER_AT_LPM; - val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0; - writel_relaxed(val, ccm_base + CLPCR); - - /* configure well bias count */ - val = readl_relaxed(ccm_base + CCR); - val &= ~BM_CCR_WB_COUNT; - val |= enable ? BM_CCR_WB_COUNT : 0; - writel_relaxed(val, ccm_base + CCR); - - last_wb_mode = enable; -} - -int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) -{ - u32 val = readl_relaxed(ccm_base + CLPCR); - - val &= ~BM_CLPCR_LPM; - switch (mode) { - case WAIT_CLOCKED: - imx6q_enable_wb(false); - imx6q_enable_rbc(false); - break; - case WAIT_UNCLOCKED: - val |= 0x1 << BP_CLPCR_LPM; - val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; - break; - case STOP_POWER_ON: - val |= 0x2 << BP_CLPCR_LPM; - break; - case WAIT_UNCLOCKED_POWER_OFF: - val |= 0x1 << BP_CLPCR_LPM; - val &= ~BM_CLPCR_VSTBY; - val &= ~BM_CLPCR_SBYOS; - break; - case STOP_POWER_OFF: - val |= 0x2 << BP_CLPCR_LPM; - val |= 0x3 << BP_CLPCR_STBY_COUNT; - val |= BM_CLPCR_VSTBY; - val |= BM_CLPCR_SBYOS; - imx6q_enable_wb(true); - imx6q_enable_rbc(true); - break; - default: - return -EINVAL; - } - - writel_relaxed(val, ccm_base + CLPCR); - - return 0; -} +#define CCM_CCGR_OFFSET(index) (index * 2) static const char *step_sels[] = { "osc", "pll2_pfd2_396m", }; static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; @@ -182,13 +34,15 @@ static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; static const char *axi_sels[] = { "periph", "pll2_pfd2_396m", "periph", "pll3_pfd1_540m", }; -static const char *audio_sels[] = { "pll4_post_div", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", }; +static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", }; static const char *gpu_axi_sels[] = { "axi", "ahb", }; static const char *gpu2d_core_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", }; static const char *gpu3d_core_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd2_396m", }; static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll3_pfd0_720m", }; static const char *ipu_sels[] = { "mmdc_ch0_axi", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; static const char *ldb_di_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_usb_otg", }; +static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", }; +static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", }; static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", }; static const char *ipu1_di0_sels[] = { "ipu1_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; static const char *ipu1_di1_sels[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; @@ -196,15 +50,29 @@ static const char *ipu2_di1_sels[] = { "ipu2_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; static const char *hsi_tx_sels[] = { "pll3_120m", "pll2_pfd2_396m", }; static const char *pcie_axi_sels[] = { "axi", "ahb", }; -static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_post_div", }; +static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_audio_div", }; static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; static const char *enfc_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", }; -static const char *emi_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *emi_sels[] = { "pll2_pfd2_396m", "pll3_usb_otg", "axi", "pll2_pfd0_352m", }; +static const char *emi_slow_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", }; static const char *vdo_axi_sels[] = { "axi", "ahb", }; static const char *vpu_axi_sels[] = { "axi", "pll2_pfd2_396m", "pll2_pfd0_352m", }; static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div", "dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0", - "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_post_div", }; + "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio_div", }; +static const char *cko2_sels[] = { + "mmdc_ch0_axi", "mmdc_ch1_axi", "usdhc4", "usdhc1", + "gpu2d_axi", "dummy", "ecspi_root", "gpu3d_axi", + "usdhc3", "dummy", "arm", "ipu1", + "ipu2", "vdo_axi", "osc", "gpu2d_core", + "gpu3d_core", "usdhc2", "ssi1", "ssi2", + "ssi3", "gpu3d_shader", "vpu_axi", "can_root", + "ldb_di0", "ldb_di1", "esai", "eim_slow", + "uart_serial", "spdif", "asrc", "hsi_tx", +}; +static const char *cko_sels[] = { "cko1", "cko2", }; +static const char *lvds_sels[] = { "arm", "pll1_sys", "dummy", "dummy", "dummy", "dummy", "dummy", "pll5_video_div", + "dummy", "dummy", "pcie_ref", "sata_ref", "usbphy1", "usbphy2", }; enum mx6q_clks { dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m, @@ -221,11 +89,11 @@ periph_clk2, periph2_clk2, ipg, ipg_per, esai_pred, esai_podf, asrc_pred, asrc_podf, spdif_pred, spdif_podf, can_root, ecspi_root, gpu2d_core_podf, gpu3d_core_podf, gpu3d_shader, ipu1_podf, ipu2_podf, - ldb_di0_podf, ldb_di1_podf, ipu1_di0_pre, ipu1_di1_pre, ipu2_di0_pre, - ipu2_di1_pre, hsi_tx_podf, ssi1_pred, ssi1_podf, ssi2_pred, ssi2_podf, - ssi3_pred, ssi3_podf, uart_serial_podf, usdhc1_podf, usdhc2_podf, - usdhc3_podf, usdhc4_podf, enfc_pred, enfc_podf, emi_podf, - emi_slow_podf, vpu_axi_podf, cko1_podf, axi, mmdc_ch0_axi_podf, + ldb_di0_podf_unused, ldb_di1_podf_unused, ipu1_di0_pre, ipu1_di1_pre, + ipu2_di0_pre, ipu2_di1_pre, hsi_tx_podf, ssi1_pred, ssi1_podf, + ssi2_pred, ssi2_podf, ssi3_pred, ssi3_podf, uart_serial_podf, + usdhc1_podf, usdhc2_podf, usdhc3_podf, usdhc4_podf, enfc_pred, enfc_podf, + emi_podf, emi_slow_podf, vpu_axi_podf, cko1_podf, axi, mmdc_ch0_axi_podf, mmdc_ch1_axi_podf, arm, ahb, apbh_dma, asrc, can1_ipg, can1_serial, can2_ipg, can2_serial, ecspi1, ecspi2, ecspi3, ecspi4, ecspi5, enet, esai, gpt_ipg, gpt_ipg_per, gpu2d_core, gpu3d_core, hdmi_iahb, @@ -238,14 +106,18 @@ pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg, ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5, sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate, - usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, clk_max + usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, eim_slow, + spdif, cko2_sel, cko2_podf, cko2, cko, vdoa, gpt_3m, video_27m, + ldb_di0_div_7, ldb_di1_div_7, ldb_di0_div_sel, ldb_di1_div_sel, + pll4_audio_div, lvds1_sel, lvds1_in, lvds1_out, caam_mem, caam_aclk, + caam_ipg, epit1, epit2, tzasc2, clk_max }; static struct clk *clk[clk_max]; static struct clk_onecell_data clk_data; static enum mx6q_clks const clks_init_on[] __initconst = { - mmdc_ch0_axi, rom, pll1_sys, + mmdc_ch0_axi, rom, arm, }; static struct clk_div_table clk_enet_ref_table[] = { @@ -270,34 +142,24 @@ { } }; -int __init mx6q_clocks_init(void) +static void __init imx6q_clocks_init(struct device_node *ccm_node) { struct device_node *np; void __iomem *base; int i, irq; + int ret; clk[dummy] = imx_clk_fixed("dummy", 0); - - /* retrieve the freqency of fixed clocks from device tree */ - for_each_compatible_node(np, NULL, "fixed-clock") { - u32 rate; - if (of_property_read_u32(np, "clock-frequency", &rate)) - continue; - - if (of_device_is_compatible(np, "fsl,imx-ckil")) - clk[ckil] = imx_clk_fixed("ckil", rate); - else if (of_device_is_compatible(np, "fsl,imx-ckih1")) - clk[ckih] = imx_clk_fixed("ckih", rate); - else if (of_device_is_compatible(np, "fsl,imx-osc")) - clk[osc] = imx_clk_fixed("osc", rate); - } + clk[ckil] = imx_obtain_fixed_clock("ckil", 0); + clk[ckih] = imx_obtain_fixed_clock("ckih1", 0); + clk[osc] = imx_obtain_fixed_clock("osc", 0); np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); base = of_iomap(np, 0); WARN_ON(!base); /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ - if (cpu_is_imx6q() && imx6q_revision() == IMX_CHIP_REVISION_1_0) { + if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) { post_div_table[1].div = 1; post_div_table[2].div = 1; video_div_table[1].div = 1; @@ -305,14 +167,16 @@ }; /* type name parent_name base div_mask */ - clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f); - clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1); - clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3); - clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f); - clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f); - clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3); - clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x3); - clk[pll8_mlb] = imx_clk_pllv3(IMX_PLLV3_MLB, "pll8_mlb", "osc", base + 0xd0, 0x0); + clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f, false); + clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1, false); + clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3, false); + clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f, false); + clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f, false); + clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3, false); + clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host", "osc", base + 0x20, 0x3, false); + + /* name reg shift width parent_names num_parents */ + clk[lvds1_sel] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); /* * Bit 20 is the reserved and read-only bit, we do this only for: @@ -332,6 +196,9 @@ clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); + /* NOTICE: The gate of the lvds1 in/out is used to select the clk direction */ + clk[lvds1_in] = imx_clk_gate("lvds1_in", NULL, base + 0x160, 12); + clk[lvds1_out] = imx_clk_gate("lvds1_out", "lvds1_sel", base + 0x160, 10); clk[sata_ref_100m] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); clk[pcie_ref_125m] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); @@ -355,15 +222,18 @@ clk[pll3_80m] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); clk[pll3_60m] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); clk[twd] = imx_clk_fixed_factor("twd", "arm", 1, 2); + clk[gpt_3m] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8); + clk[video_27m] = imx_clk_fixed_factor("video_27m", "pll3_pfd1_540m", 1, 20); clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + clk[pll4_audio_div] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); - np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ccm"); + np = ccm_node; base = of_iomap(np, 0); WARN_ON(!base); - ccm_base = base; + imx6_pm_set_ccm_base(base); /* name reg shift width parent_names num_parents */ clk[step] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); @@ -385,29 +255,33 @@ clk[ipu2_sel] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); - clk[ipu1_di0_pre_sel] = imx_clk_mux("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels)); - clk[ipu1_di1_pre_sel] = imx_clk_mux("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels)); - clk[ipu2_di0_pre_sel] = imx_clk_mux("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels)); - clk[ipu2_di1_pre_sel] = imx_clk_mux("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels)); - clk[ipu1_di0_sel] = imx_clk_mux("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels)); - clk[ipu1_di1_sel] = imx_clk_mux("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels)); - clk[ipu2_di0_sel] = imx_clk_mux("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels)); - clk[ipu2_di1_sel] = imx_clk_mux("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels)); + clk[ldb_di0_div_sel] = imx_clk_mux_flags("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels), CLK_SET_RATE_PARENT); + clk[ldb_di1_div_sel] = imx_clk_mux_flags("ldb_di1_div_sel", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels), CLK_SET_RATE_PARENT); + clk[ipu1_di0_pre_sel] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + clk[ipu1_di1_pre_sel] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + clk[ipu2_di0_pre_sel] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + clk[ipu2_di1_pre_sel] = imx_clk_mux_flags("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + clk[ipu1_di0_sel] = imx_clk_mux_flags("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels), CLK_SET_RATE_PARENT); + clk[ipu1_di1_sel] = imx_clk_mux_flags("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels), CLK_SET_RATE_PARENT); + clk[ipu2_di0_sel] = imx_clk_mux_flags("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels), CLK_SET_RATE_PARENT); + clk[ipu2_di1_sel] = imx_clk_mux_flags("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels), CLK_SET_RATE_PARENT); clk[hsi_tx_sel] = imx_clk_mux("hsi_tx_sel", base + 0x30, 28, 1, hsi_tx_sels, ARRAY_SIZE(hsi_tx_sels)); clk[pcie_axi_sel] = imx_clk_mux("pcie_axi_sel", base + 0x18, 10, 1, pcie_axi_sels, ARRAY_SIZE(pcie_axi_sels)); - clk[ssi1_sel] = imx_clk_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); - clk[ssi2_sel] = imx_clk_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); - clk[ssi3_sel] = imx_clk_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); - clk[usdhc1_sel] = imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); - clk[usdhc2_sel] = imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); - clk[usdhc3_sel] = imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); - clk[usdhc4_sel] = imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + clk[ssi1_sel] = imx_clk_fixup_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clk[ssi2_sel] = imx_clk_fixup_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clk[ssi3_sel] = imx_clk_fixup_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clk[usdhc1_sel] = imx_clk_fixup_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clk[usdhc2_sel] = imx_clk_fixup_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clk[usdhc3_sel] = imx_clk_fixup_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clk[usdhc4_sel] = imx_clk_fixup_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); clk[enfc_sel] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels, ARRAY_SIZE(enfc_sels)); - clk[emi_sel] = imx_clk_mux("emi_sel", base + 0x1c, 27, 2, emi_sels, ARRAY_SIZE(emi_sels)); - clk[emi_slow_sel] = imx_clk_mux("emi_slow_sel", base + 0x1c, 29, 2, emi_sels, ARRAY_SIZE(emi_sels)); + clk[emi_sel] = imx_clk_fixup_mux("emi_sel", base + 0x1c, 27, 2, emi_sels, ARRAY_SIZE(emi_sels), imx_cscmr1_fixup); + clk[emi_slow_sel] = imx_clk_fixup_mux("emi_slow_sel", base + 0x1c, 29, 2, emi_slow_sels, ARRAY_SIZE(emi_slow_sels), imx_cscmr1_fixup); clk[vdo_axi_sel] = imx_clk_mux("vdo_axi_sel", base + 0x18, 11, 1, vdo_axi_sels, ARRAY_SIZE(vdo_axi_sels)); clk[vpu_axi_sel] = imx_clk_mux("vpu_axi_sel", base + 0x18, 14, 2, vpu_axi_sels, ARRAY_SIZE(vpu_axi_sels)); clk[cko1_sel] = imx_clk_mux("cko1_sel", base + 0x60, 0, 4, cko1_sels, ARRAY_SIZE(cko1_sels)); + clk[cko2_sel] = imx_clk_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels)); + clk[cko] = imx_clk_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels)); /* name reg shift width busy: reg, shift parent_names num_parents */ clk[periph] = imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); @@ -417,7 +291,7 @@ clk[periph_clk2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); clk[periph2_clk2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); clk[ipg] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); - clk[ipg_per] = imx_clk_divider("ipg_per", "ipg", base + 0x1c, 0, 6); + clk[ipg_per] = imx_clk_fixup_divider("ipg_per", "ipg", base + 0x1c, 0, 6, imx_cscmr1_fixup); clk[esai_pred] = imx_clk_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); clk[esai_podf] = imx_clk_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); clk[asrc_pred] = imx_clk_divider("asrc_pred", "asrc_sel", base + 0x30, 12, 3); @@ -432,9 +306,9 @@ clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3); clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3); clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); - clk[ldb_di0_podf] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0); + clk[ldb_di0_div_7] = imx_clk_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7); clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); - clk[ldb_di1_podf] = imx_clk_divider_flags("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1, 0); + clk[ldb_di1_div_7] = imx_clk_fixed_factor("ldb_di1_div_7", "ldb_di1_sel", 1, 7); clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3); clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3); clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3); @@ -453,10 +327,11 @@ clk[usdhc4_podf] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); clk[enfc_pred] = imx_clk_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3); clk[enfc_podf] = imx_clk_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6); - clk[emi_podf] = imx_clk_divider("emi_podf", "emi_sel", base + 0x1c, 20, 3); - clk[emi_slow_podf] = imx_clk_divider("emi_slow_podf", "emi_slow_sel", base + 0x1c, 23, 3); + clk[emi_podf] = imx_clk_fixup_divider("emi_podf", "emi_sel", base + 0x1c, 20, 3, imx_cscmr1_fixup); + clk[emi_slow_podf] = imx_clk_fixup_divider("emi_slow_podf", "emi_slow_sel", base + 0x1c, 23, 3, imx_cscmr1_fixup); clk[vpu_axi_podf] = imx_clk_divider("vpu_axi_podf", "vpu_axi_sel", base + 0x24, 25, 3); clk[cko1_podf] = imx_clk_divider("cko1_podf", "cko1_sel", base + 0x60, 4, 3); + clk[cko2_podf] = imx_clk_divider("cko2_podf", "cko2_sel", base + 0x60, 21, 3); /* name parent_name reg shift width busy: reg, shift */ clk[axi] = imx_clk_busy_divider("axi", "axi_sel", base + 0x14, 16, 3, base + 0x48, 0); @@ -468,6 +343,9 @@ /* name parent_name reg shift */ clk[apbh_dma] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4); clk[asrc] = imx_clk_gate2("asrc", "asrc_podf", base + 0x68, 6); + clk[caam_mem] = imx_clk_gate2("caam_mem", "ahb", base + 0x68, 8); + clk[caam_aclk] = imx_clk_gate2("caam_aclk", "ahb", base + 0x68, 10); + clk[caam_ipg] = imx_clk_gate2("caam_ipg", "ipg", base + 0x68, 12); clk[can1_ipg] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14); clk[can1_serial] = imx_clk_gate2("can1_serial", "can_root", base + 0x68, 16); clk[can2_ipg] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); @@ -478,10 +356,19 @@ clk[ecspi4] = imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); clk[ecspi5] = imx_clk_gate2("ecspi5", "ecspi_root", base + 0x6c, 8); clk[enet] = imx_clk_gate2("enet", "ipg", base + 0x6c, 10); + clk[epit1] = imx_clk_gate2("epit1", "ipg", base + 0x6c, 12); + clk[epit2] = imx_clk_gate2("epit2", "ipg", base + 0x6c, 14); clk[esai] = imx_clk_gate2("esai", "esai_podf", base + 0x6c, 16); clk[gpt_ipg] = imx_clk_gate2("gpt_ipg", "ipg", base + 0x6c, 20); clk[gpt_ipg_per] = imx_clk_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22); - clk[gpu2d_core] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); + if (cpu_is_imx6dl()) + /* + * The multiplexer and divider of imx6q clock gpu3d_shader get + * redefined/reused as gpu2d_core_sel and gpu2d_core_podf on imx6dl. + */ + clk[gpu2d_core] = imx_clk_gate2("gpu2d_core", "gpu3d_shader", base + 0x6c, 24); + else + clk[gpu2d_core] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); clk[gpu3d_core] = imx_clk_gate2("gpu3d_core", "gpu3d_core_podf", base + 0x6c, 26); clk[hdmi_iahb] = imx_clk_gate2("hdmi_iahb", "ahb", base + 0x70, 0); clk[hdmi_isfr] = imx_clk_gate2("hdmi_isfr", "pll3_pfd1_540m", base + 0x70, 4); @@ -490,16 +377,25 @@ clk[i2c3] = imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10); clk[iim] = imx_clk_gate2("iim", "ipg", base + 0x70, 12); clk[enfc] = imx_clk_gate2("enfc", "enfc_podf", base + 0x70, 14); + clk[tzasc2] = imx_clk_gate2("tzasc2", "mmdc_ch0_axi_podf", base + 0x70, 24); + clk[vdoa] = imx_clk_gate2("vdoa", "vdo_axi", base + 0x70, 26); clk[ipu1] = imx_clk_gate2("ipu1", "ipu1_podf", base + 0x74, 0); clk[ipu1_di0] = imx_clk_gate2("ipu1_di0", "ipu1_di0_sel", base + 0x74, 2); clk[ipu1_di1] = imx_clk_gate2("ipu1_di1", "ipu1_di1_sel", base + 0x74, 4); clk[ipu2] = imx_clk_gate2("ipu2", "ipu2_podf", base + 0x74, 6); clk[ipu2_di0] = imx_clk_gate2("ipu2_di0", "ipu2_di0_sel", base + 0x74, 8); - clk[ldb_di0] = imx_clk_gate2("ldb_di0", "ldb_di0_podf", base + 0x74, 12); - clk[ldb_di1] = imx_clk_gate2("ldb_di1", "ldb_di1_podf", base + 0x74, 14); clk[ipu2_di1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); + clk[ldb_di0] = imx_clk_gate2("ldb_di0", "ldb_di0_div_sel", base + 0x74, 12); + clk[ldb_di1] = imx_clk_gate2("ldb_di1", "ldb_di1_div_sel", base + 0x74, 14); clk[hsi_tx] = imx_clk_gate2("hsi_tx", "hsi_tx_podf", base + 0x74, 16); - clk[mlb] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); + if (cpu_is_imx6dl()) + /* + * The multiplexer and divider of the imx6q clock gpu2d get + * redefined/reused as mlb_sys_sel and mlb_sys_clk_podf on imx6dl. + */ + clk[mlb] = imx_clk_gate2("mlb", "gpu2d_core_podf", base + 0x74, 18); + else + clk[mlb] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); clk[mmdc_ch0_axi] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); clk[mmdc_ch1_axi] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); clk[ocram] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); @@ -518,9 +414,13 @@ clk[sata] = imx_clk_gate2("sata", "ipg", base + 0x7c, 4); clk[sdma] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); clk[spba] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); + clk[spdif] = imx_clk_gate2("spdif", "spdif_podf", base + 0x7c, 14); clk[ssi1_ipg] = imx_clk_gate2("ssi1_ipg", "ipg", base + 0x7c, 18); clk[ssi2_ipg] = imx_clk_gate2("ssi2_ipg", "ipg", base + 0x7c, 20); clk[ssi3_ipg] = imx_clk_gate2("ssi3_ipg", "ipg", base + 0x7c, 22); + clk[ssi1] = imx_clk_gate2("ssi1", "ssi1_podf", base + 0x7c, 18); + clk[ssi2] = imx_clk_gate2("ssi2", "ssi2_podf", base + 0x7c, 20); + clk[ssi3] = imx_clk_gate2("ssi3", "ssi3_podf", base + 0x7c, 22); clk[uart_ipg] = imx_clk_gate2("uart_ipg", "ipg", base + 0x7c, 24); clk[uart_serial] = imx_clk_gate2("uart_serial", "uart_serial_podf", base + 0x7c, 26); clk[usboh3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); @@ -528,31 +428,63 @@ clk[usdhc2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); clk[usdhc3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); clk[usdhc4] = imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); + clk[eim_slow] = imx_clk_gate2("eim_slow", "emi_slow_podf", base + 0x80, 10); clk[vdo_axi] = imx_clk_gate2("vdo_axi", "vdo_axi_sel", base + 0x80, 12); clk[vpu_axi] = imx_clk_gate2("vpu_axi", "vpu_axi_podf", base + 0x80, 14); clk[cko1] = imx_clk_gate("cko1", "cko1_podf", base + 0x60, 7); + clk[cko2] = imx_clk_gate("cko2", "cko2_podf", base + 0x60, 24); for (i = 0; i < ARRAY_SIZE(clk); i++) if (IS_ERR(clk[i])) pr_err("i.MX6q clk %d: register failed with %ld\n", i, PTR_ERR(clk[i])); + /* Initialize clock gate status */ + writel_relaxed(1 << CCM_CCGR_OFFSET(11) | + 3 << CCM_CCGR_OFFSET(1) | + 3 << CCM_CCGR_OFFSET(0), base + 0x68); + if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) + writel_relaxed(3 << CCM_CCGR_OFFSET(11) | + 3 << CCM_CCGR_OFFSET(10), base + 0x6c); + else + writel_relaxed(3 << CCM_CCGR_OFFSET(10), base + 0x6c); + writel_relaxed(1 << CCM_CCGR_OFFSET(12) | + 3 << CCM_CCGR_OFFSET(11) | + 3 << CCM_CCGR_OFFSET(10) | + 3 << CCM_CCGR_OFFSET(9) | + 3 << CCM_CCGR_OFFSET(8), base + 0x70); + writel_relaxed(3 << CCM_CCGR_OFFSET(14) | + 1 << CCM_CCGR_OFFSET(13) | + 3 << CCM_CCGR_OFFSET(12) | + 1 << CCM_CCGR_OFFSET(11) | + 3 << CCM_CCGR_OFFSET(10), base + 0x74); + writel_relaxed(3 << CCM_CCGR_OFFSET(7) | + 3 << CCM_CCGR_OFFSET(6) | + 3 << CCM_CCGR_OFFSET(4), base + 0x78); + writel_relaxed(1 << CCM_CCGR_OFFSET(0), base + 0x7c); + writel_relaxed(0, base + 0x80); + clk_data.clks = clk; clk_data.clk_num = ARRAY_SIZE(clk); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); clk_register_clkdev(clk[gpt_ipg], "ipg", "imx-gpt.0"); clk_register_clkdev(clk[gpt_ipg_per], "per", "imx-gpt.0"); + clk_register_clkdev(clk[gpt_3m], "gpt_3m", "imx-gpt.0"); clk_register_clkdev(clk[cko1_sel], "cko1_sel", NULL); clk_register_clkdev(clk[ahb], "ahb", NULL); clk_register_clkdev(clk[cko1], "cko1", NULL); clk_register_clkdev(clk[arm], NULL, "cpu0"); - if (imx6q_revision() != IMX_CHIP_REVISION_1_0) { + if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) || cpu_is_imx6dl()) { clk_set_parent(clk[ldb_di0_sel], clk[pll5_video_div]); clk_set_parent(clk[ldb_di1_sel], clk[pll5_video_div]); } + if (cpu_is_imx6dl()) { + clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]); + } + /* * The gpmi needs 100MHz frequency in the EDO/Sync mode, * We can not get the 100MHz from the pll2_pfd0_352m. @@ -560,6 +492,20 @@ */ clk_set_parent(clk[enfc_sel], clk[pll2_pfd2_396m]); + /* Set the parent clks of PCIe lvds1 and pcie_axi to be sata ref, axi */ + if (clk_set_parent(clk[lvds1_sel], clk[sata_ref])) + pr_err("Failed to set PCIe bus parent clk.\n"); + if (clk_set_parent(clk[pcie_axi_sel], clk[axi])) + pr_err("Failed to set PCIe parent clk.\n"); + + + /* gpu clock initilazation */ + clk_set_parent(clk[gpu3d_shader_sel], clk[pll2_pfd1_594m]); + clk_set_rate(clk[gpu3d_shader], 594000000); + clk_set_parent(clk[gpu3d_core_sel], clk[mmdc_ch0_axi]); + clk_set_rate(clk[gpu3d_core], 528000000); + clk_set_parent(clk[gpu2d_core_sel], clk[pll3_usb_otg]); + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clk[clks_init_on[i]]); @@ -568,14 +514,57 @@ clk_prepare_enable(clk[usbphy2_gate]); } + /* ipu clock initialization */ + clk_set_parent(clk[ldb_di0_sel], clk[pll2_pfd0_352m]); + clk_set_parent(clk[ldb_di1_sel], clk[pll2_pfd0_352m]); + clk_set_parent(clk[ipu1_di0_pre_sel], clk[pll5_video_div]); + clk_set_parent(clk[ipu1_di1_pre_sel], clk[pll5_video_div]); + clk_set_parent(clk[ipu2_di0_pre_sel], clk[pll5_video_div]); + clk_set_parent(clk[ipu2_di1_pre_sel], clk[pll5_video_div]); + clk_set_parent(clk[ipu1_di0_sel], clk[ipu1_di0_pre]); + clk_set_parent(clk[ipu1_di1_sel], clk[ipu1_di1_pre]); + clk_set_parent(clk[ipu2_di0_sel], clk[ipu2_di0_pre]); + clk_set_parent(clk[ipu2_di1_sel], clk[ipu2_di1_pre]); + if (cpu_is_imx6dl()) { + clk_set_rate(clk[pll3_pfd1_540m], 540000000); + clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]); + clk_set_parent(clk[axi_sel], clk[pll3_pfd1_540m]); + /* set epdc/pxp axi clock to 200Mhz */ + clk_set_parent(clk[ipu2_sel], clk[pll2_pfd2_396m]); + clk_set_rate(clk[ipu2], 200000000); + } else if (cpu_is_imx6q()) { + clk_set_parent(clk[ipu1_sel], clk[mmdc_ch0_axi]); + clk_set_parent(clk[ipu2_sel], clk[mmdc_ch0_axi]); + } + + /* + * Let's initially set up CLKO with OSC24M, since this configuration + * is widely used by imx6q board designs to clock audio codec. + */ + ret = clk_set_parent(clk[cko2_sel], clk[osc]); + if (!ret) + ret = clk_set_parent(clk[cko], clk[cko2]); + if (ret) + pr_warn("failed to set up CLKO: %d\n", ret); + + /* Audio clocks */ + clk_set_parent(clk[ssi1_sel], clk[pll4_audio_div]); + clk_set_parent(clk[ssi2_sel], clk[pll4_audio_div]); + clk_set_parent(clk[ssi3_sel], clk[pll4_audio_div]); + clk_set_parent(clk[spdif_sel], clk[pll3_pfd3_454m]); + clk_set_parent(clk[asrc_sel], clk[pll3_usb_otg]); + clk_set_rate(clk[asrc_sel], 7500000); + + /* Set pll4_audio to a value that can derive 5K-88.2KHz and 8K-96KHz */ + clk_set_rate(clk[pll4_audio_div], 541900800); + /* Set initial power mode */ - imx6q_set_lpm(WAIT_CLOCKED); + imx6_set_lpm(WAIT_CLOCKED); np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpt"); base = of_iomap(np, 0); WARN_ON(!base); irq = irq_of_parse_and_map(np, 0); mxc_timer_init(base, irq); - - return 0; } +CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init); diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-imx6sl.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-imx6sl.c --- linux-3.10.30/arch/arm/mach-imx/clk-imx6sl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-imx6sl.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + * + */ + +#define CCM_CCDR_OFFSET 0x4 +#define ANATOP_PLL_USB1 0x10 +#define ANATOP_PLL_USB2 0x20 +#define ANATOP_PLL_ENET 0xE0 +#define ANATOP_PLL_BYPASS_OFFSET (1 << 16) +#define ANATOP_PLL_ENABLE_OFFSET (1 << 13) +#define ANATOP_PLL_POWER_OFFSET (1 << 12) +#define ANATOP_PFD_480n_OFFSET 0xf0 +#define ANATOP_PFD_528n_OFFSET 0x100 +#define PFD0_CLKGATE (1 << 7) +#define PFD1_CLK_GATE (1 << 15) +#define PFD2_CLK_GATE (1 << 23) +#define PFD3_CLK_GATE (1 << 31) +#define CCDR_CH0_HS_BYP 17 +#define OSC_RATE 24000000 + +#define CCM_CCGR_OFFSET(index) (index * 2) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" +#include "common.h" + +static bool uart_from_osc; +static const char const *step_sels[] = { "osc", "pll2_pfd2", }; +static const char const *pll1_sw_sels[] = { "pll1_sys", "step", }; +static const char const *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", }; +static const char const *ocram_sels[] = { "periph", "ocram_alt_sels", }; +static const char const *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", }; +static const char const *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; +static const char const *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; +static const char const *periph_sels[] = { "pre_periph_sel", "periph_clk2", }; +static const char const *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2", }; +static const char const *csi_sels[] = { "osc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", }; +static const char const *lcdif_axi_sels[] = { "pll2_bus", "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", }; +static const char const *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", }; +static const char const *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", }; +static const char const *perclk_sels[] = { "ipg", "osc", }; +static const char const *epdc_axi_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd2", }; +static const char const *pxp_axi_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd3", }; +static const char const *gpu2d_ovg_sels[] = { "pll3_pfd1", "pll3_usb_otg", "pll2_bus", "pll2_pfd2", }; +static const char const *gpu2d_sels[] = { "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", "pll2_bus", }; +static const char const *lcdif_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll3_pfd0", "pll3_pfd1", }; +static const char const *epdc_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd1", "pll3_pfd1", }; +static const char const *audio_sels[] = { "pll4_audio_div", "pll3_pfd2", "pll3_pfd3", "pll3_usb_otg", }; +static const char const *ecspi_sels[] = { "pll3_60m", "osc", }; +static const char const *uart_sels[] = { "pll3_80m", "uart_osc_4M", }; + +static struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, + { } +}; + +static struct clk_div_table post_div_table[] = { + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static struct clk_div_table video_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 1, }, + { .val = 3, .div = 4, }, + { } +}; + +static struct clk *clks[IMX6SL_CLK_CLK_END]; +static struct clk_onecell_data clk_data; +static u32 cur_arm_podf; +static u32 pll1_org_rate; + +extern int low_bus_freq_mode; +extern int audio_bus_freq_mode; + +/* + * On MX6SL, need to ensure that the ARM:IPG clock ratio is maintained + * within 12:5 when the clocks to ARM are gated when the SOC enters + * WAIT mode. This is necessary to avoid WAIT mode issue (an early + * interrupt waking up the ARM). + * This function will set the ARM clk to max value within the 12:5 limit. + */ +void imx6sl_set_wait_clk(bool enter) +{ + u32 parent_rate; + + if (enter) { + u32 wait_podf; + u32 new_parent_rate = OSC_RATE; + u32 ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]); + u32 max_arm_wait_clk = (12 * ipg_rate) / 5; + parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); + cur_arm_podf = parent_rate / clk_get_rate(clks[IMX6SL_CLK_ARM]); + if (low_bus_freq_mode) { + /* + * IPG clk is at 12MHz at this point, we can only run + * ARM at a max of 28.8MHz. So we need to set ARM + * to run from the 24MHz OSC, as there is no way to + * get 28.8MHz when ARM is sourced from PLL1. + */ + clk_set_parent(clks[IMX6SL_CLK_STEP], + clks[IMX6SL_CLK_OSC]); + clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], + clks[IMX6SL_CLK_STEP]); + } else if (audio_bus_freq_mode) { + /* + * In this mode ARM is from PLL2_PFD2 (396MHz), + * but IPG is at 12MHz. Need to switch ARM to run + * from the bypassed PLL1 clocks so that we can run + * ARM at 24MHz. + */ + pll1_org_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SYS]); + /* Ensure PLL1 is at 24MHz. */ + clk_set_rate(clks[IMX6SL_CLK_PLL1_SYS], OSC_RATE); + clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], clks[IMX6SL_CLK_PLL1_SYS]); + } else + new_parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); + wait_podf = (new_parent_rate + max_arm_wait_clk - 1) / + max_arm_wait_clk; + + clk_set_rate(clks[IMX6SL_CLK_ARM], new_parent_rate / wait_podf); + } else { + if (low_bus_freq_mode) + /* Move ARM back to PLL1. */ + clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], + clks[IMX6SL_CLK_PLL1_SYS]); + else if (audio_bus_freq_mode) { + /* Move ARM back to PLL2_PFD2 via STEP_CLK. */ + clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], clks[IMX6SL_CLK_STEP]); + clk_set_rate(clks[IMX6SL_CLK_PLL1_SYS], pll1_org_rate); + } + parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); + clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / cur_arm_podf); + } +} + +static int __init setup_uart_clk(char *uart_rate) +{ + uart_from_osc = true; + return 1; +} + +__setup("uart_at_4M", setup_uart_clk); + +static void __init imx6sl_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *base; + int irq; + int ret; + int i; + u32 reg; + + clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clks[IMX6SL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); + clks[IMX6SL_CLK_OSC] = imx_obtain_fixed_clock("osc", 0); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop"); + base = of_iomap(np, 0); + WARN_ON(!base); + + /* type name parent base div_mask */ + clks[IMX6SL_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f, true); + clks[IMX6SL_CLK_PLL2_BUS] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1, true); + clks[IMX6SL_CLK_PLL3_USB_OTG] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3, false); + clks[IMX6SL_CLK_PLL4_AUDIO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f, false); + clks[IMX6SL_CLK_PLL5_VIDEO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f, false); + clks[IMX6SL_CLK_PLL6_ENET] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3, false); + clks[IMX6SL_CLK_PLL7_USB_HOST] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host", "osc", base + 0x20, 0x3, false); + + /* + * usbphy1 and usbphy2 are implemented as dummy gates using reserve + * bit 20. They are used by phy driver to keep the refcount of + * parent PLL correct. usbphy1_gate and usbphy2_gate only needs to be + * turned on during boot, and software will not need to control it + * anymore after that. + */ + clks[IMX6SL_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + clks[IMX6SL_CLK_USBPHY2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + clks[IMX6SL_CLK_USBPHY1_GATE] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6); + clks[IMX6SL_CLK_USBPHY2_GATE] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); + + /* dev name parent_name flags reg shift width div: flags, div_table lock */ + clks[IMX6SL_CLK_PLL4_POST_DIV] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL4_AUDIO_DIV] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL5_POST_DIV] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_PLL5_VIDEO_DIV] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + clks[IMX6SL_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, base + 0xe0, 0, 2, 0, clk_enet_ref_table, &imx_ccm_lock); + + /* name parent_name reg idx */ + clks[IMX6SL_CLK_PLL2_PFD0] = imx_clk_pfd("pll2_pfd0", "pll2_bus", base + 0x100, 0); + clks[IMX6SL_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_bus", base + 0x100, 1); + clks[IMX6SL_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_bus", base + 0x100, 2); + clks[IMX6SL_CLK_PLL3_PFD0] = imx_clk_pfd("pll3_pfd0", "pll3_usb_otg", base + 0xf0, 0); + clks[IMX6SL_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_usb_otg", base + 0xf0, 1); + clks[IMX6SL_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_usb_otg", base + 0xf0, 2); + clks[IMX6SL_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_usb_otg", base + 0xf0, 3); + + /* name parent_name mult div */ + clks[IMX6SL_CLK_PLL2_198M] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2", 1, 2); + clks[IMX6SL_CLK_PLL3_120M] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); + clks[IMX6SL_CLK_PLL3_80M] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + clks[IMX6SL_CLK_PLL3_60M] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + clks[IMX6SL_CLK_UART_OSC_4M] = imx_clk_fixed_factor("uart_osc_4M", "osc", 1, 6); + + /* Ensure all PFDs but PLL2_PFD2 are disabled. */ + reg = readl_relaxed(base + ANATOP_PFD_480n_OFFSET); + reg |= (PFD0_CLKGATE | PFD1_CLK_GATE | PFD2_CLK_GATE | PFD3_CLK_GATE); + writel_relaxed(reg, base + ANATOP_PFD_480n_OFFSET); + reg = readl_relaxed(base + ANATOP_PFD_528n_OFFSET); + reg |= (PFD0_CLKGATE | PFD1_CLK_GATE); + writel_relaxed(reg, base + ANATOP_PFD_528n_OFFSET); + + /* Ensure Unused PLLs are disabled. */ + reg = readl_relaxed(base + ANATOP_PLL_USB1); + reg |= ANATOP_PLL_BYPASS_OFFSET; + reg &= ~(ANATOP_PLL_ENABLE_OFFSET | ANATOP_PLL_POWER_OFFSET); + writel_relaxed(reg, base + ANATOP_PLL_USB1); + + reg = readl_relaxed(base + ANATOP_PLL_USB2); + reg |= ANATOP_PLL_BYPASS_OFFSET; + reg &= ~(ANATOP_PLL_ENABLE_OFFSET | ANATOP_PLL_POWER_OFFSET); + writel_relaxed(reg, base + ANATOP_PLL_USB2); + + reg = readl_relaxed(base + ANATOP_PLL_ENET); + reg |= (ANATOP_PLL_BYPASS_OFFSET | ANATOP_PLL_POWER_OFFSET); + reg &= ~ANATOP_PLL_ENABLE_OFFSET; + writel_relaxed(reg, base + ANATOP_PLL_ENET); + + np = ccm_node; + base = of_iomap(np, 0); + WARN_ON(!base); + imx6_pm_set_ccm_base(base); + + /* name reg shift width parent_names num_parents */ + clks[IMX6SL_CLK_STEP] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + clks[IMX6SL_CLK_PLL1_SW] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); + clks[IMX6SL_CLK_OCRAM_ALT_SEL] = imx_clk_mux("ocram_alt_sel", base + 0x14, 7, 1, ocram_alt_sels, ARRAY_SIZE(ocram_alt_sels)); + clks[IMX6SL_CLK_OCRAM_SEL] = imx_clk_mux("ocram_sel", base + 0x14, 6, 1, ocram_sels, ARRAY_SIZE(ocram_sels)); + clks[IMX6SL_CLK_PRE_PERIPH2_SEL] = imx_clk_mux("pre_periph2_sel", base + 0x18, 21, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels)); + clks[IMX6SL_CLK_PRE_PERIPH_SEL] = imx_clk_mux("pre_periph_sel", base + 0x18, 18, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels)); + clks[IMX6SL_CLK_PERIPH2_CLK2_SEL] = imx_clk_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + clks[IMX6SL_CLK_PERIPH_CLK2_SEL] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + clks[IMX6SL_CLK_CSI_SEL] = imx_clk_mux("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels)); + clks[IMX6SL_CLK_LCDIF_AXI_SEL] = imx_clk_mux("lcdif_axi_sel", base + 0x3c, 14, 2, lcdif_axi_sels, ARRAY_SIZE(lcdif_axi_sels)); + clks[IMX6SL_CLK_USDHC1_SEL] = imx_clk_fixup_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clks[IMX6SL_CLK_USDHC2_SEL] = imx_clk_fixup_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clks[IMX6SL_CLK_USDHC3_SEL] = imx_clk_fixup_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clks[IMX6SL_CLK_USDHC4_SEL] = imx_clk_fixup_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clks[IMX6SL_CLK_SSI1_SEL] = imx_clk_fixup_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clks[IMX6SL_CLK_SSI2_SEL] = imx_clk_fixup_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clks[IMX6SL_CLK_SSI3_SEL] = imx_clk_fixup_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clks[IMX6SL_CLK_PERCLK_SEL] = imx_clk_fixup_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels), imx_cscmr1_fixup); + clks[IMX6SL_CLK_PXP_AXI_SEL] = imx_clk_mux("pxp_axi_sel", base + 0x34, 6, 3, pxp_axi_sels, ARRAY_SIZE(pxp_axi_sels)); + clks[IMX6SL_CLK_EPDC_AXI_SEL] = imx_clk_mux("epdc_axi_sel", base + 0x34, 15, 3, epdc_axi_sels, ARRAY_SIZE(epdc_axi_sels)); + clks[IMX6SL_CLK_GPU2D_OVG_SEL] = imx_clk_mux("gpu2d_ovg_sel", base + 0x18, 4, 2, gpu2d_ovg_sels, ARRAY_SIZE(gpu2d_ovg_sels)); + clks[IMX6SL_CLK_GPU2D_SEL] = imx_clk_mux("gpu2d_sel", base + 0x18, 8, 2, gpu2d_sels, ARRAY_SIZE(gpu2d_sels)); + clks[IMX6SL_CLK_LCDIF_PIX_SEL] = imx_clk_mux_flags("lcdif_pix_sel", base + 0x38, 6, 3, lcdif_pix_sels, ARRAY_SIZE(lcdif_pix_sels), CLK_SET_RATE_PARENT); + clks[IMX6SL_CLK_EPDC_PIX_SEL] = imx_clk_mux_flags("epdc_pix_sel", base + 0x38, 15, 3, epdc_pix_sels, ARRAY_SIZE(epdc_pix_sels), CLK_SET_RATE_PARENT); + clks[IMX6SL_CLK_SPDIF0_SEL] = imx_clk_mux("spdif0_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SL_CLK_SPDIF1_SEL] = imx_clk_mux("spdif1_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clks[IMX6SL_CLK_EXTERN_AUDIO_SEL] = imx_clk_mux_flags("extern_audio_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels), CLK_SET_RATE_PARENT); + clks[IMX6SL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + clks[IMX6SL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + + /* name reg shift width busy: reg, shift parent_names num_parents */ + clks[IMX6SL_CLK_PERIPH] = imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + clks[IMX6SL_CLK_PERIPH2] = imx_clk_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + + /* name parent_name reg shift width */ + clks[IMX6SL_CLK_OCRAM_PODF] = imx_clk_divider("ocram_podf", "ocram_sel", base + 0x14, 16, 3); + clks[IMX6SL_CLK_PERIPH_CLK2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); + clks[IMX6SL_CLK_PERIPH2_CLK2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); + clks[IMX6SL_CLK_IPG] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); + clks[IMX6SL_CLK_CSI_PODF] = imx_clk_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); + clks[IMX6SL_CLK_LCDIF_AXI_PODF] = imx_clk_divider("lcdif_axi_podf", "lcdif_axi_sel", base + 0x3c, 16, 3); + clks[IMX6SL_CLK_USDHC1_PODF] = imx_clk_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + clks[IMX6SL_CLK_USDHC2_PODF] = imx_clk_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + clks[IMX6SL_CLK_USDHC3_PODF] = imx_clk_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); + clks[IMX6SL_CLK_USDHC4_PODF] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); + clks[IMX6SL_CLK_SSI1_PRED] = imx_clk_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); + clks[IMX6SL_CLK_SSI1_PODF] = imx_clk_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); + clks[IMX6SL_CLK_SSI2_PRED] = imx_clk_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); + clks[IMX6SL_CLK_SSI2_PODF] = imx_clk_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); + clks[IMX6SL_CLK_SSI3_PRED] = imx_clk_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); + clks[IMX6SL_CLK_SSI3_PODF] = imx_clk_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); + clks[IMX6SL_CLK_PERCLK] = imx_clk_fixup_divider("perclk", "perclk_sel", base + 0x1c, 0, 6, imx_cscmr1_fixup); + clks[IMX6SL_CLK_PXP_AXI_PODF] = imx_clk_divider("pxp_axi_podf", "pxp_axi_sel", base + 0x34, 3, 3); + clks[IMX6SL_CLK_EPDC_AXI_PODF] = imx_clk_divider("epdc_axi_podf", "epdc_axi_sel", base + 0x34, 12, 3); + clks[IMX6SL_CLK_GPU2D_OVG_PODF] = imx_clk_divider("gpu2d_ovg_podf", "gpu2d_ovg_sel", base + 0x18, 26, 3); + clks[IMX6SL_CLK_GPU2D_PODF] = imx_clk_divider("gpu2d_podf", "gpu2d_sel", base + 0x18, 29, 3); + clks[IMX6SL_CLK_LCDIF_PIX_PRED] = imx_clk_divider("lcdif_pix_pred", "lcdif_pix_sel", base + 0x38, 3, 3); + clks[IMX6SL_CLK_EPDC_PIX_PRED] = imx_clk_divider("epdc_pix_pred", "epdc_pix_sel", base + 0x38, 12, 3); + clks[IMX6SL_CLK_LCDIF_PIX_PODF] = imx_clk_fixup_divider("lcdif_pix_podf", "lcdif_pix_pred", base + 0x1c, 20, 3, imx_cscmr1_fixup); + clks[IMX6SL_CLK_EPDC_PIX_PODF] = imx_clk_divider("epdc_pix_podf", "epdc_pix_pred", base + 0x18, 23, 3); + clks[IMX6SL_CLK_SPDIF0_PRED] = imx_clk_divider("spdif0_pred", "spdif0_sel", base + 0x30, 25, 3); + clks[IMX6SL_CLK_SPDIF0_PODF] = imx_clk_divider("spdif0_podf", "spdif0_pred", base + 0x30, 22, 3); + clks[IMX6SL_CLK_SPDIF1_PRED] = imx_clk_divider("spdif1_pred", "spdif1_sel", base + 0x30, 12, 3); + clks[IMX6SL_CLK_SPDIF1_PODF] = imx_clk_divider("spdif1_podf", "spdif1_pred", base + 0x30, 9, 3); + clks[IMX6SL_CLK_EXTERN_AUDIO_PRED] = imx_clk_divider("extern_audio_pred", "extern_audio_sel", base + 0x28, 9, 3); + clks[IMX6SL_CLK_EXTERN_AUDIO_PODF] = imx_clk_divider("extern_audio_podf", "extern_audio_pred", base + 0x28, 25, 3); + clks[IMX6SL_CLK_ECSPI_ROOT] = imx_clk_divider("ecspi_root", "ecspi_sel", base + 0x38, 19, 6); + clks[IMX6SL_CLK_UART_ROOT] = imx_clk_divider("uart_root", "uart_sel", base + 0x24, 0, 6); + + /* name parent_name reg shift width busy: reg, shift */ + clks[IMX6SL_CLK_AHB] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + clks[IMX6SL_CLK_MMDC_ROOT] = imx_clk_busy_divider("mmdc", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + clks[IMX6SL_CLK_ARM] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + + /* name parent_name reg shift */ + clks[IMX6SL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0); + clks[IMX6SL_CLK_ECSPI2] = imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); + clks[IMX6SL_CLK_ECSPI3] = imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); + clks[IMX6SL_CLK_ECSPI4] = imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); + clks[IMX6SL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x6c, 10); + clks[IMX6SL_CLK_EPIT1] = imx_clk_gate2("epit1", "perclk", base + 0x6c, 12); + clks[IMX6SL_CLK_EPIT2] = imx_clk_gate2("epit2", "perclk", base + 0x6c, 14); + clks[IMX6SL_CLK_EXTERN_AUDIO] = imx_clk_gate2("extern_audio", "extern_audio_podf", base + 0x6c, 16); + clks[IMX6SL_CLK_GPT] = imx_clk_gate2("gpt", "perclk", base + 0x6c, 20); + clks[IMX6SL_CLK_GPT_SERIAL] = imx_clk_gate2("gpt_serial", "perclk", base + 0x6c, 22); + clks[IMX6SL_CLK_GPU2D_OVG] = imx_clk_gate2("gpu2d_ovg", "gpu2d_ovg_podf", base + 0x6c, 26); + clks[IMX6SL_CLK_I2C1] = imx_clk_gate2("i2c1", "perclk", base + 0x70, 6); + clks[IMX6SL_CLK_I2C2] = imx_clk_gate2("i2c2", "perclk", base + 0x70, 8); + clks[IMX6SL_CLK_I2C3] = imx_clk_gate2("i2c3", "perclk", base + 0x70, 10); + clks[IMX6SL_CLK_OCOTP] = imx_clk_gate2("ocotp", "ipg", base + 0x70, 12); + clks[IMX6SL_CLK_CSI] = imx_clk_gate2("csi", "csi_podf", base + 0x74, 0); + clks[IMX6SL_CLK_PXP_AXI] = imx_clk_gate2("pxp_axi", "pxp_axi_podf", base + 0x74, 2); + clks[IMX6SL_CLK_EPDC_AXI] = imx_clk_gate2("epdc_axi", "epdc_axi_podf", base + 0x74, 4); + clks[IMX6SL_CLK_LCDIF_AXI] = imx_clk_gate2("lcdif_axi", "lcdif_axi_podf", base + 0x74, 6); + clks[IMX6SL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_pix_podf", base + 0x74, 8); + clks[IMX6SL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_pix_podf", base + 0x74, 10); + clks[IMX6SL_CLK_OCRAM] = imx_clk_gate2("ocram", "ocram_podf", base + 0x74, 28); + clks[IMX6SL_CLK_PWM1] = imx_clk_gate2("pwm1", "perclk", base + 0x78, 16); + clks[IMX6SL_CLK_PWM2] = imx_clk_gate2("pwm2", "perclk", base + 0x78, 18); + clks[IMX6SL_CLK_PWM3] = imx_clk_gate2("pwm3", "perclk", base + 0x78, 20); + clks[IMX6SL_CLK_PWM4] = imx_clk_gate2("pwm4", "perclk", base + 0x78, 22); + clks[IMX6SL_CLK_SDMA] = imx_clk_gate2("sdma", "ipg", base + 0x7c, 6); + clks[IMX6SL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); + clks[IMX6SL_CLK_SPDIF] = imx_clk_gate2("spdif", "spdif0_podf", base + 0x7c, 14); + clks[IMX6SL_CLK_SSI1] = imx_clk_gate2("ssi1", "ssi1_podf", base + 0x7c, 18); + clks[IMX6SL_CLK_SSI2] = imx_clk_gate2("ssi2", "ssi2_podf", base + 0x7c, 20); + clks[IMX6SL_CLK_SSI3] = imx_clk_gate2("ssi3", "ssi3_podf", base + 0x7c, 22); + clks[IMX6SL_CLK_UART] = imx_clk_gate2("uart", "ipg", base + 0x7c, 24); + clks[IMX6SL_CLK_UART_SERIAL] = imx_clk_gate2("uart_serial", "uart_root", base + 0x7c, 26); + clks[IMX6SL_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); + clks[IMX6SL_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + clks[IMX6SL_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + clks[IMX6SL_CLK_USDHC3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); + clks[IMX6SL_CLK_USDHC4] = imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); + + for (i = 0; i < ARRAY_SIZE(clks); i++) + if (IS_ERR(clks[i])) + pr_err("i.MX6SL clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + + /* Initialize clock gate status */ + writel_relaxed(1 << CCM_CCGR_OFFSET(11) | + 3 << CCM_CCGR_OFFSET(1) | + 3 << CCM_CCGR_OFFSET(0), base + 0x68); + writel_relaxed(3 << CCM_CCGR_OFFSET(10), base + 0x6c); + writel_relaxed(1 << CCM_CCGR_OFFSET(11) | + 3 << CCM_CCGR_OFFSET(10) | + 3 << CCM_CCGR_OFFSET(9) | + 3 << CCM_CCGR_OFFSET(8), base + 0x70); + writel_relaxed(3 << CCM_CCGR_OFFSET(14) | + 3 << CCM_CCGR_OFFSET(13) | + 3 << CCM_CCGR_OFFSET(12) | + 3 << CCM_CCGR_OFFSET(11) | + 3 << CCM_CCGR_OFFSET(10), base + 0x74); + writel_relaxed(3 << CCM_CCGR_OFFSET(7) | + 3 << CCM_CCGR_OFFSET(4), base + 0x78); + writel_relaxed(1 << CCM_CCGR_OFFSET(0), base + 0x7c); + writel_relaxed(0, base + 0x80); + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + clk_register_clkdev(clks[IMX6SL_CLK_GPT], "ipg", "imx-gpt.0"); + clk_register_clkdev(clks[IMX6SL_CLK_GPT_SERIAL], "per", "imx-gpt.0"); + + /* Ensure the AHB clk is at 132MHz. */ + ret = clk_set_rate(clks[IMX6SL_CLK_AHB], 132000000); + if (ret) + pr_warn("%s: failed to set AHB clock rate %d\n", __func__, ret); + + /* + * To prevent the bus clock from being disabled accidently when + * clk_disable() gets called on child clock, let's increment the use + * count of IPG clock by initially calling clk_prepare_enable() on it. + */ + ret = clk_prepare_enable(clks[IMX6SL_CLK_IPG]); + if (ret) + pr_warn("%s: failed to enable IPG clock %d\n", __func__, ret); + + /* + * Make sure the ARM clk is enabled to maintain the correct usecount + * and enabling/disabling of parent PLLs. + */ + ret = clk_prepare_enable(clks[IMX6SL_CLK_ARM]); + if (ret) + pr_warn("%s: failed to enable ARM core clock %d\n", + __func__, ret); + + /* + * Make sure the MMDC clk is enabled to maintain the correct usecount + * and enabling/disabling of parent PLLs. + */ + ret = clk_prepare_enable(clks[IMX6SL_CLK_MMDC_ROOT]); + if (ret) + pr_warn("%s: failed to enable MMDC clock %d\n", + __func__, ret); + + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]); + clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]); + } + + clk_set_parent(clks[IMX6SL_CLK_GPU2D_OVG_SEL], + clks[IMX6SL_CLK_PLL2_BUS]); + clk_set_parent(clks[IMX6SL_CLK_GPU2D_SEL], clks[IMX6SL_CLK_PLL2_BUS]); + + /* Initialize Video PLLs to valid frequency (650MHz). */ + clk_set_rate(clks[IMX6SL_CLK_PLL5_VIDEO], 650000000); + /* set PLL5 video as lcdif pix parent clock */ + clk_set_parent(clks[IMX6SL_CLK_LCDIF_PIX_SEL], + clks[IMX6SL_CLK_PLL5_VIDEO_DIV]); + clk_set_parent(clks[IMX6SL_CLK_EPDC_PIX_SEL], + clks[IMX6SL_CLK_PLL5_VIDEO_DIV]); + + clk_set_parent(clks[IMX6SL_CLK_EPDC_AXI_SEL], clks[IMX6SL_CLK_PLL2_PFD2]); + clk_set_rate(clks[IMX6SL_CLK_EPDC_AXI], 200000000); + clk_set_parent(clks[IMX6SL_CLK_PXP_AXI_SEL], clks[IMX6SL_CLK_PLL2_PFD2]); + clk_set_rate(clks[IMX6SL_CLK_PXP_AXI], 200000000); + clk_set_parent(clks[IMX6SL_CLK_LCDIF_AXI_SEL], clks[IMX6SL_CLK_PLL2_PFD2]); + clk_set_rate(clks[IMX6SL_CLK_LCDIF_AXI], 200000000); + + /* Audio clocks */ + clk_set_parent(clks[IMX6SL_CLK_SPDIF0_SEL], clks[IMX6SL_CLK_PLL3_PFD3]); + + /* set extern_audio to be sourced from PLL4/audio PLL */ + clk_set_parent(clks[IMX6SL_CLK_EXTERN_AUDIO_SEL], clks[IMX6SL_CLK_PLL4_AUDIO_DIV]); + /* set extern_audio to 24MHz */ + clk_set_rate(clks[IMX6SL_CLK_PLL4_AUDIO], 24000000); + clk_set_rate(clks[IMX6SL_CLK_EXTERN_AUDIO], 24000000); + + /* set SSI2 parent to PLL4 */ + clk_set_parent(clks[IMX6SL_CLK_SSI2_SEL], clks[IMX6SL_CLK_PLL4_AUDIO_DIV]); + clk_set_rate(clks[IMX6SL_CLK_SSI2], 24000000); + + /* set perclk to source from OSC 24MHz */ + clk_set_parent(clks[IMX6SL_CLK_PERCLK_SEL], clks[IMX6SL_CLK_OSC]); + + /* Set initial power mode */ + imx6_set_lpm(WAIT_CLOCKED); + + /* Ensure that CH0 handshake is bypassed. */ + reg = readl_relaxed(base + CCM_CCDR_OFFSET); + reg |= 1 << CCDR_CH0_HS_BYP; + writel_relaxed(reg, base + CCM_CCDR_OFFSET); + + /* Set the UART parent if needed. */ + if (uart_from_osc) + ret = clk_set_parent(clks[IMX6SL_CLK_UART_SEL], clks[IMX6SL_CLK_UART_OSC_4M]); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-gpt"); + base = of_iomap(np, 0); + WARN_ON(!base); + irq = irq_of_parse_and_map(np, 0); + mxc_timer_init(base, irq); +} +CLK_OF_DECLARE(imx6sl, "fsl,imx6sl-ccm", imx6sl_clocks_init); diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-pfd.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-pfd.c --- linux-3.10.30/arch/arm/mach-imx/clk-pfd.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-pfd.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012-2013 Freescale Semiconductor, Inc. * Copyright 2012 Linaro Ltd. * * The code contained herein is licensed under the GNU General Public @@ -17,6 +17,8 @@ #include #include "clk.h" +#define BYPASS_RATE 24000000 + /** * struct clk_pfd - IMX PFD clock * @clk_hw: clock source @@ -62,9 +64,14 @@ u64 tmp = parent_rate; u8 frac = (readl_relaxed(pfd->reg) >> (pfd->idx * 8)) & 0x3f; - tmp *= 18; - do_div(tmp, frac); - + /* + * If the parent PLL is in bypass state, the PFDs + * are also in bypass state. + */ + if (tmp != BYPASS_RATE) { + tmp *= 18; + do_div(tmp, frac); + } return tmp; } @@ -74,17 +81,22 @@ u64 tmp = *prate; u8 frac; - tmp = tmp * 18 + rate / 2; - do_div(tmp, rate); - frac = tmp; - if (frac < 12) - frac = 12; - else if (frac > 35) - frac = 35; - tmp = *prate; - tmp *= 18; - do_div(tmp, frac); - + /* + * If the parent PLL is in bypass state, the PFDs + * are also in bypass state. + */ + if (tmp != BYPASS_RATE) { + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + if (frac < 12) + frac = 12; + else if (frac > 35) + frac = 35; + tmp = *prate; + tmp *= 18; + do_div(tmp, frac); + } return tmp; } @@ -95,6 +107,9 @@ u64 tmp = parent_rate; u8 frac; + if (tmp == BYPASS_RATE) + return 0; + tmp = tmp * 18 + rate / 2; do_div(tmp, rate); frac = tmp; @@ -109,12 +124,24 @@ return 0; } +static int clk_pfd_is_enabled(struct clk_hw *hw) +{ + struct clk_pfd *pfd = to_clk_pfd(hw); + + if (readl_relaxed(pfd->reg) & + (1 << ((pfd->idx + 1) * 8 - 1))) + return 0; + + return 1; +} + static const struct clk_ops clk_pfd_ops = { .enable = clk_pfd_enable, .disable = clk_pfd_disable, .recalc_rate = clk_pfd_recalc_rate, .round_rate = clk_pfd_round_rate, .set_rate = clk_pfd_set_rate, + .is_enabled = clk_pfd_is_enabled, }; struct clk *imx_clk_pfd(const char *name, const char *parent_name, diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-pllv3.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-pllv3.c --- linux-3.10.30/arch/arm/mach-imx/clk-pllv3.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-pllv3.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012-2013 Freescale Semiconductor, Inc. * Copyright 2012 Linaro Ltd. * * The code contained herein is licensed under the GNU General Public @@ -25,12 +25,15 @@ #define BM_PLL_ENABLE (0x1 << 13) #define BM_PLL_BYPASS (0x1 << 16) #define BM_PLL_LOCK (0x1 << 31) +#define BYPASS_RATE 24000000 +#define BYPASS_MASK 0x10000 /** * struct clk_pllv3 - IMX PLL clock version 3 * @clk_hw: clock source * @base: base address of PLL registers * @powerup_set: set POWER bit to power up the PLL + * @always_on : Leave the PLL powered up all the time. * @div_mask: mask of divider bits * * IMX PLL clock version 3, found on i.MX6 series. Divider for pllv3 @@ -40,52 +43,72 @@ struct clk_hw hw; void __iomem *base; bool powerup_set; + bool always_on; u32 div_mask; + u32 rate_req; }; #define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw) -static int clk_pllv3_prepare(struct clk_hw *hw) +static int clk_pllv3_wait_for_lock(struct clk_pllv3 *pll, u32 timeout_ms) { - struct clk_pllv3 *pll = to_clk_pllv3(hw); - unsigned long timeout = jiffies + msecs_to_jiffies(10); - u32 val; + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); + u32 val = readl_relaxed(pll->base) & BM_PLL_POWER; - val = readl_relaxed(pll->base); - val &= ~BM_PLL_BYPASS; - if (pll->powerup_set) - val |= BM_PLL_POWER; - else - val &= ~BM_PLL_POWER; - writel_relaxed(val, pll->base); + /* No need to wait for lock when pll is power down */ + if ((pll->powerup_set && !val) || (!pll->powerup_set && val)) + return 0; /* Wait for PLL to lock */ - while (!(readl_relaxed(pll->base) & BM_PLL_LOCK)) + do { + if (readl_relaxed(pll->base) & BM_PLL_LOCK) + break; if (time_after(jiffies, timeout)) - return -ETIMEDOUT; + break; + } while (1); - return 0; + if (readl_relaxed(pll->base) & BM_PLL_LOCK) + return 0; + else + return -ETIMEDOUT; } -static void clk_pllv3_unprepare(struct clk_hw *hw) +static int clk_pllv3_power_up_down(struct clk_hw *hw, bool enable) { struct clk_pllv3 *pll = to_clk_pllv3(hw); - u32 val; + u32 val, ret = 0; - val = readl_relaxed(pll->base); - val |= BM_PLL_BYPASS; - if (pll->powerup_set) - val &= ~BM_PLL_POWER; - else - val |= BM_PLL_POWER; - writel_relaxed(val, pll->base); + if (enable) { + val = readl_relaxed(pll->base); + val &= ~BM_PLL_BYPASS; + if (pll->powerup_set) + val |= BM_PLL_POWER; + else + val &= ~BM_PLL_POWER; + writel_relaxed(val, pll->base); + + ret = clk_pllv3_wait_for_lock(pll, 10); + } else { + val = readl_relaxed(pll->base); + val |= BM_PLL_BYPASS; + if (pll->powerup_set) + val &= ~BM_PLL_POWER; + else + val |= BM_PLL_POWER; + writel_relaxed(val, pll->base); + } + return ret; } + static int clk_pllv3_enable(struct clk_hw *hw) { struct clk_pllv3 *pll = to_clk_pllv3(hw); u32 val; + if (pll->rate_req != BYPASS_RATE) + clk_pllv3_power_up_down(hw, true); + val = readl_relaxed(pll->base); val |= BM_PLL_ENABLE; writel_relaxed(val, pll->base); @@ -99,8 +122,12 @@ u32 val; val = readl_relaxed(pll->base); - val &= ~BM_PLL_ENABLE; + if (!pll->always_on) + val &= ~BM_PLL_ENABLE; writel_relaxed(val, pll->base); + + if (pll->rate_req != BYPASS_RATE) + clk_pllv3_power_up_down(hw, false); } static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw, @@ -108,8 +135,15 @@ { struct clk_pllv3 *pll = to_clk_pllv3(hw); u32 div = readl_relaxed(pll->base) & pll->div_mask; + u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; + u32 rate; + + if (pll->rate_req == BYPASS_RATE && bypass) + rate = BYPASS_RATE; + else + rate = (div == 1) ? parent_rate * 22 : parent_rate * 20; - return (div == 1) ? parent_rate * 22 : parent_rate * 20; + return rate; } static long clk_pllv3_round_rate(struct clk_hw *hw, unsigned long rate, @@ -117,6 +151,10 @@ { unsigned long parent_rate = *prate; + /* If the PLL is bypassed, its rate is 24MHz. */ + if (rate == BYPASS_RATE) + return BYPASS_RATE; + return (rate >= parent_rate * 22) ? parent_rate * 22 : parent_rate * 20; } @@ -127,6 +165,22 @@ struct clk_pllv3 *pll = to_clk_pllv3(hw); u32 val, div; + pll->rate_req = rate; + val = readl_relaxed(pll->base); + + /* If the PLL is bypassed, its rate is 24MHz. */ + if (rate == BYPASS_RATE) { + /* Set the bypass bit. */ + val |= BM_PLL_BYPASS; + /* Power down the PLL. */ + if (pll->powerup_set) + val &= ~BM_PLL_POWER; + else + val |= BM_PLL_POWER; + writel_relaxed(val, pll->base); + + return 0; + } if (rate == parent_rate * 22) div = 1; else if (rate == parent_rate * 20) @@ -139,12 +193,10 @@ val |= div; writel_relaxed(val, pll->base); - return 0; + return clk_pllv3_wait_for_lock(pll, 10); } static const struct clk_ops clk_pllv3_ops = { - .prepare = clk_pllv3_prepare, - .unprepare = clk_pllv3_unprepare, .enable = clk_pllv3_enable, .disable = clk_pllv3_disable, .recalc_rate = clk_pllv3_recalc_rate, @@ -157,6 +209,10 @@ { struct clk_pllv3 *pll = to_clk_pllv3(hw); u32 div = readl_relaxed(pll->base) & pll->div_mask; + u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; + + if (pll->rate_req == BYPASS_RATE && bypass) + return BYPASS_RATE; return parent_rate * div / 2; } @@ -169,6 +225,9 @@ unsigned long max_rate = parent_rate * 108 / 2; u32 div; + if (rate == BYPASS_RATE) + return BYPASS_RATE; + if (rate > max_rate) rate = max_rate; else if (rate < min_rate) @@ -186,21 +245,36 @@ unsigned long max_rate = parent_rate * 108 / 2; u32 val, div; - if (rate < min_rate || rate > max_rate) + if (rate != BYPASS_RATE && (rate < min_rate || rate > max_rate)) return -EINVAL; + pll->rate_req = rate; + val = readl_relaxed(pll->base); + + if (rate == BYPASS_RATE) { + /* + * Set the PLL in bypass mode if rate requested is + * BYPASS_RATE. + */ + val |= BM_PLL_BYPASS; + /* Power down the PLL. */ + if (pll->powerup_set) + val &= ~BM_PLL_POWER; + else + val |= BM_PLL_POWER; + writel_relaxed(val, pll->base); + return 0; + } div = rate * 2 / parent_rate; val = readl_relaxed(pll->base); val &= ~pll->div_mask; val |= div; writel_relaxed(val, pll->base); - return 0; + return clk_pllv3_wait_for_lock(pll, 10); } static const struct clk_ops clk_pllv3_sys_ops = { - .prepare = clk_pllv3_prepare, - .unprepare = clk_pllv3_unprepare, .enable = clk_pllv3_enable, .disable = clk_pllv3_disable, .recalc_rate = clk_pllv3_sys_recalc_rate, @@ -215,6 +289,10 @@ u32 mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET); u32 mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET); u32 div = readl_relaxed(pll->base) & pll->div_mask; + u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; + + if (pll->rate_req == BYPASS_RATE && bypass) + return BYPASS_RATE; return (parent_rate * div) + ((parent_rate / mfd) * mfn); } @@ -229,6 +307,9 @@ u32 mfn, mfd = 1000000; s64 temp64; + if (rate == BYPASS_RATE) + return BYPASS_RATE; + if (rate > max_rate) rate = max_rate; else if (rate < min_rate) @@ -249,13 +330,36 @@ struct clk_pllv3 *pll = to_clk_pllv3(hw); unsigned long min_rate = parent_rate * 27; unsigned long max_rate = parent_rate * 54; - u32 val, div; + u32 val, newval, div; u32 mfn, mfd = 1000000; s64 temp64; + int ret; - if (rate < min_rate || rate > max_rate) + if (rate != BYPASS_RATE && (rate < min_rate || rate > max_rate)) return -EINVAL; + pll->rate_req = rate; + val = readl_relaxed(pll->base); + + if (rate == BYPASS_RATE) { + /* + * Set the PLL in bypass mode if rate requested is + * BYPASS_RATE. + */ + /* Bypass the PLL */ + val |= BM_PLL_BYPASS; + /* Power down the PLL. */ + if (pll->powerup_set) + val &= ~BM_PLL_POWER; + else + val |= BM_PLL_POWER; + writel_relaxed(val, pll->base); + return 0; + } + /* Else clear the bypass bit. */ + val &= ~BM_PLL_BYPASS; + writel_relaxed(val, pll->base); + div = rate / parent_rate; temp64 = (u64) (rate - div * parent_rate); temp64 *= mfd; @@ -263,18 +367,30 @@ mfn = temp64; val = readl_relaxed(pll->base); - val &= ~pll->div_mask; - val |= div; - writel_relaxed(val, pll->base); + + /* set the PLL into bypass mode */ + newval = val | BM_PLL_BYPASS; + writel_relaxed(newval, pll->base); + + /* configure the new frequency */ + newval &= ~pll->div_mask; + newval |= div; + writel_relaxed(newval, pll->base); writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET); - writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET); + writel(mfd, pll->base + PLL_DENOM_OFFSET); - return 0; + ret = clk_pllv3_wait_for_lock(pll, 10); + if (ret == 0 && val & BM_PLL_POWER) { + /* only if it locked can we switch back to the PLL */ + newval &= ~BM_PLL_BYPASS; + newval |= val & BM_PLL_BYPASS; + writel(newval, pll->base); + } + + return ret; } static const struct clk_ops clk_pllv3_av_ops = { - .prepare = clk_pllv3_prepare, - .unprepare = clk_pllv3_unprepare, .enable = clk_pllv3_enable, .disable = clk_pllv3_disable, .recalc_rate = clk_pllv3_av_recalc_rate, @@ -289,23 +405,19 @@ } static const struct clk_ops clk_pllv3_enet_ops = { - .prepare = clk_pllv3_prepare, - .unprepare = clk_pllv3_unprepare, .enable = clk_pllv3_enable, .disable = clk_pllv3_disable, .recalc_rate = clk_pllv3_enet_recalc_rate, }; static const struct clk_ops clk_pllv3_mlb_ops = { - .prepare = clk_pllv3_prepare, - .unprepare = clk_pllv3_unprepare, .enable = clk_pllv3_enable, .disable = clk_pllv3_disable, }; struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, const char *parent_name, void __iomem *base, - u32 div_mask) + u32 div_mask, bool always_on) { struct clk_pllv3 *pll; const struct clk_ops *ops; @@ -338,6 +450,7 @@ } pll->base = base; pll->div_mask = div_mask; + pll->always_on = always_on; init.name = name; init.ops = ops; diff -Nur linux-3.10.30/arch/arm/mach-imx/clk-vf610.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-vf610.c --- linux-3.10.30/arch/arm/mach-imx/clk-vf610.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk-vf610.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,321 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, 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. + * + */ + +#include +#include +#include + +#include "clk.h" + +#define CCM_CCR (ccm_base + 0x00) +#define CCM_CSR (ccm_base + 0x04) +#define CCM_CCSR (ccm_base + 0x08) +#define CCM_CACRR (ccm_base + 0x0c) +#define CCM_CSCMR1 (ccm_base + 0x10) +#define CCM_CSCDR1 (ccm_base + 0x14) +#define CCM_CSCDR2 (ccm_base + 0x18) +#define CCM_CSCDR3 (ccm_base + 0x1c) +#define CCM_CSCMR2 (ccm_base + 0x20) +#define CCM_CSCDR4 (ccm_base + 0x24) +#define CCM_CLPCR (ccm_base + 0x2c) +#define CCM_CISR (ccm_base + 0x30) +#define CCM_CIMR (ccm_base + 0x34) +#define CCM_CGPR (ccm_base + 0x3c) +#define CCM_CCGR0 (ccm_base + 0x40) +#define CCM_CCGR1 (ccm_base + 0x44) +#define CCM_CCGR2 (ccm_base + 0x48) +#define CCM_CCGR3 (ccm_base + 0x4c) +#define CCM_CCGR4 (ccm_base + 0x50) +#define CCM_CCGR5 (ccm_base + 0x54) +#define CCM_CCGR6 (ccm_base + 0x58) +#define CCM_CCGR7 (ccm_base + 0x5c) +#define CCM_CCGR8 (ccm_base + 0x60) +#define CCM_CCGR9 (ccm_base + 0x64) +#define CCM_CCGR10 (ccm_base + 0x68) +#define CCM_CCGR11 (ccm_base + 0x6c) +#define CCM_CMEOR0 (ccm_base + 0x70) +#define CCM_CMEOR1 (ccm_base + 0x74) +#define CCM_CMEOR2 (ccm_base + 0x78) +#define CCM_CMEOR3 (ccm_base + 0x7c) +#define CCM_CMEOR4 (ccm_base + 0x80) +#define CCM_CMEOR5 (ccm_base + 0x84) +#define CCM_CPPDSR (ccm_base + 0x88) +#define CCM_CCOWR (ccm_base + 0x8c) +#define CCM_CCPGR0 (ccm_base + 0x90) +#define CCM_CCPGR1 (ccm_base + 0x94) +#define CCM_CCPGR2 (ccm_base + 0x98) +#define CCM_CCPGR3 (ccm_base + 0x9c) + +#define CCM_CCGRx_CGn(n) ((n) * 2) + +#define PFD_PLL1_BASE (anatop_base + 0x2b0) +#define PFD_PLL2_BASE (anatop_base + 0x100) +#define PFD_PLL3_BASE (anatop_base + 0xf0) + +static void __iomem *anatop_base; +static void __iomem *ccm_base; + +/* sources for multiplexer clocks, this is used multiple times */ +static const char const *fast_sels[] = { "firc", "fxosc", }; +static const char const *slow_sels[] = { "sirc_32k", "sxosc", }; +static const char const *pll1_sels[] = { "pll1_main", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", }; +static const char const *pll2_sels[] = { "pll2_main", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", }; +static const char const *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_main", "pll1_pfd_sel", "pll3_main", }; +static const char const *ddr_sels[] = { "pll2_pfd2", "sys_sel", }; +static const char const *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", }; +static const char const *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", }; +static const char const *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", }; +static const char const *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", }; +static const char const *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", }; +static const char const *qspi_sels[] = { "pll3_main", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", }; +static const char const *esdhc_sels[] = { "pll3_main", "pll3_pfd3", "pll1_pfd3", "platform_bus", }; +static const char const *dcu_sels[] = { "pll1_pfd2", "pll3_main", }; +static const char const *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", }; +static const char const *vadc_sels[] = { "pll6_main_div", "pll3_main_div", "pll3_main", }; +/* FTM counter clock source, not module clock */ +static const char const *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", }; +static const char const *ftm_fix_sels[] = { "sxosc", "ipg_bus", }; + +static struct clk_div_table pll4_main_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 6 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 10 }, + { .val = 5, .div = 12 }, + { .val = 6, .div = 14 }, + { .val = 7, .div = 16 }, + { } +}; + +static struct clk *clk[VF610_CLK_END]; +static struct clk_onecell_data clk_data; + +static void __init vf610_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + + clk[VF610_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[VF610_CLK_SIRC_128K] = imx_clk_fixed("sirc_128k", 128000); + clk[VF610_CLK_SIRC_32K] = imx_clk_fixed("sirc_32k", 32000); + clk[VF610_CLK_FIRC] = imx_clk_fixed("firc", 24000000); + + clk[VF610_CLK_SXOSC] = imx_obtain_fixed_clock("sxosc", 0); + clk[VF610_CLK_FXOSC] = imx_obtain_fixed_clock("fxosc", 0); + clk[VF610_CLK_AUDIO_EXT] = imx_obtain_fixed_clock("audio_ext", 0); + clk[VF610_CLK_ENET_EXT] = imx_obtain_fixed_clock("enet_ext", 0); + + clk[VF610_CLK_FXOSC_HALF] = imx_clk_fixed_factor("fxosc_half", "fxosc", 1, 2); + + np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop"); + anatop_base = of_iomap(np, 0); + BUG_ON(!anatop_base); + + np = ccm_node; + ccm_base = of_iomap(np, 0); + BUG_ON(!ccm_base); + + clk[VF610_CLK_SLOW_CLK_SEL] = imx_clk_mux("slow_clk_sel", CCM_CCSR, 4, 1, slow_sels, ARRAY_SIZE(slow_sels)); + clk[VF610_CLK_FASK_CLK_SEL] = imx_clk_mux("fast_clk_sel", CCM_CCSR, 5, 1, fast_sels, ARRAY_SIZE(fast_sels)); + + clk[VF610_CLK_PLL1_MAIN] = imx_clk_fixed_factor("pll1_main", "fast_clk_sel", 22, 1); + clk[VF610_CLK_PLL1_PFD1] = imx_clk_pfd("pll1_pfd1", "pll1_main", PFD_PLL1_BASE, 0); + clk[VF610_CLK_PLL1_PFD2] = imx_clk_pfd("pll1_pfd2", "pll1_main", PFD_PLL1_BASE, 1); + clk[VF610_CLK_PLL1_PFD3] = imx_clk_pfd("pll1_pfd3", "pll1_main", PFD_PLL1_BASE, 2); + clk[VF610_CLK_PLL1_PFD4] = imx_clk_pfd("pll1_pfd4", "pll1_main", PFD_PLL1_BASE, 3); + + clk[VF610_CLK_PLL2_MAIN] = imx_clk_fixed_factor("pll2_main", "fast_clk_sel", 22, 1); + clk[VF610_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_main", PFD_PLL2_BASE, 0); + clk[VF610_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_main", PFD_PLL2_BASE, 1); + clk[VF610_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3", "pll2_main", PFD_PLL2_BASE, 2); + clk[VF610_CLK_PLL2_PFD4] = imx_clk_pfd("pll2_pfd4", "pll2_main", PFD_PLL2_BASE, 3); + + clk[VF610_CLK_PLL3_MAIN] = imx_clk_fixed_factor("pll3_main", "fast_clk_sel", 20, 1); + clk[VF610_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_main", PFD_PLL3_BASE, 0); + clk[VF610_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_main", PFD_PLL3_BASE, 1); + clk[VF610_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_main", PFD_PLL3_BASE, 2); + clk[VF610_CLK_PLL3_PFD4] = imx_clk_pfd("pll3_pfd4", "pll3_main", PFD_PLL3_BASE, 3); + + clk[VF610_CLK_PLL4_MAIN] = imx_clk_fixed_factor("pll4_main", "fast_clk_sel", 25, 1); + /* Enet pll: fixed 50Mhz */ + clk[VF610_CLK_PLL5_MAIN] = imx_clk_fixed_factor("pll5_main", "fast_clk_sel", 125, 6); + /* pll6: default 960Mhz */ + clk[VF610_CLK_PLL6_MAIN] = imx_clk_fixed_factor("pll6_main", "fast_clk_sel", 40, 1); + clk[VF610_CLK_PLL1_PFD_SEL] = imx_clk_mux("pll1_pfd_sel", CCM_CCSR, 16, 3, pll1_sels, 5); + clk[VF610_CLK_PLL2_PFD_SEL] = imx_clk_mux("pll2_pfd_sel", CCM_CCSR, 19, 3, pll2_sels, 5); + clk[VF610_CLK_SYS_SEL] = imx_clk_mux("sys_sel", CCM_CCSR, 0, 3, sys_sels, ARRAY_SIZE(sys_sels)); + clk[VF610_CLK_DDR_SEL] = imx_clk_mux("ddr_sel", CCM_CCSR, 6, 1, ddr_sels, ARRAY_SIZE(ddr_sels)); + clk[VF610_CLK_SYS_BUS] = imx_clk_divider("sys_bus", "sys_sel", CCM_CACRR, 0, 3); + clk[VF610_CLK_PLATFORM_BUS] = imx_clk_divider("platform_bus", "sys_bus", CCM_CACRR, 3, 3); + clk[VF610_CLK_IPG_BUS] = imx_clk_divider("ipg_bus", "platform_bus", CCM_CACRR, 11, 2); + + clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_main_div", "pll3_main", CCM_CACRR, 20, 1); + clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_main_div", "pll4_main", 0, CCM_CACRR, 6, 3, 0, pll4_main_div_table, &imx_ccm_lock); + clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_main_div", "pll6_main", CCM_CACRR, 21, 1); + + clk[VF610_CLK_USBC0] = imx_clk_gate2("usbc0", "pll3_main", CCM_CCGR1, CCM_CCGRx_CGn(4)); + clk[VF610_CLK_USBC1] = imx_clk_gate2("usbc1", "pll3_main", CCM_CCGR7, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_QSPI0_SEL] = imx_clk_mux("qspi0_sel", CCM_CSCMR1, 22, 2, qspi_sels, 4); + clk[VF610_CLK_QSPI0_EN] = imx_clk_gate("qspi0_en", "qspi0_sel", CCM_CSCDR3, 4); + clk[VF610_CLK_QSPI0_X4_DIV] = imx_clk_divider("qspi0_x4", "qspi0_en", CCM_CSCDR3, 0, 2); + clk[VF610_CLK_QSPI0_X2_DIV] = imx_clk_divider("qspi0_x2", "qspi0_x4", CCM_CSCDR3, 2, 1); + clk[VF610_CLK_QSPI0_X1_DIV] = imx_clk_divider("qspi0_x1", "qspi0_x2", CCM_CSCDR3, 3, 1); + clk[VF610_CLK_QSPI0] = imx_clk_gate2("qspi0", "qspi0_x1", CCM_CCGR2, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_QSPI1_SEL] = imx_clk_mux("qspi1_sel", CCM_CSCMR1, 24, 2, qspi_sels, 4); + clk[VF610_CLK_QSPI1_EN] = imx_clk_gate("qspi1_en", "qspi1_sel", CCM_CSCDR3, 12); + clk[VF610_CLK_QSPI1_X4_DIV] = imx_clk_divider("qspi1_x4", "qspi1_en", CCM_CSCDR3, 8, 2); + clk[VF610_CLK_QSPI1_X2_DIV] = imx_clk_divider("qspi1_x2", "qspi1_x4", CCM_CSCDR3, 10, 1); + clk[VF610_CLK_QSPI1_X1_DIV] = imx_clk_divider("qspi1_x1", "qspi1_x2", CCM_CSCDR3, 11, 1); + clk[VF610_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_x1", CCM_CCGR8, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_ENET_50M] = imx_clk_fixed_factor("enet_50m", "pll5_main", 1, 10); + clk[VF610_CLK_ENET_25M] = imx_clk_fixed_factor("enet_25m", "pll5_main", 1, 20); + clk[VF610_CLK_ENET_SEL] = imx_clk_mux("enet_sel", CCM_CSCMR2, 4, 2, rmii_sels, 4); + clk[VF610_CLK_ENET_TS_SEL] = imx_clk_mux("enet_ts_sel", CCM_CSCMR2, 0, 3, enet_ts_sels, 7); + clk[VF610_CLK_ENET] = imx_clk_gate("enet", "enet_sel", CCM_CSCDR1, 24); + clk[VF610_CLK_ENET_TS] = imx_clk_gate("enet_ts", "enet_ts_sel", CCM_CSCDR1, 23); + clk[VF610_CLK_ENET0] = imx_clk_gate2("enet0", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(0)); + clk[VF610_CLK_ENET1] = imx_clk_gate2("enet1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_UART0] = imx_clk_gate2("uart0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(7)); + clk[VF610_CLK_UART1] = imx_clk_gate2("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_UART2] = imx_clk_gate2("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9)); + clk[VF610_CLK_UART3] = imx_clk_gate2("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10)); + + clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(6)); + clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_DSPI0] = imx_clk_gate2("dspi0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DSPI1] = imx_clk_gate2("dspi1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(13)); + clk[VF610_CLK_DSPI2] = imx_clk_gate2("dspi2", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DSPI3] = imx_clk_gate2("dspi3", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_WDT] = imx_clk_gate2("wdt", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(14)); + + clk[VF610_CLK_ESDHC0_SEL] = imx_clk_mux("esdhc0_sel", CCM_CSCMR1, 16, 2, esdhc_sels, 4); + clk[VF610_CLK_ESDHC0_EN] = imx_clk_gate("esdhc0_en", "esdhc0_sel", CCM_CSCDR2, 28); + clk[VF610_CLK_ESDHC0_DIV] = imx_clk_divider("esdhc0_div", "esdhc0_en", CCM_CSCDR2, 16, 4); + clk[VF610_CLK_ESDHC0] = imx_clk_gate2("eshc0", "esdhc0_div", CCM_CCGR7, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_ESDHC1_SEL] = imx_clk_mux("esdhc1_sel", CCM_CSCMR1, 18, 2, esdhc_sels, 4); + clk[VF610_CLK_ESDHC1_EN] = imx_clk_gate("esdhc1_en", "esdhc1_sel", CCM_CSCDR2, 29); + clk[VF610_CLK_ESDHC1_DIV] = imx_clk_divider("esdhc1_div", "esdhc1_en", CCM_CSCDR2, 20, 4); + clk[VF610_CLK_ESDHC1] = imx_clk_gate2("eshc1", "esdhc1_div", CCM_CCGR7, CCM_CCGRx_CGn(2)); + + /* + * ftm_ext_clk and ftm_fix_clk are FTM timer counter's + * selectable clock sources, both use a common enable bit + * in CCM_CSCDR1, selecting "dummy" clock as parent of + * "ftm0_ext_fix" make it serve only for enable/disable. + */ + clk[VF610_CLK_FTM0_EXT_SEL] = imx_clk_mux("ftm0_ext_sel", CCM_CSCMR2, 6, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM0_FIX_SEL] = imx_clk_mux("ftm0_fix_sel", CCM_CSCMR2, 14, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM0_EXT_FIX_EN] = imx_clk_gate("ftm0_ext_fix_en", "dummy", CCM_CSCDR1, 25); + clk[VF610_CLK_FTM1_EXT_SEL] = imx_clk_mux("ftm1_ext_sel", CCM_CSCMR2, 8, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM1_FIX_SEL] = imx_clk_mux("ftm1_fix_sel", CCM_CSCMR2, 15, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM1_EXT_FIX_EN] = imx_clk_gate("ftm1_ext_fix_en", "dummy", CCM_CSCDR1, 26); + clk[VF610_CLK_FTM2_EXT_SEL] = imx_clk_mux("ftm2_ext_sel", CCM_CSCMR2, 10, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM2_FIX_SEL] = imx_clk_mux("ftm2_fix_sel", CCM_CSCMR2, 16, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM2_EXT_FIX_EN] = imx_clk_gate("ftm2_ext_fix_en", "dummy", CCM_CSCDR1, 27); + clk[VF610_CLK_FTM3_EXT_SEL] = imx_clk_mux("ftm3_ext_sel", CCM_CSCMR2, 12, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM3_FIX_SEL] = imx_clk_mux("ftm3_fix_sel", CCM_CSCMR2, 17, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM3_EXT_FIX_EN] = imx_clk_gate("ftm3_ext_fix_en", "dummy", CCM_CSCDR1, 28); + + /* ftm(n)_clk are FTM module operation clock */ + clk[VF610_CLK_FTM0] = imx_clk_gate2("ftm0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_FTM1] = imx_clk_gate2("ftm1", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(9)); + clk[VF610_CLK_FTM2] = imx_clk_gate2("ftm2", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_FTM3] = imx_clk_gate2("ftm3", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(9)); + + clk[VF610_CLK_DCU0_SEL] = imx_clk_mux("dcu0_sel", CCM_CSCMR1, 28, 1, dcu_sels, 2); + clk[VF610_CLK_DCU0_EN] = imx_clk_gate("dcu0_en", "dcu0_sel", CCM_CSCDR3, 19); + clk[VF610_CLK_DCU0_DIV] = imx_clk_divider("dcu0_div", "dcu0_en", CCM_CSCDR3, 16, 3); + clk[VF610_CLK_DCU0] = imx_clk_gate2("dcu0", "dcu0_div", CCM_CCGR3, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_DCU1_SEL] = imx_clk_mux("dcu1_sel", CCM_CSCMR1, 29, 1, dcu_sels, 2); + clk[VF610_CLK_DCU1_EN] = imx_clk_gate("dcu1_en", "dcu1_sel", CCM_CSCDR3, 23); + clk[VF610_CLK_DCU1_DIV] = imx_clk_divider("dcu1_div", "dcu1_en", CCM_CSCDR3, 20, 3); + clk[VF610_CLK_DCU1] = imx_clk_gate2("dcu1", "dcu1_div", CCM_CCGR9, CCM_CCGRx_CGn(8)); + + clk[VF610_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", CCM_CSCMR1, 20, 2, esai_sels, 4); + clk[VF610_CLK_ESAI_EN] = imx_clk_gate("esai_en", "esai_sel", CCM_CSCDR2, 30); + clk[VF610_CLK_ESAI_DIV] = imx_clk_divider("esai_div", "esai_en", CCM_CSCDR2, 24, 4); + clk[VF610_CLK_ESAI] = imx_clk_gate2("esai", "esai_div", CCM_CCGR4, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_SAI0_SEL] = imx_clk_mux("sai0_sel", CCM_CSCMR1, 0, 2, sai_sels, 4); + clk[VF610_CLK_SAI0_EN] = imx_clk_gate("sai0_en", "sai0_sel", CCM_CSCDR1, 16); + clk[VF610_CLK_SAI0_DIV] = imx_clk_divider("sai0_div", "sai0_en", CCM_CSCDR1, 0, 4); + clk[VF610_CLK_SAI0] = imx_clk_gate2("sai0", "sai0_div", CCM_CCGR0, CCM_CCGRx_CGn(15)); + + clk[VF610_CLK_SAI1_SEL] = imx_clk_mux("sai1_sel", CCM_CSCMR1, 2, 2, sai_sels, 4); + clk[VF610_CLK_SAI1_EN] = imx_clk_gate("sai1_en", "sai1_sel", CCM_CSCDR1, 17); + clk[VF610_CLK_SAI1_DIV] = imx_clk_divider("sai1_div", "sai1_en", CCM_CSCDR1, 4, 4); + clk[VF610_CLK_SAI1] = imx_clk_gate2("sai1", "sai1_div", CCM_CCGR1, CCM_CCGRx_CGn(0)); + + clk[VF610_CLK_SAI2_SEL] = imx_clk_mux("sai2_sel", CCM_CSCMR1, 4, 2, sai_sels, 4); + clk[VF610_CLK_SAI2_EN] = imx_clk_gate("sai2_en", "sai2_sel", CCM_CSCDR1, 18); + clk[VF610_CLK_SAI2_DIV] = imx_clk_divider("sai2_div", "sai2_en", CCM_CSCDR1, 8, 4); + clk[VF610_CLK_SAI2] = imx_clk_gate2("sai2", "sai2_div", CCM_CCGR1, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_SAI3_SEL] = imx_clk_mux("sai3_sel", CCM_CSCMR1, 6, 2, sai_sels, 4); + clk[VF610_CLK_SAI3_EN] = imx_clk_gate("sai3_en", "sai3_sel", CCM_CSCDR1, 19); + clk[VF610_CLK_SAI3_DIV] = imx_clk_divider("sai3_div", "sai3_en", CCM_CSCDR1, 12, 4); + clk[VF610_CLK_SAI3] = imx_clk_gate2("sai3", "sai3_div", CCM_CCGR1, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_NFC_SEL] = imx_clk_mux("nfc_sel", CCM_CSCMR1, 12, 2, nfc_sels, 4); + clk[VF610_CLK_NFC_EN] = imx_clk_gate("nfc_en", "nfc_sel", CCM_CSCDR2, 9); + clk[VF610_CLK_NFC_PRE_DIV] = imx_clk_divider("nfc_pre_div", "nfc_en", CCM_CSCDR3, 13, 3); + clk[VF610_CLK_NFC_FRAC_DIV] = imx_clk_divider("nfc_frac_div", "nfc_pre_div", CCM_CSCDR2, 4, 4); + clk[VF610_CLK_NFC] = imx_clk_gate2("nfc", "nfc_frac_div", CCM_CCGR10, CCM_CCGRx_CGn(0)); + + clk[VF610_CLK_GPU_SEL] = imx_clk_mux("gpu_sel", CCM_CSCMR1, 14, 1, gpu_sels, 2); + clk[VF610_CLK_GPU_EN] = imx_clk_gate("gpu_en", "gpu_sel", CCM_CSCDR2, 10); + clk[VF610_CLK_GPU2D] = imx_clk_gate2("gpu", "gpu_en", CCM_CCGR8, CCM_CCGRx_CGn(15)); + + clk[VF610_CLK_VADC_SEL] = imx_clk_mux("vadc_sel", CCM_CSCMR1, 8, 2, vadc_sels, 3); + clk[VF610_CLK_VADC_EN] = imx_clk_gate("vadc_en", "vadc_sel", CCM_CSCDR1, 22); + clk[VF610_CLK_VADC_DIV] = imx_clk_divider("vadc_div", "vadc_en", CCM_CSCDR1, 20, 2); + clk[VF610_CLK_VADC_DIV_HALF] = imx_clk_fixed_factor("vadc_div_half", "vadc_div", 1, 2); + clk[VF610_CLK_VADC] = imx_clk_gate2("vadc", "vadc_div", CCM_CCGR8, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_ADC0] = imx_clk_gate2("adc0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(11)); + clk[VF610_CLK_ADC1] = imx_clk_gate2("adc1", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(11)); + clk[VF610_CLK_DAC0] = imx_clk_gate2("dac0", "ipg_bus", CCM_CCGR8, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DAC1] = imx_clk_gate2("dac1", "ipg_bus", CCM_CCGR8, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_ASRC] = imx_clk_gate2("asrc", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_FLEXCAN0] = imx_clk_gate2("flexcan0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(0)); + clk[VF610_CLK_FLEXCAN1] = imx_clk_gate2("flexcan1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(4)); + + clk_set_parent(clk[VF610_CLK_QSPI0_SEL], clk[VF610_CLK_PLL1_PFD4]); + clk_set_rate(clk[VF610_CLK_QSPI0_X4_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_SEL]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI0_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_X4_DIV]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI0_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_X2_DIV]) / 2); + + clk_set_parent(clk[VF610_CLK_QSPI1_SEL], clk[VF610_CLK_PLL1_PFD4]); + clk_set_rate(clk[VF610_CLK_QSPI1_X4_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_SEL]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI1_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X4_DIV]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI1_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X2_DIV]) / 2); + + clk_set_parent(clk[VF610_CLK_SAI0_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI1_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI2_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI3_SEL], clk[VF610_CLK_AUDIO_EXT]); + + /* Add the clocks to provider list */ + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(vf610, "fsl,vf610-ccm", vf610_clocks_init); diff -Nur linux-3.10.30/arch/arm/mach-imx/clk.c linux-3.10.30-cubox-i/arch/arm/mach-imx/clk.c --- linux-3.10.30/arch/arm/mach-imx/clk.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,4 +1,65 @@ +#include +#include +#include +#include #include #include "clk.h" DEFINE_SPINLOCK(imx_ccm_lock); + +static struct clk * __init imx_obtain_fixed_clock_from_dt(const char *name) +{ + struct of_phandle_args phandle = {0}; + struct clk *clk = ERR_PTR(-ENODEV); + char *path; + + path = kasprintf(GFP_KERNEL, "/clocks/%s", name); + if (!path) + return ERR_PTR(-ENOMEM); + + phandle.np = of_find_node_by_path(path); + kfree(path); + + if (phandle.np) { + clk = of_clk_get_from_provider(&phandle); + of_node_put(phandle.np); + } + return clk; +} + +struct clk * __init imx_obtain_fixed_clock( + const char *name, unsigned long rate) +{ + struct clk *clk; + + clk = imx_obtain_fixed_clock_from_dt(name); + if (IS_ERR(clk)) + clk = imx_clk_fixed(name, rate); + return clk; +} + +/* + * This fixups the register CCM_CSCMR1 write value. + * The write/read/divider values of the aclk_podf field + * of that register have the relationship described by + * the following table: + * + * write value read value divider + * 3b'000 3b'110 7 + * 3b'001 3b'111 8 + * 3b'010 3b'100 5 + * 3b'011 3b'101 6 + * 3b'100 3b'010 3 + * 3b'101 3b'011 4 + * 3b'110 3b'000 1 + * 3b'111 3b'001 2(default) + * + * That's why we do the xor operation below. + */ +#define CSCMR1_FIXUP 0x00600000 + +void imx_cscmr1_fixup(u32 *val) +{ + *val ^= CSCMR1_FIXUP; + return; +} diff -Nur linux-3.10.30/arch/arm/mach-imx/clk.h linux-3.10.30-cubox-i/arch/arm/mach-imx/clk.h --- linux-3.10.30/arch/arm/mach-imx/clk.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/clk.h 2014-03-08 20:32:54.000000000 +0100 @@ -6,6 +6,8 @@ extern spinlock_t imx_ccm_lock; +extern void imx_cscmr1_fixup(u32 *val); + struct clk *imx_clk_pllv1(const char *name, const char *parent, void __iomem *base); @@ -22,13 +24,17 @@ }; struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, - const char *parent_name, void __iomem *base, u32 div_mask); + const char *parent_name, void __iomem *base, + u32 div_mask, bool always_on); struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock); +struct clk * imx_obtain_fixed_clock( + const char *name, unsigned long rate); + static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) { @@ -47,6 +53,14 @@ u8 width, void __iomem *busy_reg, u8 busy_shift, const char **parent_names, int num_parents); +struct clk *imx_clk_fixup_divider(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width, + void (*fixup)(u32 *val)); + +struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char **parents, + int num_parents, void (*fixup)(u32 *val)); + static inline struct clk *imx_clk_fixed(const char *name, int rate) { return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); diff -Nur linux-3.10.30/arch/arm/mach-imx/common.h linux-3.10.30-cubox-i/arch/arm/mach-imx/common.h --- linux-3.10.30/arch/arm/mach-imx/common.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/common.h 2014-03-08 20:32:54.000000000 +0100 @@ -14,6 +14,7 @@ struct platform_device; struct pt_regs; struct clk; +struct irq_data; enum mxc_cpu_pwr_mode; extern void mx1_map_io(void); @@ -68,17 +69,21 @@ extern int mx31_clocks_init_dt(void); extern int mx51_clocks_init_dt(void); extern int mx53_clocks_init_dt(void); -extern int mx6q_clocks_init(void); extern struct platform_device *mxc_register_gpio(char *name, int id, resource_size_t iobase, resource_size_t iosize, int irq, int irq_high); extern void mxc_set_cpu_type(unsigned int type); +extern void mxc_set_system_rev(unsigned int sysrev); extern void mxc_restart(char, const char *); extern void mxc_arch_reset_init(void __iomem *); +extern void mxc_arch_reset_init_dt(void); extern int mx53_revision(void); -extern int imx6q_revision(void); extern int mx53_display_revision(void); extern void imx_set_aips(void __iomem *); extern int mxc_device_init(void); +extern void imx_set_soc_revision(unsigned int rev); +extern unsigned int imx_get_soc_revision(void); +extern void imx_init_revision_from_anatop(void); +extern struct device *imx_soc_device_init(void); enum mxc_cpu_pwr_mode { WAIT_CLOCKED, /* wfi only */ @@ -125,30 +130,41 @@ static inline void imx_smp_prepare(void) {} static inline void imx_scu_standby_enable(void) {} #endif +extern void imx6_pm_map_io(void); +extern void imx6_suspend(void); extern void imx_src_init(void); +#ifdef CONFIG_HAVE_IMX_SRC extern void imx_src_prepare_restart(void); +#else +static inline void imx_src_prepare_restart(void) {} +#endif extern void imx_gpc_init(void); -extern void imx_gpc_pre_suspend(void); +extern void imx_gpc_pre_suspend(bool arm_power_off); extern void imx_gpc_post_resume(void); extern void imx_gpc_mask_all(void); +extern void imx_gpc_irq_mask(struct irq_data *d); +extern void imx_gpc_irq_unmask(struct irq_data *d); extern void imx_gpc_restore_all(void); extern void imx_anatop_init(void); extern void imx_anatop_pre_suspend(void); extern void imx_anatop_post_resume(void); -extern void imx_anatop_usb_chrg_detect_disable(void); -extern u32 imx_anatop_get_digprog(void); -extern int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode); -extern void imx6q_set_chicken_bit(void); +extern void imx_anatop_pu_enable(bool enable); +extern int imx6_set_lpm(enum mxc_cpu_pwr_mode mode); +extern void imx6_set_cache_lpm_in_wait(bool enable); +extern void imx6sl_set_wait_clk(bool enter); +extern void imx6_enet_mac_init(const char *compatible); extern void imx_cpu_die(unsigned int cpu); extern int imx_cpu_kill(unsigned int cpu); #ifdef CONFIG_PM -extern void imx6q_pm_init(void); +extern void imx6_pm_init(void); +extern void imx6_pm_set_ccm_base(void __iomem *base); extern void imx51_pm_init(void); extern void imx53_pm_init(void); #else -static inline void imx6q_pm_init(void) {} +static inline void imx6_pm_init(void) {} +static inline void imx6_pm_set_ccm_base(void __iomem *base) {} static inline void imx51_pm_init(void) {} static inline void imx53_pm_init(void) {} #endif @@ -159,6 +175,12 @@ static inline int mx51_neon_fixup(void) { return 0; } #endif +#ifdef CONFIG_CACHE_L2X0 +extern void imx_init_l2cache(void); +#else +static inline void imx_init_l2cache(void) {} +#endif + extern struct smp_operations imx_smp_ops; #endif diff -Nur linux-3.10.30/arch/arm/mach-imx/cpu.c linux-3.10.30-cubox-i/arch/arm/mach-imx/cpu.c --- linux-3.10.30/arch/arm/mach-imx/cpu.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/cpu.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,6 +1,9 @@ #include #include +#include +#include +#include #include "hardware.h" #include "common.h" @@ -8,11 +11,30 @@ unsigned int __mxc_cpu_type; EXPORT_SYMBOL(__mxc_cpu_type); +extern unsigned int system_rev; + +static unsigned int imx_soc_revision; + void mxc_set_cpu_type(unsigned int type) { __mxc_cpu_type = type; } +void mxc_set_system_rev(unsigned int sysrev) +{ + system_rev = sysrev; +} + +void imx_set_soc_revision(unsigned int rev) +{ + imx_soc_revision = rev; +} + +unsigned int imx_get_soc_revision(void) +{ + return imx_soc_revision; +} + void imx_print_silicon_rev(const char *cpu, int srev) { if (srev == IMX_CHIP_REVISION_UNKNOWN) @@ -44,3 +66,80 @@ reg = __raw_readl(base + 0x50) & 0x00FFFFFF; __raw_writel(reg, base + 0x50); } + +struct device * __init imx_soc_device_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device_node *root; + const char *soc_id; + int ret; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return NULL; + + soc_dev_attr->family = "Freescale i.MX"; + + root = of_find_node_by_path("/"); + ret = of_property_read_string(root, "model", &soc_dev_attr->machine); + if (ret) + goto free_soc; + + switch (__mxc_cpu_type) { + case MXC_CPU_MX1: + soc_id = "i.MX1"; + break; + case MXC_CPU_MX21: + soc_id = "i.MX21"; + break; + case MXC_CPU_MX25: + soc_id = "i.MX25"; + break; + case MXC_CPU_MX27: + soc_id = "i.MX27"; + break; + case MXC_CPU_MX31: + soc_id = "i.MX31"; + break; + case MXC_CPU_MX35: + soc_id = "i.MX35"; + break; + case MXC_CPU_MX51: + soc_id = "i.MX51"; + break; + case MXC_CPU_MX53: + soc_id = "i.MX53"; + break; + case MXC_CPU_IMX6SL: + soc_id = "i.MX6SL"; + break; + case MXC_CPU_IMX6DL: + soc_id = "i.MX6DL"; + break; + case MXC_CPU_IMX6Q: + soc_id = "i.MX6Q"; + break; + default: + soc_id = "Unknown"; + } + soc_dev_attr->soc_id = soc_id; + + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d", + (imx_soc_revision >> 4) & 0xf, + imx_soc_revision & 0xf); + if (!soc_dev_attr->revision) + goto free_soc; + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) + goto free_rev; + + return soc_device_to_device(soc_dev); + +free_rev: + kfree(soc_dev_attr->revision); +free_soc: + kfree(soc_dev_attr); + return NULL; +} diff -Nur linux-3.10.30/arch/arm/mach-imx/cpuidle-imx6q.c linux-3.10.30-cubox-i/arch/arm/mach-imx/cpuidle-imx6q.c --- linux-3.10.30/arch/arm/mach-imx/cpuidle-imx6q.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/cpuidle-imx6q.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012-2013 Freescale Semiconductor, 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 @@ -27,9 +27,9 @@ */ if (!spin_trylock(&master_lock)) goto idle; - imx6q_set_lpm(WAIT_UNCLOCKED); + imx6_set_lpm(WAIT_UNCLOCKED); cpu_do_idle(); - imx6q_set_lpm(WAIT_CLOCKED); + imx6_set_lpm(WAIT_CLOCKED); spin_unlock(&master_lock); goto done; } @@ -68,8 +68,8 @@ /* Need to enable SCU standby for entering WAIT modes */ imx_scu_standby_enable(); - /* Set chicken bit to get a reliable WAIT mode support */ - imx6q_set_chicken_bit(); + /* Set cache lpm bit for reliable WAIT mode support */ + imx6_set_cache_lpm_in_wait(true); return cpuidle_register(&imx6q_cpuidle_driver, NULL); } diff -Nur linux-3.10.30/arch/arm/mach-imx/cpuidle-imx6sl.c linux-3.10.30-cubox-i/arch/arm/mach-imx/cpuidle-imx6sl.c --- linux-3.10.30/arch/arm/mach-imx/cpuidle-imx6sl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/cpuidle-imx6sl.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2012-2013 Freescale Semiconductor, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "cpuidle.h" + +extern u32 audio_bus_freq_mode; +extern u32 ultra_low_bus_freq_mode; +extern unsigned long reg_addrs[]; +extern void imx6sl_low_power_wfi(void); + +static void __iomem *iomux_base; +static void *wfi_iram_base; + +void (*imx6sl_wfi_in_iram_fn)(void *wfi_iram_base, + void *iomux_addr, void *regs_addr, u32 audio_mode) = NULL; + +#define WFI_IN_IRAM_SIZE 0x1000 + +static int imx6sl_enter_wait(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + imx6_set_lpm(WAIT_UNCLOCKED); + if (ultra_low_bus_freq_mode || audio_bus_freq_mode) { + /* + * Flush the TLB, to ensure no TLB maintenance occurs + * when DDR is in self-refresh. + */ + local_flush_tlb_all(); + /* + * Run WFI code from IRAM. + * Drop the DDR freq to 1MHz and AHB to 3MHz + * Also float DDR IO pads. + */ + imx6sl_wfi_in_iram_fn(wfi_iram_base, iomux_base, reg_addrs, audio_bus_freq_mode); + } else { + imx6sl_set_wait_clk(true); + cpu_do_idle(); + imx6sl_set_wait_clk(false); + } + imx6_set_lpm(WAIT_CLOCKED); + + return index; +} + +static struct cpuidle_driver imx6sl_cpuidle_driver = { + .name = "imx6sl_cpuidle", + .owner = THIS_MODULE, + .states = { + /* WFI */ + ARM_CPUIDLE_WFI_STATE, + /* WAIT */ + { + .exit_latency = 50, + .target_residency = 75, + .flags = CPUIDLE_FLAG_TIME_VALID | + CPUIDLE_FLAG_TIMER_STOP, + .enter = imx6sl_enter_wait, + .name = "WAIT", + .desc = "Clock off", + }, + }, + .state_count = 2, + .safe_state_index = 0, +}; + +int __init imx6sl_cpuidle_init(void) +{ + struct platform_device *ocram_dev; + unsigned int iram_paddr; + struct device_node *node; + struct gen_pool *iram_pool; + + node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-iomuxc"); + if (!node) { + pr_err("failed to find imx6sl-iomuxc device tree data!\n"); + return -EINVAL; + } + iomux_base = of_iomap(node, 0); + WARN(!iomux_base, "unable to map iomux registers\n"); + + node = NULL; + node = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!node) { + pr_err("%s: failed to find ocram node\n", + __func__); + return -EINVAL; + } + + ocram_dev = of_find_device_by_node(node); + if (!ocram_dev) { + pr_err("failed to find ocram device!\n"); + return -EINVAL; + } + + iram_pool = dev_get_gen_pool(&ocram_dev->dev); + if (!iram_pool) { + pr_err("iram pool unavailable!\n"); + return -EINVAL; + } + /* + * Allocate IRAM memory when ARM executes WFI in + * ultra_low_power_mode. + */ + wfi_iram_base = (void *)gen_pool_alloc(iram_pool, + WFI_IN_IRAM_SIZE); + if (!wfi_iram_base) { + pr_err("Cannot alloc iram for wfi code!\n"); + return -ENOMEM; + } + + iram_paddr = gen_pool_virt_to_phys(iram_pool, + (unsigned long)wfi_iram_base); + /* + * Need to remap the area here since we want + * the memory region to be executable. + */ + wfi_iram_base = __arm_ioremap(iram_paddr, + WFI_IN_IRAM_SIZE, + MT_MEMORY_NONCACHED); + if (!wfi_iram_base) + pr_err("wfi_ram_base NOT remapped\n"); + + imx6sl_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base, + &imx6sl_low_power_wfi, WFI_IN_IRAM_SIZE); + + return cpuidle_register(&imx6sl_cpuidle_driver, NULL); +} diff -Nur linux-3.10.30/arch/arm/mach-imx/cpuidle.h linux-3.10.30-cubox-i/arch/arm/mach-imx/cpuidle.h --- linux-3.10.30/arch/arm/mach-imx/cpuidle.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/cpuidle.h 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012-2013 Freescale Semiconductor, Inc. * Copyright 2012 Linaro Ltd. * * The code contained herein is licensed under the GNU General Public @@ -13,6 +13,7 @@ #ifdef CONFIG_CPU_IDLE extern int imx5_cpuidle_init(void); extern int imx6q_cpuidle_init(void); +extern int imx6sl_cpuidle_init(void); #else static inline int imx5_cpuidle_init(void) { @@ -22,4 +23,8 @@ { return 0; } +static inline int imx6sl_cpuidle_init(void) +{ + return 0; +} #endif diff -Nur linux-3.10.30/arch/arm/mach-imx/ddr3_freq_imx6.S linux-3.10.30-cubox-i/arch/arm/mach-imx/ddr3_freq_imx6.S --- linux-3.10.30/arch/arm/mach-imx/ddr3_freq_imx6.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/ddr3_freq_imx6.S 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,893 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#define MMDC0_MDPDC 0x4 +#define MMDC0_MDCF0 0x0c +#define MMDC0_MDCF1 0x10 +#define MMDC0_MDMISC 0x18 +#define MMDC0_MDSCR 0x1c +#define MMDC0_MAPSR 0x404 +#define MMDC0_MADPCR0 0x410 +#define MMDC0_MPZQHWCTRL 0x800 +#define MMDC1_MPZQHWCTRL 0x4800 +#define MMDC0_MPODTCTRL 0x818 +#define MMDC1_MPODTCTRL 0x4818 +#define MMDC0_MPDGCTRL0 0x83c +#define MMDC1_MPDGCTRL0 0x483c +#define MMDC0_MPMUR0 0x8b8 +#define MMDC1_MPMUR0 0x48b8 + +#define CCM_CBCDR 0x14 +#define CCM_CBCMR 0x18 +#define CCM_CSCMR1 0x1c +#define CCM_CDHIPR 0x48 + +#define L2_CACHE_SYNC 0x730 + + .align 3 + + .macro switch_to_528MHz + + /* check if periph_clk_sel is already set */ + ldr r0, [r6, #CCM_CBCDR] + and r0, r0, #(1 << 25) + cmp r0, #(1 << 25) + beq set_ahb_podf_before_switch + + /* change periph_clk to be sourced from pll3_clk. */ + ldr r0, [r6, #CCM_CBCMR] + bic r0, r0, #(3 << 12) + str r0, [r6, #CCM_CBCMR] + + ldr r0, [r6, #CCM_CBCDR] + bic r0, r0, #(0x38 << 20) + str r0, [r6, #CCM_CBCDR] + + /* + * set the AHB dividers before the switch, + * don't change AXI clock divider, + * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4, + */ + ldr r0, [r6, #CCM_CBCDR] + ldr r2, =0x3f1f00 + bic r0, r0, r2 + orr r0, r0, #0xd00 + orr r0, r0, #(1 << 16) + str r0, [r6, #CCM_CBCDR] + +wait_div_update528: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne wait_div_update528 + + /* now switch periph_clk to pll3_main_clk. */ + ldr r0, [r6, #CCM_CBCDR] + orr r0, r0, #(1 << 25) + str r0, [r6, #CCM_CBCDR] + +periph_clk_switch3: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne periph_clk_switch3 + + b switch_pre_periph_clk_528 + +set_ahb_podf_before_switch: + /* + * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4, + */ + ldr r0, [r6, #CCM_CBCDR] + ldr r2, =0x3f1f00 + bic r0, r0, r2 + orr r0, r0, #0xd00 + orr r0, r0, #(1 << 16) + str r0, [r6, #CCM_CBCDR] + +wait_div_update528_1: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne wait_div_update528_1 + +switch_pre_periph_clk_528: + + /* now switch pre_periph_clk to PLL2_528MHz. */ + ldr r0, [r6, #CCM_CBCMR] + bic r0, r0, #(0xc << 16) + str r0, [r6, #CCM_CBCMR] + + /* now switch periph_clk back. */ + ldr r0, [r6, #CCM_CBCDR] + bic r0, r0, #(1 << 25) + str r0, [r6, #CCM_CBCDR] + +periph_clk_switch4: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne periph_clk_switch4 + + .endm + + .macro switch_to_400MHz + + /* check if periph_clk_sel is already set. */ + ldr r0, [r6, #CCM_CBCDR] + and r0, r0, #(1 << 25) + cmp r0, #(1 << 25) + beq set_ahb_podf_before_switch1 + + /* change periph_clk to be sourced from pll3_clk. */ + ldr r0, [r6, #CCM_CBCMR] + bic r0, r0, #(3 << 12) + str r0, [r6, #CCM_CBCMR] + + ldr r0, [r6, #CCM_CBCDR] + bic r0, r0, #(0x38 << 24) + str r0, [r6, #CCM_CBCDR] + + /* now switch periph_clk to pll3_main_clk. */ + ldr r0, [r6, #CCM_CBCDR] + orr r0, r0, #(1 << 25) + str r0, [r6, #CCM_CBCDR] + +periph_clk_switch5: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne periph_clk_switch5 + + b switch_pre_periph_clk_400 + +set_ahb_podf_before_switch1: + /* + * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4, + */ + ldr r0, [r6, #CCM_CBCDR] + ldr r2, =0x3f1f00 + bic r0, r0, r2 + orr r0, r0, #(0x9 << 8) + orr r0, r0, #(1 << 16) + str r0, [r6, #CCM_CBCDR] + +wait_div_update400_1: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne wait_div_update400_1 + +switch_pre_periph_clk_400: + + /* now switch pre_periph_clk to PFD_400MHz. */ + ldr r0, [r6, #CCM_CBCMR] + bic r0, r0, #(0xc << 16) + orr r0, r0, #(0x4 << 16) + str r0, [r6, #CCM_CBCMR] + + /* now switch periph_clk back. */ + ldr r0, [r6, #CCM_CBCDR] + bic r0, r0, #(1 << 25) + str r0, [r6, #CCM_CBCDR] + +periph_clk_switch6: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne periph_clk_switch6 + + /* + * change AHB divider so that we are at 400/3=133MHz. + * don't change AXI clock divider. + * set the MMDC_DIV=1, AXI_DIV=2, AHB_DIV=3, + */ + ldr r0, [r6, #CCM_CBCDR] + ldr r2, =0x3f1f00 + bic r0, r0, r2 + orr r0, r0, #(0x9 << 8) + orr r0, r0, #(1 << 16) + str r0, [r6, #CCM_CBCDR] + +wait_div_update400_2: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne wait_div_update400_2 + + .endm + + .macro switch_to_50MHz + + /* check if periph_clk_sel is already set. */ + ldr r0, [r6, #CCM_CBCDR] + and r0, r0, #(1 << 25) + cmp r0, #(1 << 25) + beq switch_pre_periph_clk_50 + + /* + * set the periph_clk to be sourced from PLL2_PFD_200M + * change periph_clk to be sourced from pll3_clk. + * ensure PLL3 is the source and set the divider to 1. + */ + ldr r0, [r6, #CCM_CBCMR] + bic r0, r0, #(0x3 << 12) + str r0, [r6, #CCM_CBCMR] + + ldr r0, [r6, #CCM_CBCDR] + bic r0, r0, #(0x38 << 24) + str r0, [r6, #CCM_CBCDR] + + /* now switch periph_clk to pll3_main_clk. */ + ldr r0, [r6, #CCM_CBCDR] + orr r0, r0, #(1 << 25) + str r0, [r6, #CCM_CBCDR] + +periph_clk_switch_50: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne periph_clk_switch_50 + +switch_pre_periph_clk_50: + + /* now switch pre_periph_clk to PFD_200MHz. */ + ldr r0, [r6, #CCM_CBCMR] + orr r0, r0, #(0xc << 16) + str r0, [r6, #CCM_CBCMR] + + /* + * set the MMDC_DIV=4, AXI_DIV = 4, AHB_DIV=8, + */ + ldr r0, [r6, #CCM_CBCDR] + ldr r2, =0x3f1f00 + bic r0, r0, r2 + orr r0, r0, #(0x18 << 16) + orr r0, r0, #(0x3 << 16) + + /* + * if changing AHB divider remember to change + * the IPGPER divider too below. + */ + orr r0, r0, #0x1d00 + str r0, [r6, #CCM_CBCDR] + +wait_div_update_50: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne wait_div_update_50 + + /* now switch periph_clk back. */ + ldr r0, [r6, #CCM_CBCDR] + bic r0, r0, #(1 << 25) + str r0, [r6, #CCM_CBCDR] + +periph_clk_switch2: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne periph_clk_switch2 + + .endm + + .macro switch_to_24MHz + /* + * change the freq now try setting DDR to 24MHz. + * source it from the periph_clk2 ensure the + * periph_clk2 is sourced from 24MHz and the + * divider is 1. + */ + + ldr r0, [r6, #CCM_CBCMR] + bic r0, r0, #(0x3 << 12) + orr r0, r0, #(1 << 12) + str r0, [r6, #CCM_CBCMR] + + ldr r0, [r6, #CCM_CBCDR] + bic r0, r0, #(0x38 << 24) + str r0, [r6, #CCM_CBCDR] + + /* now switch periph_clk to 24MHz. */ + ldr r0, [r6, #CCM_CBCDR] + orr r0, r0, #(1 << 25) + str r0, [r6, #CCM_CBCDR] + +periph_clk_switch1: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne periph_clk_switch1 + + /* change all the dividers to 1. */ + ldr r0, [r6, #CCM_CBCDR] + ldr r2, =0x3f1f00 + bic r0, r0, r2 + orr r0, r0, #(1 << 8) + str r0, [r6, #CCM_CBCDR] + + /* Wait for the divider to change. */ +wait_div_update: + ldr r0, [r6, #CCM_CDHIPR] + cmp r0, #0 + bne wait_div_update + + .endm + +/* + * mx6_ddr3_freq_change + * + * idle the processor (eg, wait for interrupt). + * make sure DDR is in self-refresh. + * IRQs are already disabled. + */ +ENTRY(mx6_ddr3_freq_change) + + stmfd sp!, {r4-r12} + + /* + * r5 -> mmdc_base + * r6 -> ccm_base + * r7 -> iomux_base + * r12 -> l2_base + */ + mov r4, r0 + mov r8, r1 + mov r9, r2 + mov r11, r3 + + /* + * Get the addresses of the registers. + * They are last few entries in the + * ddr_settings parameter. + * The first entry contains the count, + * and each entry is 2 words. + */ + ldr r0, [r1] + add r0, r0, #1 + lsl r0, r0, #3 + add r1, r0, r1 + /* mmdc_base. */ + ldr r5, [r1] + add r1, #8 + /* ccm_base */ + ldr r6, [r1] + add r1, #8 + /*iomux_base */ + ldr r7, [r1] + add r1, #8 + /*l2_base */ + ldr r12, [r1] + +ddr_freq_change: + /* + * make sure no TLB miss will occur when + * the DDR is in self refresh. invalidate + * TLB single entry to ensure that the + * address is not already in the TLB. + */ + + adr r10, ddr_freq_change + + ldr r2, [r6] + ldr r2, [r5] + ldr r2, [r7] + ldr r2, [r8] + ldr r2, [r10] + ldr r2, [r11] + ldr r2, [r12] + +#ifdef CONFIG_CACHE_L2X0 + /* + * Make sure the L2 buffers are drained. + * Sync operation on L2 drains the buffers. + */ + mov r1, #0x0 + str r1, [r12, #L2_CACHE_SYNC] +#endif + + /* disable automatic power saving. */ + ldr r0, [r5, #MMDC0_MAPSR] + orr r0, r0, #0x01 + str r0, [r5, #MMDC0_MAPSR] + + /* disable MMDC power down timer. */ + ldr r0, [r5, #MMDC0_MDPDC] + bic r0, r0, #(0xff << 8) + str r0, [r5, #MMDC0_MDPDC] + + /* delay for a while */ + ldr r1, =4 +delay1: + ldr r2, =0 +cont1: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont1 + sub r1, r1, #1 + cmp r1, #0 + bgt delay1 + + /* set CON_REG */ + ldr r0, =0x8000 + str r0, [r5, #MMDC0_MDSCR] +poll_conreq_set_1: + ldr r0, [r5, #MMDC0_MDSCR] + and r0, r0, #(0x4 << 12) + cmp r0, #(0x4 << 12) + bne poll_conreq_set_1 + + ldr r0, =0x00008010 + str r0, [r5, #MMDC0_MDSCR] + ldr r0, =0x00008018 + str r0, [r5, #MMDC0_MDSCR] + + /* + * if requested frequency is greater than + * 300MHz go to DLL on mode. + */ + ldr r1, =300000000 + cmp r4, r1 + bge dll_on_mode + +dll_off_mode: + + /* if DLL is currently on, turn it off. */ + cmp r9, #1 + beq continue_dll_off_1 + + ldr r0, =0x00018031 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x00018039 + str r0, [r5, #MMDC0_MDSCR] + + ldr r1, =10 +delay1a: + ldr r2, =0 +cont1a: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont1a + sub r1, r1, #1 + cmp r1, #0 + bgt delay1a + +continue_dll_off_1: + /* set DVFS - enter self refresh mode */ + ldr r0, [r5, #MMDC0_MAPSR] + orr r0, r0, #(1 << 21) + str r0, [r5, #MMDC0_MAPSR] + + /* de-assert con_req */ + mov r0, #0x0 + str r0, [r5, #MMDC0_MDSCR] + +poll_dvfs_set_1: + ldr r0, [r5, #MMDC0_MAPSR] + and r0, r0, #(1 << 25) + cmp r0, #(1 << 25) + bne poll_dvfs_set_1 + + ldr r1, =24000000 + cmp r4, r1 + beq switch_freq_24 + + switch_to_50MHz + b continue_dll_off_2 + +switch_freq_24: + switch_to_24MHz + +continue_dll_off_2: + + /* set SBS - block ddr accesses */ + ldr r0, [r5, #MMDC0_MADPCR0] + orr r0, r0, #(1 << 8) + str r0, [r5, #MMDC0_MADPCR0] + + /* clear DVFS - exit from self refresh mode */ + ldr r0, [r5, #MMDC0_MAPSR] + bic r0, r0, #(1 << 21) + str r0, [r5, #MMDC0_MAPSR] + +poll_dvfs_clear_1: + ldr r0, [r5, #MMDC0_MAPSR] + and r0, r0, #(1 << 25) + cmp r0, #(1 << 25) + beq poll_dvfs_clear_1 + + /* if DLL was previously on, continue DLL off routine. */ + cmp r9, #1 + beq continue_dll_off_3 + + ldr r0, =0x00018031 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x00018039 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x08208030 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x08208038 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x00088032 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x0008803A + str r0, [r5, #MMDC0_MDSCR] + + /* delay for a while. */ + ldr r1, =4 +delay_1: + ldr r2, =0 +cont_1: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont_1 + sub r1, r1, #1 + cmp r1, #0 + bgt delay_1 + + ldr r0, [r5, #MMDC0_MDCF0] + bic r0, r0, #0xf + orr r0, r0, #0x3 + str r0, [r5, #MMDC0_MDCF0] + + ldr r0, [r5, #MMDC0_MDCF1] + bic r0, r0, #0x7 + orr r0, r0, #0x4 + str r0, [r5, #MMDC0_MDCF1] + + ldr r0, =0x00091680 + str r0, [r5, #MMDC0_MDMISC] + + /* enable dqs pull down in the IOMUX. */ + ldr r1, [r11] + add r11, r11, #8 + ldr r2, =0x3028 +update_iomux: + ldr r0, [r11, #0x0] + ldr r3, [r7, r0] + bic r3, r3, r2 + orr r3, r3, #(0x3 << 12) + orr r3, r3, #0x28 + str r3, [r7, r0] + add r11, r11, #8 + sub r1, r1, #1 + cmp r1, #0 + bgt update_iomux + + /* ODT disabled. */ + ldr r0, =0x0 + ldr r2, =MMDC0_MPODTCTRL + str r0, [r5, r2] + ldr r2, =MMDC1_MPODTCTRL + str r0, [r5, r2] + + /* DQS gating disabled. */ + ldr r2, =MMDC0_MPDGCTRL0 + ldr r0, [r5, r2] + orr r0, r0, #(1 << 29) + str r0, [r5, r2] + + ldr r2, =MMDC1_MPDGCTRL0 + ldr r0, [r5, r2] + orr r0, r0, #(0x1 << 29) + str r0, [r5, r2] + + /* MMDC0_MAPSR adopt power down enable. */ + ldr r0, [r5, #MMDC0_MAPSR] + bic r0, r0, #0x01 + str r0, [r5, #MMDC0_MAPSR] + + /* frc_msr + mu bypass */ + ldr r0, =0x00000060 + str r0, [r5, #MMDC0_MPMUR0] + ldr r2, =MMDC1_MPMUR0 + str r0, [r5, r2] + ldr r0, =0x00000460 + str r0, [r5, #MMDC0_MPMUR0] + ldr r2, =MMDC1_MPMUR0 + str r0, [r5, r2] + ldr r0, =0x00000c60 + str r0, [r5, #MMDC0_MPMUR0] + ldr r2, =MMDC1_MPMUR0 + str r0, [r5, r2] + +continue_dll_off_3: + /* clear SBS - unblock accesses to DDR. */ + ldr r0, [r5, #MMDC0_MADPCR0] + bic r0, r0, #(0x1 << 8) + str r0, [r5, #MMDC0_MADPCR0] + + mov r0, #0x0 + str r0, [r5, #MMDC0_MDSCR] +poll_conreq_clear_1: + ldr r0, [r5, #MMDC0_MDSCR] + and r0, r0, #(0x4 << 12) + cmp r0, #(0x4 << 12) + beq poll_conreq_clear_1 + + b done + +dll_on_mode: + /* assert DVFS - enter self refresh mode. */ + ldr r0, [r5, #MMDC0_MAPSR] + orr r0, r0, #(1 << 21) + str r0, [r5, #MMDC0_MAPSR] + + /* de-assert CON_REQ. */ + mov r0, #0x0 + str r0, [r5, #MMDC0_MDSCR] + + /* poll DVFS ack. */ +poll_dvfs_set_2: + ldr r0, [r5, #MMDC0_MAPSR] + and r0, r0, #(1 << 25) + cmp r0, #(1 << 25) + bne poll_dvfs_set_2 + + ldr r1, =528000000 + cmp r4, r1 + beq switch_freq_528 + + switch_to_400MHz + + b continue_dll_on + +switch_freq_528: + switch_to_528MHz + +continue_dll_on: + + /* set SBS step-by-step mode. */ + ldr r0, [r5, #MMDC0_MADPCR0] + orr r0, r0, #( 1 << 8) + str r0, [r5, #MMDC0_MADPCR0] + + /* clear DVFS - exit self refresh mode. */ + ldr r0, [r5, #MMDC0_MAPSR] + bic r0, r0, #(1 << 21) + str r0, [r5, #MMDC0_MAPSR] + +poll_dvfs_clear_2: + ldr r0, [r5, #MMDC0_MAPSR] + and r0, r0, #(1 << 25) + cmp r0, #(1 << 25) + beq poll_dvfs_clear_2 + + /* if DLL is currently off, turn it back on. */ + cmp r9, #0 + beq update_calibration_only + + ldr r0, =0xa5390003 + str r0, [r5, #MMDC0_MPZQHWCTRL] + ldr r2, =MMDC1_MPZQHWCTRL + str r0, [r5, r2] + + /* enable DQS gating. */ + ldr r2, =MMDC0_MPDGCTRL0 + ldr r0, [r5, r2] + bic r0, r0, #(1 << 29) + str r0, [r5, r2] + + ldr r2, =MMDC1_MPDGCTRL0 + ldr r0, [r5, r2] + bic r0, r0, #(1 << 29) + str r0, [r5, r2] + + /* force measure. */ + ldr r0, =0x00000800 + str r0, [r5, #MMDC0_MPMUR0] + ldr r2, =MMDC1_MPMUR0 + str r0, [r5, r2] + + /* delay for while. */ + ldr r1, =4 +delay5: + ldr r2, =0 +cont5: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont5 + sub r1, r1, #1 + cmp r1, #0 + bgt delay5 + + /* disable dqs pull down in the IOMUX. */ + ldr r1, [r11] + add r11, r11, #8 +update_iomux1: + ldr r0, [r11, #0x0] + ldr r3, [r11, #0x4] + str r3, [r7, r0] + add r11, r11, #8 + sub r1, r1, #1 + cmp r1, #0 + bgt update_iomux1 + + /* config MMDC timings to 528MHz. */ + ldr r9, [r8] + add r8, r8, #8 + ldr r0, [r8, #0x0] + ldr r3, [r8, #0x4] + str r3, [r5, r0] + add r8, r8, #8 + + ldr r0, [r8, #0x0] + ldr r3, [r8, #0x4] + str r3, [r5, r0] + add r8, r8, #8 + + /* update MISC register: WALAT, RALAT */ + ldr r0, =0x00081740 + str r0, [r5, #MMDC0_MDMISC] + + /* configure ddr devices to dll on, odt. */ + ldr r0, =0x00028031 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x00028039 + str r0, [r5, #MMDC0_MDSCR] + + /* delay for while. */ + ldr r1, =4 +delay7: + ldr r2, =0 +cont7: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont7 + sub r1, r1, #1 + cmp r1, #0 + bgt delay7 + + /* reset dll. */ + ldr r0, =0x09208030 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x09208038 + str r0, [r5, #MMDC0_MDSCR] + + /* delay for while. */ + ldr r1, =100 +delay8: + ldr r2, =0 +cont8: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont8 + sub r1, r1, #1 + cmp r1, #0 + bgt delay8 + + ldr r0, [r8, #0x0] + ldr r3, [r8, #0x4] + str r3, [r5, r0] + add r8, r8, #8 + + ldr r0, [r8, #0x0] + ldr r3, [r8, #0x4] + str r3, [r5, r0] + add r8, r8, #8 + + ldr r0, =0x00428031 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x00428039 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, [r8, #0x0] + ldr r3, [r8, #0x4] + str r3, [r5, r0] + add r8, r8, #8 + + ldr r0, [r8, #0x0] + ldr r3, [r8, #0x4] + str r3, [r5, r0] + add r8, r8, #8 + + /* issue a zq command. */ + ldr r0, =0x04008040 + str r0, [r5, #MMDC0_MDSCR] + + ldr r0, =0x04008048 + str r0, [r5, #MMDC0_MDSCR] + + /* MMDC ODT enable. */ + ldr r0, [r8, #0x0] + ldr r3, [r8, #0x4] + str r3, [r5, r0] + add r8, r8, #8 + + ldr r2, =0x4818 + str r0, [r5, r2] + + /* delay for while. */ + ldr r1, =40 +delay15: + ldr r2, =0 +cont15: + ldr r0, [r5, r2] + add r2, r2, #4 + cmp r2, #16 + bne cont15 + sub r1, r1, #1 + cmp r1, #0 + bgt delay15 + + /* MMDC0_MAPSR adopt power down enable. */ + ldr r0, [r5, #MMDC0_MAPSR] + bic r0, r0, #0x01 + str r0, [r5, #MMDC0_MAPSR] + + /* enable MMDC power down timer. */ + ldr r0, [r5, #MMDC0_MDPDC] + orr r0, r0, #(0x55 << 8) + str r0, [r5, #MMDC0_MDPDC] + + b update_calibration + +update_calibration_only: + ldr r1, [r8] + sub r1, r1, #7 + add r8, r8, #64 + b update_calib + +update_calibration: + /* write the new calibration values. */ + mov r1, r9 + sub r1, r1, #7 + +update_calib: + ldr r0, [r8, #0x0] + ldr r3, [r8, #0x4] + str r3, [r5, r0] + add r8, r8, #8 + sub r1, r1, #1 + cmp r1, #0 + bgt update_calib + + /* perform a force measurement. */ + ldr r0, =0x800 + str r0, [r5, #MMDC0_MPMUR0] + ldr r2, =MMDC1_MPMUR0 + str r0, [r5, r2] + + /* clear SBS - unblock DDR accesses. */ + ldr r0, [r5, #MMDC0_MADPCR0] + bic r0, r0, #(1 << 8) + str r0, [r5, #MMDC0_MADPCR0] + + mov r0, #0x0 + str r0, [r5, #MMDC0_MDSCR] +poll_conreq_clear_2: + ldr r0, [r5, #MMDC0_MDSCR] + and r0, r0, #(0x4 << 12) + cmp r0, #(0x4 << 12) + beq poll_conreq_clear_2 + +done: + /* restore registers */ + + ldmfd sp!, {r4-r12} + mov pc, lr + + .type mx6_do_ddr3_freq_change, #object +ENTRY(mx6_do_ddr_freq_change) + .word mx6_ddr3_freq_change + .size mx6_ddr3_freq_change, . - mx6_ddr3_freq_change diff -Nur linux-3.10.30/arch/arm/mach-imx/gpc.c linux-3.10.30-cubox-i/arch/arm/mach-imx/gpc.c --- linux-3.10.30/arch/arm/mach-imx/gpc.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/gpc.c 2014-03-08 20:32:54.000000000 +0100 @@ -10,30 +10,127 @@ * http://www.gnu.org/copyleft/gpl.html */ +#include +#include #include #include +#include #include #include #include +#include #include +#include +#include +#include #include "common.h" +#include "hardware.h" #define GPC_IMR1 0x008 #define GPC_PGC_CPU_PDN 0x2a0 +#define GPC_PGC_GPU_PDN 0x260 +#define GPC_PGC_GPU_PUPSCR 0x264 +#define GPC_PGC_GPU_PDNSCR 0x268 +#define GPC_PGC_DISP_PGCR_OFFSET 0x240 +#define GPC_PGC_DISP_PUPSCR_OFFSET 0x244 +#define GPC_PGC_DISP_PDNSCR_OFFSET 0x248 +#define GPC_PGC_DISP_SR_OFFSET 0x24c +#define GPC_PGC_GPU_SW_SHIFT 0 +#define GPC_PGC_GPU_SW_MASK 0x3f +#define GPC_PGC_GPU_SW2ISO_SHIFT 8 +#define GPC_PGC_GPU_SW2ISO_MASK 0x3f +#define GPC_PGC_CPU_PUPSCR 0x2a4 +#define GPC_PGC_CPU_PDNSCR 0x2a8 +#define GPC_PGC_CPU_SW_SHIFT 0 +#define GPC_PGC_CPU_SW_MASK 0x3f +#define GPC_PGC_CPU_SW2ISO_SHIFT 8 +#define GPC_PGC_CPU_SW2ISO_MASK 0x3f +#define GPC_CNTR 0x0 +#define GPC_CNTR_PU_UP_REQ_SHIFT 0x1 +#define GPC_CNTR_PU_DOWN_REQ_SHIFT 0x0 #define IMR_NUM 4 static void __iomem *gpc_base; static u32 gpc_wake_irqs[IMR_NUM]; static u32 gpc_saved_imrs[IMR_NUM]; +static struct clk *gpu3d_clk, *gpu3d_shader_clk, *gpu2d_clk, *gpu2d_axi_clk; +static struct clk *lcd_axi_clk, *lcd_pix_clk, *epdc_axi_clk, *epdc_pix_clk; +static struct clk *pxp_axi_clk; +static struct clk *openvg_axi_clk, *vpu_clk, *ipg_clk; +static struct device *gpc_dev; +struct regulator *pu_reg; +struct notifier_block nb; +static struct regulator_dev *pu_dummy_regulator_rdev; +static struct regulator_init_data pu_dummy_initdata = { + .constraints = { + .max_uV = 1450000, /* allign with real max of anatop */ + .valid_ops_mask = REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_VOLTAGE, + }, +}; +static int pu_dummy_enable; -void imx_gpc_pre_suspend(void) +static void imx_disp_clk(bool enable) +{ + if (enable) { + clk_prepare_enable(lcd_axi_clk); + clk_prepare_enable(lcd_pix_clk); + clk_prepare_enable(epdc_axi_clk); + clk_prepare_enable(epdc_pix_clk); + clk_prepare_enable(pxp_axi_clk); + } else { + clk_disable_unprepare(lcd_axi_clk); + clk_disable_unprepare(lcd_pix_clk); + clk_disable_unprepare(epdc_axi_clk); + clk_disable_unprepare(epdc_pix_clk); + clk_disable_unprepare(pxp_axi_clk); + } +} + +static void imx_gpc_dispmix_on(void) +{ + if (cpu_is_imx6sl()) { + imx_disp_clk(true); + + writel_relaxed(0x0, gpc_base + GPC_PGC_DISP_PGCR_OFFSET); + writel_relaxed(0x20, gpc_base + GPC_CNTR); + while (readl_relaxed(gpc_base + GPC_CNTR) & 0x20) + ; + writel_relaxed(0x1, gpc_base + GPC_PGC_DISP_SR_OFFSET); + + imx_disp_clk(false); + } +} + +static void imx_gpc_dispmix_off(void) +{ + if (cpu_is_imx6sl()) { + imx_disp_clk(true); + + writel_relaxed(0xFFFFFFFF, + gpc_base + GPC_PGC_DISP_PUPSCR_OFFSET); + writel_relaxed(0xFFFFFFFF, + gpc_base + GPC_PGC_DISP_PDNSCR_OFFSET); + writel_relaxed(0x1, gpc_base + GPC_PGC_DISP_PGCR_OFFSET); + writel_relaxed(0x10, gpc_base + GPC_CNTR); + while (readl_relaxed(gpc_base + GPC_CNTR) & 0x10) + ; + + imx_disp_clk(false); + } +} + +void imx_gpc_pre_suspend(bool arm_power_off) { void __iomem *reg_imr1 = gpc_base + GPC_IMR1; int i; - /* Tell GPC to power off ARM core when suspend */ - writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_PDN); + imx_gpc_dispmix_off(); + + if (arm_power_off) + /* Tell GPC to power off ARM core when suspend */ + writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_PDN); for (i = 0; i < IMR_NUM; i++) { gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); @@ -51,6 +148,8 @@ for (i = 0; i < IMR_NUM; i++) writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); + + imx_gpc_dispmix_on(); } static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on) @@ -90,7 +189,7 @@ writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); } -static void imx_gpc_irq_unmask(struct irq_data *d) +void imx_gpc_irq_unmask(struct irq_data *d) { void __iomem *reg; u32 val; @@ -105,7 +204,7 @@ writel_relaxed(val, reg); } -static void imx_gpc_irq_mask(struct irq_data *d) +void imx_gpc_irq_mask(struct irq_data *d) { void __iomem *reg; u32 val; @@ -120,10 +219,119 @@ writel_relaxed(val, reg); } +static void imx_pu_clk(bool enable) +{ + if (enable) { + if (cpu_is_imx6sl()) { + clk_prepare_enable(gpu2d_clk); + clk_prepare_enable(openvg_axi_clk); + } else { + clk_prepare_enable(gpu3d_clk); + clk_prepare_enable(gpu3d_shader_clk); + clk_prepare_enable(vpu_clk); + clk_prepare_enable(gpu2d_clk); + clk_prepare_enable(gpu2d_axi_clk); + clk_prepare_enable(openvg_axi_clk); + } + } else { + if (cpu_is_imx6sl()) { + clk_disable_unprepare(gpu2d_clk); + clk_disable_unprepare(openvg_axi_clk); + } else { + clk_disable_unprepare(gpu3d_clk); + clk_disable_unprepare(gpu3d_shader_clk); + clk_disable_unprepare(vpu_clk); + clk_disable_unprepare(gpu2d_clk); + clk_disable_unprepare(gpu2d_axi_clk); + clk_disable_unprepare(openvg_axi_clk); + } + } +} + +static void imx_gpc_pu_enable(bool enable) +{ + u32 rate, delay_us; + u32 gpu_pupscr_sw2iso, gpu_pdnscr_iso2sw; + u32 gpu_pupscr_sw, gpu_pdnscr_iso; + + /* get ipg clk rate for PGC delay */ + rate = clk_get_rate(ipg_clk); + + if (enable) { + imx_anatop_pu_enable(true); + /* + * need to add necessary delay between powering up PU LDO and + * disabling PU isolation in PGC, the counter of PU isolation + * is based on ipg clk. + */ + gpu_pupscr_sw2iso = (readl_relaxed(gpc_base + + GPC_PGC_GPU_PUPSCR) >> GPC_PGC_GPU_SW2ISO_SHIFT) + & GPC_PGC_GPU_SW2ISO_MASK; + gpu_pupscr_sw = (readl_relaxed(gpc_base + + GPC_PGC_GPU_PUPSCR) >> GPC_PGC_GPU_SW_SHIFT) + & GPC_PGC_GPU_SW_MASK; + delay_us = (gpu_pupscr_sw2iso + gpu_pupscr_sw) * 1000000 + / rate + 1; + udelay(delay_us); + + imx_pu_clk(true); + writel_relaxed(1, gpc_base + GPC_PGC_GPU_PDN); + writel_relaxed(1 << GPC_CNTR_PU_UP_REQ_SHIFT, + gpc_base + GPC_CNTR); + while (readl_relaxed(gpc_base + GPC_CNTR) & + (1 << GPC_CNTR_PU_UP_REQ_SHIFT)) + ; + imx_pu_clk(false); + } else { + writel_relaxed(1, gpc_base + GPC_PGC_GPU_PDN); + writel_relaxed(1 << GPC_CNTR_PU_DOWN_REQ_SHIFT, + gpc_base + GPC_CNTR); + while (readl_relaxed(gpc_base + GPC_CNTR) & + (1 << GPC_CNTR_PU_DOWN_REQ_SHIFT)) + ; + /* + * need to add necessary delay between enabling PU isolation + * in PGC and powering down PU LDO , the counter of PU isolation + * is based on ipg clk. + */ + gpu_pdnscr_iso2sw = (readl_relaxed(gpc_base + + GPC_PGC_GPU_PDNSCR) >> GPC_PGC_GPU_SW2ISO_SHIFT) + & GPC_PGC_GPU_SW2ISO_MASK; + gpu_pdnscr_iso = (readl_relaxed(gpc_base + + GPC_PGC_GPU_PDNSCR) >> GPC_PGC_GPU_SW_SHIFT) + & GPC_PGC_GPU_SW_MASK; + delay_us = (gpu_pdnscr_iso2sw + gpu_pdnscr_iso) * 1000000 + / rate + 1; + udelay(delay_us); + imx_anatop_pu_enable(false); + } +} + +static int imx_gpc_regulator_notify(struct notifier_block *nb, + unsigned long event, + void *ignored) +{ + switch (event) { + case REGULATOR_EVENT_PRE_DISABLE: + imx_gpc_pu_enable(false); + break; + case REGULATOR_EVENT_ENABLE: + imx_gpc_pu_enable(true); + break; + default: + break; + } + + return NOTIFY_OK; +} + void __init imx_gpc_init(void) { struct device_node *np; int i; + u32 val; + u32 cpu_pupscr_sw2iso, cpu_pupscr_sw; + u32 cpu_pdnscr_iso2sw, cpu_pdnscr_iso; np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); gpc_base = of_iomap(np, 0); @@ -137,4 +345,197 @@ gic_arch_extn.irq_mask = imx_gpc_irq_mask; gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; + + /* + * If there are CPU isolation timing settings in dts, + * update them according to dts, otherwise, keep them + * with default value in registers. + */ + cpu_pupscr_sw2iso = cpu_pupscr_sw = + cpu_pdnscr_iso2sw = cpu_pdnscr_iso = 0; + + /* Read CPU isolation setting for GPC */ + of_property_read_u32(np, "fsl,cpu_pupscr_sw2iso", &cpu_pupscr_sw2iso); + of_property_read_u32(np, "fsl,cpu_pupscr_sw", &cpu_pupscr_sw); + of_property_read_u32(np, "fsl,cpu_pdnscr_iso2sw", &cpu_pdnscr_iso2sw); + of_property_read_u32(np, "fsl,cpu_pdnscr_iso", &cpu_pdnscr_iso); + + /* Update CPU PUPSCR timing if it is defined in dts */ + val = readl_relaxed(gpc_base + GPC_PGC_CPU_PUPSCR); + if (cpu_pupscr_sw2iso) + val &= ~(GPC_PGC_CPU_SW2ISO_MASK << GPC_PGC_CPU_SW2ISO_SHIFT); + if (cpu_pupscr_sw) + val &= ~(GPC_PGC_CPU_SW_MASK << GPC_PGC_CPU_SW_SHIFT); + val |= cpu_pupscr_sw2iso << GPC_PGC_CPU_SW2ISO_SHIFT; + val |= cpu_pupscr_sw << GPC_PGC_CPU_SW_SHIFT; + writel_relaxed(val, gpc_base + GPC_PGC_CPU_PUPSCR); + + /* Update CPU PDNSCR timing if it is defined in dts */ + val = readl_relaxed(gpc_base + GPC_PGC_CPU_PDNSCR); + if (cpu_pdnscr_iso2sw) + val &= ~(GPC_PGC_CPU_SW2ISO_MASK << GPC_PGC_CPU_SW2ISO_SHIFT); + if (cpu_pdnscr_iso) + val &= ~(GPC_PGC_CPU_SW_MASK << GPC_PGC_CPU_SW_SHIFT); + val |= cpu_pdnscr_iso2sw << GPC_PGC_CPU_SW2ISO_SHIFT; + val |= cpu_pdnscr_iso << GPC_PGC_CPU_SW_SHIFT; + writel_relaxed(val, gpc_base + GPC_PGC_CPU_PDNSCR); +} + +static int imx_pureg_set_voltage(struct regulator_dev *reg, int min_uV, + int max_uV, unsigned *selector) +{ + return 0; +} + +static int imx_pureg_enable(struct regulator_dev *rdev) +{ + pu_dummy_enable = 1; + + return 0; +} + +static int imx_pureg_disable(struct regulator_dev *rdev) +{ + pu_dummy_enable = 0; + + return 0; +} + +static int imx_pureg_is_enable(struct regulator_dev *rdev) +{ + return pu_dummy_enable; +} + +static int imx_pureg_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + return 0; +} + +static struct regulator_ops pu_dummy_ops = { + .set_voltage = imx_pureg_set_voltage, + .enable = imx_pureg_enable, + .disable = imx_pureg_disable, + .is_enabled = imx_pureg_is_enable, + .list_voltage = imx_pureg_list_voltage, +}; + +static struct regulator_desc pu_dummy_desc = { + .name = "pureg-dummy", + .id = -1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &pu_dummy_ops, +}; + +static int pu_dummy_probe(struct platform_device *pdev) +{ + struct regulator_config config = { }; + int ret; + + config.dev = &pdev->dev; + config.init_data = &pu_dummy_initdata; + config.of_node = pdev->dev.of_node; + + pu_dummy_regulator_rdev = regulator_register(&pu_dummy_desc, &config); + if (IS_ERR(pu_dummy_regulator_rdev)) { + ret = PTR_ERR(pu_dummy_regulator_rdev); + dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); + return ret; + } + + return 0; } + +static const struct of_device_id imx_pudummy_ids[] = { + { .compatible = "fsl,imx6-dummy-pureg" }, +}; +MODULE_DEVICE_TABLE(of, imx_pudummy_ids); + +static struct platform_driver pu_dummy_driver = { + .probe = pu_dummy_probe, + .driver = { + .name = "pu-dummy", + .owner = THIS_MODULE, + .of_match_table = imx_pudummy_ids, + }, +}; + +static int imx_gpc_probe(struct platform_device *pdev) +{ + int ret; + + gpc_dev = &pdev->dev; + + pu_reg = devm_regulator_get(gpc_dev, "pu"); + if (IS_ERR(pu_reg)) { + ret = PTR_ERR(pu_reg); + dev_info(gpc_dev, "pu regulator not ready.\n"); + return ret; + } + nb.notifier_call = &imx_gpc_regulator_notify; + + /* Get gpu&vpu clk for power up PU by GPC */ + if (cpu_is_imx6sl()) { + gpu2d_clk = devm_clk_get(gpc_dev, "gpu2d_podf"); + openvg_axi_clk = devm_clk_get(gpc_dev, "gpu2d_ovg"); + ipg_clk = devm_clk_get(gpc_dev, "ipg"); + lcd_axi_clk = devm_clk_get(gpc_dev, "lcd_axi"); + lcd_pix_clk = devm_clk_get(gpc_dev, "lcd_pix"); + epdc_axi_clk = devm_clk_get(gpc_dev, "epdc_axi"); + epdc_pix_clk = devm_clk_get(gpc_dev, "epdc_pix"); + pxp_axi_clk = devm_clk_get(gpc_dev, "pxp_axi"); + if (IS_ERR(gpu2d_clk) || IS_ERR(openvg_axi_clk) + || IS_ERR(ipg_clk) || IS_ERR(lcd_axi_clk) + || IS_ERR(lcd_pix_clk) || IS_ERR(epdc_axi_clk) + || IS_ERR(epdc_pix_clk) || IS_ERR(pxp_axi_clk)) { + dev_err(gpc_dev, "failed to get clk!\n"); + return -ENOENT; + } + } else { + gpu3d_clk = devm_clk_get(gpc_dev, "gpu3d_core"); + gpu3d_shader_clk = devm_clk_get(gpc_dev, "gpu3d_shader"); + gpu2d_clk = devm_clk_get(gpc_dev, "gpu2d_core"); + gpu2d_axi_clk = devm_clk_get(gpc_dev, "gpu2d_axi"); + openvg_axi_clk = devm_clk_get(gpc_dev, "openvg_axi"); + vpu_clk = devm_clk_get(gpc_dev, "vpu_axi"); + ipg_clk = devm_clk_get(gpc_dev, "ipg"); + if (IS_ERR(gpu3d_clk) || IS_ERR(gpu3d_shader_clk) + || IS_ERR(gpu2d_clk) || IS_ERR(gpu2d_axi_clk) + || IS_ERR(openvg_axi_clk) || IS_ERR(vpu_clk) + || IS_ERR(ipg_clk)) { + dev_err(gpc_dev, "failed to get clk!\n"); + return -ENOENT; + } + } + + ret = regulator_register_notifier(pu_reg, &nb); + if (ret) { + dev_err(gpc_dev, + "regulator notifier request failed\n"); + return ret; + } + + return 0; +} + +static const struct of_device_id imx_gpc_ids[] = { + { .compatible = "fsl,imx6q-gpc" }, +}; +MODULE_DEVICE_TABLE(of, imx_gpc_ids); + +static struct platform_driver imx_gpc_platdrv = { + .driver = { + .name = "imx-gpc", + .owner = THIS_MODULE, + .of_match_table = imx_gpc_ids, + }, + .probe = imx_gpc_probe, +}; +module_platform_driver(imx_gpc_platdrv); + +module_platform_driver(pu_dummy_driver); + +MODULE_AUTHOR("Anson Huang "); +MODULE_DESCRIPTION("Freescale i.MX GPC driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/arch/arm/mach-imx/hardware.h linux-3.10.30-cubox-i/arch/arm/mach-imx/hardware.h --- linux-3.10.30/arch/arm/mach-imx/hardware.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/hardware.h 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Juergen Beisert, kernel@pengutronix.de * * This program is free software; you can redistribute it and/or @@ -20,6 +20,9 @@ #ifndef __ASM_ARCH_MXC_HARDWARE_H__ #define __ASM_ARCH_MXC_HARDWARE_H__ +#ifndef __ASSEMBLY__ +#include +#endif #include #define addr_in_module(addr, mod) \ @@ -104,6 +107,7 @@ #include "mx51.h" #include "mx53.h" +#include "mx6.h" #include "mx3x.h" #include "mx31.h" #include "mx35.h" diff -Nur linux-3.10.30/arch/arm/mach-imx/hotplug.c linux-3.10.30-cubox-i/arch/arm/mach-imx/hotplug.c --- linux-3.10.30/arch/arm/mach-imx/hotplug.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/hotplug.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2011, 2013 Freescale Semiconductor, Inc. * Copyright 2011 Linaro Ltd. * * The code contained herein is licensed under the GNU General Public @@ -52,7 +52,8 @@ * the register being cleared to kill the cpu. */ imx_set_cpu_arg(cpu, ~0); - cpu_do_idle(); + for (;;) + cpu_do_idle(); } int imx_cpu_kill(unsigned int cpu) diff -Nur linux-3.10.30/arch/arm/mach-imx/imx25-dt.c linux-3.10.30-cubox-i/arch/arm/mach-imx/imx25-dt.c --- linux-3.10.30/arch/arm/mach-imx/imx25-dt.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/imx25-dt.c 2014-03-08 20:32:54.000000000 +0100 @@ -19,6 +19,8 @@ static void __init imx25_dt_init(void) { + mxc_arch_reset_init_dt(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff -Nur linux-3.10.30/arch/arm/mach-imx/imx27-dt.c linux-3.10.30-cubox-i/arch/arm/mach-imx/imx27-dt.c --- linux-3.10.30/arch/arm/mach-imx/imx27-dt.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/imx27-dt.c 2014-03-08 20:32:54.000000000 +0100 @@ -22,6 +22,8 @@ { struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + mxc_arch_reset_init_dt(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); platform_device_register_full(&devinfo); diff -Nur linux-3.10.30/arch/arm/mach-imx/imx31-dt.c linux-3.10.30-cubox-i/arch/arm/mach-imx/imx31-dt.c --- linux-3.10.30/arch/arm/mach-imx/imx31-dt.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/imx31-dt.c 2014-03-08 20:32:54.000000000 +0100 @@ -20,6 +20,8 @@ static void __init imx31_dt_init(void) { + mxc_arch_reset_init_dt(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff -Nur linux-3.10.30/arch/arm/mach-imx/imx51-dt.c linux-3.10.30-cubox-i/arch/arm/mach-imx/imx51-dt.c --- linux-3.10.30/arch/arm/mach-imx/imx51-dt.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/imx51-dt.c 2014-03-08 20:32:54.000000000 +0100 @@ -23,6 +23,8 @@ { struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; + mxc_arch_reset_init_dt(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); platform_device_register_full(&devinfo); } diff -Nur linux-3.10.30/arch/arm/mach-imx/imx6sl_wfi.S linux-3.10.30-cubox-i/arch/arm/mach-imx/imx6sl_wfi.S --- linux-3.10.30/arch/arm/mach-imx/imx6sl_wfi.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/imx6sl_wfi.S 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,639 @@ +/* + * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#define IRAM_WAIT_SIZE (1 << 11) + + .macro sl_ddr_io_save + + ldr r4, [r1, #0x30c] /* DRAM_DQM0 */ + ldr r5, [r1, #0x310] /* DRAM_DQM1 */ + ldr r6, [r1, #0x314] /* DRAM_DQM2 */ + ldr r7, [r1, #0x318] /* DRAM_DQM3 */ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x5c4] /* GPR_B0DS */ + ldr r5, [r1, #0x5cc] /* GPR_B1DS */ + ldr r6, [r1, #0x5d4] /* GPR_B2DS */ + ldr r7, [r1, #0x5d8] /* GPR_B3DS */ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x300] /* DRAM_CAS */ + ldr r5, [r1, #0x31c] /* DRAM_RAS */ + ldr r6, [r1, #0x338] /* DRAM_SDCLK_0 */ + ldr r7, [r1, #0x5ac] /* GPR_ADDS*/ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x5b0] /* DDRMODE_CTL */ + ldr r5, [r1, #0x5c0] /* DDRMODE */ + ldr r6, [r1, #0x33c] /* DRAM_SODT0*/ + ldr r7, [r1, #0x340] /* DRAM_SODT1*/ + stmfd r9!, {r4-r7} + + ldr r4, [r1, #0x330] /* DRAM_SDCKE0 */ + ldr r5, [r1, #0x334] /* DRAM_SDCKE1 */ + ldr r6, [r1, #0x320] /* DRAM_RESET */ + stmfd r9!, {r4-r6} + + .endm + + .macro sl_ddr_io_restore + + /* + * r9 points to IRAM stack. + * r1 points to IOMUX base address. + * r8 points to MMDC base address. + */ + ldmea r9!, {r4-r7} + str r4, [r1, #0x30c] /* DRAM_DQM0 */ + str r5, [r1, #0x310] /* DRAM_DQM1 */ + str r6, [r1, #0x314] /* DRAM_DQM2 */ + str r7, [r1, #0x318] /* DRAM_DQM3 */ + + ldmea r9!, {r4-r7} + str r4, [r1, #0x5c4] /* GPR_B0DS */ + str r5, [r1, #0x5cc] /* GPR_B1DS */ + str r6, [r1, #0x5d4] /* GPR_B2DS */ + str r7, [r1, #0x5d8] /* GPR_B3DS */ + + ldmea r9!, {r4-r7} + str r4, [r1, #0x300] /* DRAM_CAS */ + str r5, [r1, #0x31c] /* DRAM_RAS */ + str r6, [r1, #0x338] /* DRAM_SDCLK_0 */ + str r7, [r1, #0x5ac] /* GPR_ADDS*/ + + ldmea r9!, {r4-r7} + str r4, [r1, #0x5b0] /* DDRMODE_CTL */ + str r5, [r1, #0x5c0] /* DDRMODE */ + str r6, [r1, #0x33c] /* DRAM_SODT0*/ + str r7, [r1, #0x340] /* DRAM_SODT1*/ + + ldmea r9!, {r4-r6} + str r4, [r1, #0x330] /* DRAM_SDCKE0 */ + str r5, [r1, #0x334] /* DRAM_SDCKE1 */ + str r6, [r1, #0x320] /* DRAM_RESET */ + + /* + * Need to reset the FIFO to avoid MMDC lockup + * caused because of floating/changing the + * configuration of many DDR IO pads. + */ + ldr r7, =0x83c + ldr r6, [r8, r7] + orr r6, r6, #0x80000000 + str r6, [r8, r7] +fifo_reset1_wait: + ldr r6, [r8, r7] + and r6, r6, #0x80000000 + cmp r6, #0 + bne fifo_reset1_wait + + /* reset FIFO a second time */ + ldr r6, [r8, r7] + orr r6, r6, #0x80000000 + str r6, [r8, r7] +fifo_reset2_wait: + ldr r6, [r8, r7] + and r6, r6, #0x80000000 + cmp r6, #0 + bne fifo_reset2_wait + + .endm + + .macro sl_ddr_io_set_lpm + + mov r4, #0 + str r4, [r1, #0x30c] /* DRAM_DQM0 */ + str r4, [r1, #0x310] /* DRAM_DQM1 */ + str r4, [r1, #0x314] /* DRAM_DQM2 */ + str r4, [r1, #0x318] /* DRAM_DQM3 */ + + str r4, [r1, #0x5c4] /* GPR_B0DS */ + str r4, [r1, #0x5cc] /* GPR_B1DS */ + str r4, [r1, #0x5d4] /* GPR_B2DS */ + str r4, [r1, #0x5d8] /* GPR_B3DS */ + + str r4, [r1, #0x300] /* DRAM_CAS */ + str r4, [r1, #0x31c] /* DRAM_RAS */ + str r4, [r1, #0x338] /* DRAM_SDCLK_0 */ + str r4, [r1, #0x5ac] /* GPR_ADDS*/ + + str r4, [r1, #0x5b0] /* DDRMODE_CTL */ + str r4, [r1, #0x5c0] /* DDRMODE */ + str r4, [r1, #0x33c] /* DRAM_SODT0*/ + str r4, [r1, #0x340] /* DRAM_SODT1*/ + + mov r4, #0x80000 + str r4, [r1, #0x320] /* DRAM_RESET */ + mov r4, #0x1000 + str r4, [r1, #0x330] /* DRAM_SDCKE0 */ + str r4, [r1, #0x334] /* DRAM_SDCKE1 */ + + .endm + +/* + * imx6sl_low_power_wfi + * + * Idle the processor (eg, wait for interrupt). + * Make sure DDR is in self-refresh. + * IRQs are already disabled. + * r0: WFI IRAMcode base address. + * r1: IOMUX base address + * r2: Base address of CCM, ANATOP and MMDC + * r3: 1 if in audio_bus_freq_mode + */ + .align 3 +ENTRY(imx6sl_low_power_wfi) + + push {r4-r11} + +mx6sl_lpm_wfi: + /* Store audio_bus_freq_mode */ + mov r11, r3 + + mov r4,r2 + /* Get the IRAM data storage address. */ + mov r10, r0 + mov r9, r0 /* get suspend_iram_base */ + add r9, r9, #IRAM_WAIT_SIZE + + /* Anatop Base address in r3. */ + ldr r3, [r4] + /* CCM Base Address in r2 */ + ldr r2, [r4, #0x4] + /* MMDC Base Address in r8 */ + ldr r8, [r4, #0x8] + /* L2 Base Address in r7 */ + ldr r7, [r4, #0xC] + + ldr r6, [r8] + ldr r6, [r3] + ldr r6, [r2] + ldr r6, [r1] + + /* Store the original ARM PODF. */ + ldr r0, [r2, #0x10] + + /* Drain all the L1 buffers. */ + dsb + +#ifdef CONFIG_CACHE_L2X0 + /* + * Need to make sure the buffers in L2 are drained. + * Performing a sync operation does this. + */ + mov r6, #0x0 + str r6, [r7, #0x730] +#endif + + /* + * The second dsb might be needed to keep cache sync (device write) + * ordering with the memory accesses before it. + */ + dsb + isb + + /* Save the DDR IO state. */ + sl_ddr_io_save + + /* Disable Automatic power savings. */ + ldr r6, [r8, #0x404] + orr r6, r6, #0x01 + str r6, [r8, #0x404] + + /* Make the DDR explicitly enter self-refresh. */ + ldr r6, [r8, #0x404] + orr r6, r6, #0x200000 + str r6, [r8, #0x404] + +poll_dvfs_set_1: + ldr r6, [r8, #0x404] + and r6, r6, #0x2000000 + cmp r6, #0x2000000 + bne poll_dvfs_set_1 + + /* set SBS step-by-step mode */ + ldr r6, [r8, #0x410] + orr r6, r6, #0x100 + str r6, [r8, #0x410] + + cmp r11, #1 + beq audio_mode + /* + * Now set DDR rate to 1MHz. + * DDR is from bypassed PLL2 on periph2_clk2 path. + * Set the periph2_clk2_podf to divide by 8. + */ + ldr r6, [r2, #0x14] + orr r6, r6, #0x07 + str r6, [r2, #0x14] + + /* Now set MMDC PODF to divide by 3. */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x38 + orr r6, r6, #0x10 + str r6, [r2, #0x14] + b mmdc_podf + +audio_mode: + /* MMDC is from PLL2_200M. + * Set the mmdc_podf to div by 8. + */ + ldr r6, [r2, #0x14] + orr r6, r6, #0x38 + str r6, [r2, #0x14] + + /* Loop till podf is accepted. */ +mmdc_podf: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne mmdc_podf + + /* Set the DDR IO in LPM state. */ + sl_ddr_io_set_lpm + + cmp r11, #1 + beq do_audio_arm_clk + + /* + * Check if none of the PLLs are + * locked, except PLL1 which will get + * bypassed below. + * We should not be here if PLL2 is not + * bypassed. + */ + ldr r7, =1 + /* USB1 PLL3 */ + ldr r6, [r3, #0x10] + and r6, r6, #0x80000000 + cmp r6, #0x80000000 + beq no_analog_saving + + /* USB2 PLL7 */ + ldr r6, [r3, #0x20] + and r6, r6, #0x80000000 + cmp r6, #0x80000000 + beq no_analog_saving + + /* Audio PLL4 */ + ldr r6, [r3, #0x70] + and r6, r6, #0x80000000 + cmp r6, #0x80000000 + beq no_analog_saving + + /* Video PLL5 */ + ldr r6, [r3, #0xA0] + and r6, r6, #0x80000000 + cmp r6, #0x80000000 + beq no_analog_saving + + /* ENET PLL8 */ + ldr r6, [r3, #0xE0] + and r6, r6, #0x80000000 + cmp r6, #0x80000000 + beq no_analog_saving + + b cont + +no_analog_saving: + ldr r7, =0 + +cont: + /* Set the AHB to 3MHz. AXI to 3MHz. */ + ldr r9, [r2, #0x14] + mov r6, r9 + orr r6, r6, #0x1c00 + orr r6, r6, #0x70000 + str r6, [r2, #0x14] + + /* Loop till podf is accepted. */ +ahb_podf: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop + + /* + * Now set ARM to 24MHz. + * Move ARM to be sourced from STEP_CLK + * after setting STEP_CLK to 24MHz. + */ + ldr r6, [r2, #0xc] + bic r6, r6, #0x100 + str r6, [r2, #0x0c] + /* Now PLL1_SW_CLK to step_clk. */ + ldr r6, [r2, #0x0c] + orr r6, r6, #0x4 + str r6, [r2, #0x0c] + + /* Bypass PLL1 and power it down. */ + ldr r6, =(1 << 16) + orr r6, r6, #0x1000 + str r6, [r3, #0x04] + + /* + * Set the ARM PODF to divide by 8. + * IPG is at 1.5MHz here, we need ARM to + * run at the 12:5 ratio (WAIT mode issue). + */ + ldr r6, =0x7 + str r6, [r2, #0x10] + + /* Loop till podf is accepted. */ +podf_loop: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop + + /* + * Check if we can save some + * power in the Analog section. + */ + cmp r7, #0x1 + bne do_wfi + + /* Disable 1p1 brown out. */ + ldr r6, [r3, #0x110] + bic r6, r6, #0x2 + str r6, [r3, #0x110] + + /* Enable the weak 2P5 */ + ldr r6, [r3, #0x130] + orr r6, r6, #0x40000 + str r6, [r3, #0x130] + + /* Disable main 2p5. */ + ldr r6, [r3, #0x130] + bic r6, r6, #0x1 + str r6, [r3, #0x130] + + /* + * Set the OSC bias current to -37.5% + * to drop the power on VDDHIGH. + */ + ldr r6, [r3, #0x150] + orr r6, r6, #0xC000 + str r6, [r3, #0x150] + + /* Enable low power bandgap */ + ldr r6, [r3, #0x260] + orr r6, r6, #0x20 + str r6, [r3, #0x260] + + /* + * Turn off the bias current + * from the regular bandgap. + */ + ldr r6, [r3, #0x260] + orr r6, r6, #0x80 + str r6, [r3, #0x260] + + /* + * Clear the REFTOP_SELFBIASOFF, + * self-bias circuit of the band gap. + * Per RM, should be cleared when + * band gap is powered down. + */ + ldr r6, [r3, #0x150] + bic r6, r6, #0x8 + str r6, [r3, #0x150] + + /* Power down the regular bandgap. */ + ldr r6, [r3, #0x150] + orr r6, r6, #0x1 + str r6, [r3, #0x150] + + b do_wfi + +do_audio_arm_clk: + /* + * ARM is from PLL2_PFD2_400M here. + * Switch ARM to bypassed PLL1. + */ + ldr r6, [r2, #0xC] + bic r6, r6, #0x4 + str r6, [r2, #0xC] + + /* + * Set the ARM_PODF to divide by 2 + * as IPG is at 4MHz, we cannot run + * ARM_CLK above 9.6MHz when + * system enters WAIT mode. + */ + ldr r6, =0x2 + str r6, [r2, #0x10] + + /* Loop till podf is accepted. */ +podf_loop_audio: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop_audio + +do_wfi: + /* Now do WFI. */ + wfi + + /* Set original ARM PODF back. */ + str r0, [r2, #0x10] + + /* Loop till podf is accepted. */ +podf_loop1: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop1 + + cmp r11, #1 + beq audio_arm_clk_restore + + /* + * Check if powered down + * analog components. + */ + cmp r7, #0x1 + bne skip_analog_restore + + /* Power up the regular bandgap. */ + ldr r6, [r3, #0x150] + bic r6, r6, #0x1 + str r6, [r3, #0x150] + + /* + * Turn on the bias current + * from the regular bandgap. + */ + ldr r6, [r3, #0x260] + bic r6, r6, #0x80 + str r6, [r3, #0x260] + + /* Disable the low power bandgap */ + ldr r6, [r3, #0x260] + bic r6, r6, #0x20 + str r6, [r3, #0x260] + + /* + * Set the OSC bias current to max + * value for normal operation. + */ + ldr r6, [r3, #0x150] + bic r6, r6, #0xC000 + str r6, [r3, #0x150] + + /* Enable main 2p5. */ + ldr r6, [r3, #0x130] + orr r6, r6, #0x1 + str r6, [r3, #0x130] + + /* Ensure the 2P5 is up. */ +loop_2p5: + ldr r6, [r3, #0x130] + and r6, r6, #0x20000 + cmp r6, #0x20000 + bne loop_2p5 + + /* Disable the weak 2P5 */ + ldr r6, [r3, #0x130] + bic r6, r6, #0x40000 + str r6, [r3, #0x130] + + /* Enable 1p1 brown out. */ + ldr r6, [r3, #0x110] + orr r6, r6, #0x2 + str r6, [r3, #0x110] + +skip_analog_restore: + + /* Power up PLL1 and un-bypass it. */ + ldr r6, =(1 << 12) + str r6, [r3, #0x08] + + /* Wait for PLL1 to relock. */ +wait_for_pll_lock: + ldr r6, [r3, #0x0] + and r6, r6, #0x80000000 + cmp r6, #0x80000000 + bne wait_for_pll_lock + + ldr r6, =(1 << 16) + str r6, [r3, #0x08] + + /* Set PLL1_sw_clk back to PLL1. */ + ldr r6, [r2, #0x0c] + bic r6, r6, #0x4 + str r6, [r2, #0xc] + + /* Restore AHB/AXI back. */ + str r9, [r2, #0x14] + + /* Loop till podf is accepted. */ +ahb_podf1: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne podf_loop1 + + b wfi_restore + + audio_arm_clk_restore: + /* Move ARM back to PLL2_PFD2_400M */ + ldr r6, [r2, #0xC] + orr r6, r6, #0x4 + str r6, [r2, #0xC] + +wfi_restore: + /* get suspend_iram_base */ + mov r9, r10 + add r9, r9, #IRAM_WAIT_SIZE + + /* Restore the DDR IO before exiting self-refresh. */ + sl_ddr_io_restore + + /* + * Set MMDC back to 24MHz. + * Set periph2_clk2_podf to divide by 1 + * Now set MMDC PODF to divide by 1. + */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x3f + str r6, [r2, #0x14] + +mmdc_podf1: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne mmdc_podf1 + + /* clear DVFS - exit from self refresh mode */ + ldr r6, [r8, #0x404] + bic r6, r6, #0x200000 + str r6, [r8, #0x404] + +poll_dvfs_clear_1: + ldr r6, [r8, #0x404] + and r6, r6, #0x2000000 + cmp r6, #0x2000000 + beq poll_dvfs_clear_1 + + /* + * Add these nops so that the + * prefetcher will not try to get + * any instructions from DDR. + * The prefetch depth is about 23 + * on A9, so adding 25 nops. + */ + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + + /* Enable Automatic power savings. */ + ldr r6, [r8, #0x404] + bic r6, r6, #0x01 + str r6, [r8, #0x404] + + /* clear SBS - unblock DDR accesses */ + ldr r6, [r8, #0x410] + bic r6, r6, #0x100 + str r6, [r8, #0x410] + + + pop {r4-r11} + + /* Restore registers */ + mov pc, lr diff -Nur linux-3.10.30/arch/arm/mach-imx/lpddr2_freq_imx6.S linux-3.10.30-cubox-i/arch/arm/mach-imx/lpddr2_freq_imx6.S --- linux-3.10.30/arch/arm/mach-imx/lpddr2_freq_imx6.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/lpddr2_freq_imx6.S 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + + .macro mx6sl_switch_to_24MHz + + /* + * Set MMDC clock to be sourced from PLL3. + * Ensure first periph2_clk2 is sourced from PLL3. + * Set the PERIPH2_CLK2_PODF to divide by 2. + */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x7 + orr r6, r6, #0x1 + str r6, [r2, #0x14] + + /* Select PLL3 to source MMDC. */ + ldr r6, [r2, #0x18] + bic r6, r6, #0x100000 + str r6, [r2, #0x18] + + /* Swtich periph2_clk_sel to run from PLL3. */ + ldr r6, [r2, #0x14] + orr r6, r6, #0x4000000 + str r6, [r2, #0x14] + +periph2_clk_switch1: + ldr r6, [r2, #0x48] + cmp r6, #0 + bne periph2_clk_switch1 + + /* + * Need to clock gate the 528 PFDs before + * powering down PLL2. + * Only the PLL2_PFD2_400M should be ON + * at this time, so only clock gate that one. + */ + ldr r6, [r3, #0x100] + orr r6, r6, #0x800000 + str r6, [r3, #0x100] + + /* + * Set PLL2 to bypass state. We should be here + * only if MMDC is not sourced from PLL2. + */ + ldr r6, [r3, #0x30] + orr r6, r6, #0x10000 + str r6, [r3, #0x30] + + ldr r6, [r3, #0x30] + orr r6, r6, #0x1000 + str r6, [r3, #0x30] + + /* Ensure pre_periph2_clk_mux is set to pll2 */ + ldr r6, [r2, #0x18] + bic r6, r6, #0x600000 + str r6, [r2, #0x18] + + /* Set MMDC clock to be sourced from the bypassed PLL2. */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x4000000 + str r6, [r2, #0x14] + +periph2_clk_switch2: + ldr r6, [r2, #0x48] + cmp r6, #0 + bne periph2_clk_switch2 + + /* + * Now move MMDC back to periph2_clk2 source. + * after selecting PLL2 as the option. + * Select PLL2 as the source. + */ + ldr r6, [r2, #0x18] + orr r6, r6, #0x100000 + str r6, [r2, #0x18] + + /* set periph2_clk2_podf to divide by 1. */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x7 + str r6, [r2, #0x14] + + /* Now move periph2_clk to periph2_clk2 source */ + ldr r6, [r2, #0x14] + orr r6, r6, #0x4000000 + str r6, [r2, #0x14] + +periph2_clk_switch3: + ldr r6, [r2, #0x48] + cmp r6, #0 + bne periph2_clk_switch3 + + /* Now set the MMDC PODF back to 1.*/ + ldr r6, [r2, #0x14] + bic r6, r6, #0x38 + str r6, [r2, #0x14] + +mmdc_podf0: + ldr r6, [r2, #0x48] + cmp r6, #0 + bne mmdc_podf0 + + .endm + + .macro ddr_switch_400MHz + + /* Set MMDC divider first, in case PLL3 is at 480MHz. */ + ldr r6, [r3, #0x10] + and r6, r6, #0x10000 + cmp r6, #0x10000 + beq pll3_in_bypass + + /* Set MMDC divder to divide by 2. */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x38 + orr r6, r6, #0x8 + str r6, [r2, #0x14] + +mmdc_podf: + ldr r6, [r2, #0x48] + cmp r6, #0 + bne mmdc_podf + +pll3_in_bypass: + /* + * Check if we are switching between + * 400Mhz <-> 100MHz.If so, we should + * try to source MMDC from PLL2_200M. + */ + cmp r1, #0 + beq not_low_bus_freq + + /* Ensure that MMDC is sourced from PLL2 mux first. */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x4000000 + str r6, [r2, #0x14] + +periph2_clk_switch4: + ldr r6, [r2, #0x48] + cmp r6, #0 + bne periph2_clk_switch4 + +not_low_bus_freq: + /* Now ensure periph2_clk2_sel mux is set to PLL3 */ + ldr r6, [r2, #0x18] + bic r6, r6, #0x100000 + str r6, [r2, #0x18] + + /* Now switch MMDC to PLL3. */ + ldr r6, [r2, #0x14] + orr r6, r6, #0x4000000 + str r6, [r2, #0x14] + +periph2_clk_switch5: + ldr r6, [r2, #0x48] + cmp r6, #0 + bne periph2_clk_switch5 + + /* + * Check if PLL2 is already unlocked. + * If so do nothing with PLL2. + */ + cmp r1, #0 + beq pll2_already_on + + /* Now power up PLL2 and unbypass it. */ + ldr r6, [r3, #0x30] + bic r6, r6, #0x1000 + str r6, [r3, #0x30] + + /* Make sure PLL2 has locked.*/ +wait_for_pll_lock: + ldr r6, [r3, #0x30] + and r6, r6, #0x80000000 + cmp r6, #0x80000000 + bne wait_for_pll_lock + + ldr r6, [r3, #0x30] + bic r6, r6, #0x10000 + str r6, [r3, #0x30] + + /* + * Need to enable the 528 PFDs after + * powering up PLL2. + * Only the PLL2_PFD2_400M should be ON + * as it feeds the MMDC. Rest should have + * been managed by clock code. + */ + ldr r6, [r3, #0x100] + bic r6, r6, #0x800000 + str r6, [r3, #0x100] + +pll2_already_on: + /* + * Now switch MMDC clk back to pll2_mux option. + * Ensure pre_periph2_clk2 is set to pll2_pfd_400M. + * If switching to audio DDR freq, set the + * pre_periph2_clk2 to PLL2_PFD_200M + */ + ldr r6, =400000000 + cmp r6, r0 + bne use_pll2_pfd_200M + + ldr r6, [r2, #0x18] + bic r6, r6, #0x600000 + orr r6, r6, #0x200000 + str r6, [r2, #0x18] + ldr r6, =400000000 + b cont2 + +use_pll2_pfd_200M: + ldr r6, [r2, #0x18] + orr r6, r6, #0x600000 + str r6, [r2, #0x18] + ldr r6, =200000000 + +cont2: + ldr r4, [r2, #0x14] + bic r4, r4, #0x4000000 + str r4, [r2, #0x14] + +periph2_clk_switch6: + ldr r4, [r2, #0x48] + cmp r4, #0 + bne periph2_clk_switch6 + +change_divider_only: + /* + * Calculate the MMDC divider + * based on the requested freq. + */ + ldr r4, =0 +Loop2: + sub r6, r6, r0 + cmp r6, r0 + blt Div_Found + add r4, r4, #1 + bgt Loop2 + + /* Shift divider into correct offset. */ + lsl r4, r4, #3 +Div_Found: + /* Set the MMDC PODF. */ + ldr r6, [r2, #0x14] + bic r6, r6, #0x38 + orr r6, r6, r4 + str r6, [r2, #0x14] + +mmdc_podf1: + ldr r6, [r2, #0x48] + cmp r6, #0 + bne mmdc_podf1 + + .endm + + .macro mmdc_clk_lower_100MHz + + /* + * Prior to reducing the DDR frequency (at 528/400 MHz), + * read the Measure unit count bits (MU_UNIT_DEL_NUM) + */ + ldr r5, =0x8B8 + ldr r6, [r8, r5] + /* Original MU unit count */ + mov r6, r6, LSR #16 + ldr r4, =0x3FF + and r6, r6, r4 + /* Original MU unit count * 2 */ + mov r7, r6, LSL #1 + /* + * Bypass the automatic measure unit when below 100 MHz + * by setting the Measure unit bypass enable bit (MU_BYP_EN) + */ + ldr r6, [r8, r5] + orr r6, r6, #0x400 + str r6, [r8, r5] + /* + * Double the measure count value read in step 1 and program it in the + * measurement bypass bits (MU_BYP_VAL) of the MMDC PHY Measure Unit + * Register for the reduced frequency operation below 100 MHz + */ + ldr r6, [r8, r5] + ldr r4, =0x3FF + bic r6, r6, r4 + orr r6, r6, r7 + str r6, [r8, r5] + /* Now perform a Force Measurement. */ + ldr r6, [r8, r5] + orr r6, r6, #0x800 + str r6, [r8, r5] + /* Wait for FRC_MSR to clear. */ +force_measure: + ldr r6, [r8, r5] + and r6, r6, #0x800 + cmp r6, #0x0 + bne force_measure + + .endm + + .macro mmdc_clk_above_100MHz + + /* Make sure that the PHY measurement unit is NOT in bypass mode */ + ldr r5, =0x8B8 + ldr r6, [r8, r5] + bic r6, r6, #0x400 + str r6, [r8, r5] + /* Now perform a Force Measurement. */ + ldr r6, [r8, r5] + orr r6, r6, #0x800 + str r6, [r8, r5] + /* Wait for FRC_MSR to clear. */ +force_measure1: + ldr r6, [r8, r5] + and r6, r6, #0x800 + cmp r6, #0x0 + bne force_measure1 + .endm + +/* + * mx6_lpddr2_freq_change + * + * Make sure DDR is in self-refresh. + * IRQs are already disabled. + * r0 : DDR freq. + * r1: low_bus_freq_mode flag + * r2: Pointer to array containing addresses of registers. + */ + .align 3 +ENTRY(mx6_lpddr2_freq_change) + + push {r4-r10} + + mov r4, r2 + ldr r3, [r4] @ANATOP_BASE_ADDR + ldr r2, [r4, #0x4] @CCM_BASE_ADDR + ldr r8, [r4, #0x8] @MMDC_P0_BASE_ADDR + ldr r7, [r4, #0xC] @L2_BASE_ADDR + +lpddr2_freq_change: + adr r9, lpddr2_freq_change + + /* Prime all TLB entries. */ + ldr r6, [r9] + ldr r6, [r8] + ldr r6, [r3] + ldr r6, [r2] + + /* Drain all the L1 buffers. */ + dsb + +#ifdef CONFIG_CACHE_L2X0 + /* + * Need to make sure the buffers in L2 are drained. + * Performing a sync operation does this. + */ + mov r6, #0x0 + str r6, [r7, #0x730] +#endif + + /* + * The second dsb might be needed to keep cache sync (device write) + * ordering with the memory accesses before it. + */ + dsb + isb + + /* Disable Automatic power savings. */ + ldr r6, [r8, #0x404] + orr r6, r6, #0x01 + str r6, [r8, #0x404] + + /* MMDC0_MDPDC disable power down timer */ + ldr r6, [r8, #0x4] + bic r6, r6, #0xff00 + str r6, [r8, #0x4] + + /* Delay for a while */ + ldr r10, =10 +delay1: + ldr r7, =0 +cont1: + ldr r6, [r8, r7] + add r7, r7, #4 + cmp r7, #16 + bne cont1 + sub r10, r10, #1 + cmp r10, #0 + bgt delay1 + + /* Make the DDR explicitly enter self-refresh. */ + ldr r6, [r8, #0x404] + orr r6, r6, #0x200000 + str r6, [r8, #0x404] + +poll_dvfs_set_1: + ldr r6, [r8, #0x404] + and r6, r6, #0x2000000 + cmp r6, #0x2000000 + bne poll_dvfs_set_1 + + /* set SBS step-by-step mode */ + ldr r6, [r8, #0x410] + orr r6, r6, #0x100 + str r6, [r8, #0x410] + + ldr r10, =100000000 + cmp r0, r10 + bgt set_ddr_mu_above_100 + mmdc_clk_lower_100MHz + +set_ddr_mu_above_100: + ldr r10, =24000000 + cmp r0, r10 + beq set_to_24MHz + + ddr_switch_400MHz + + ldr r10,=100000000 + cmp r0, r10 + blt done + mmdc_clk_above_100MHz + + b done + +set_to_24MHz: + mx6sl_switch_to_24MHz + +done: + /* clear DVFS - exit from self refresh mode */ + ldr r6, [r8, #0x404] + bic r6, r6, #0x200000 + str r6, [r8, #0x404] + +poll_dvfs_clear_1: + ldr r6, [r8, #0x404] + and r6, r6, #0x2000000 + cmp r6, #0x2000000 + beq poll_dvfs_clear_1 + + /* Enable Automatic power savings. */ + ldr r6, [r8, #0x404] + bic r6, r6, #0x01 + str r6, [r8, #0x404] + + ldr r10, =24000000 + cmp r0, r10 + beq skip_power_down + + /* Enable MMDC power down timer. */ + ldr r6, [r8, #0x4] + orr r6, r6, #0x5500 + str r6, [r8, #0x4] + +skip_power_down: + /* clear SBS - unblock DDR accesses */ + ldr r6, [r8, #0x410] + bic r6, r6, #0x100 + str r6, [r8, #0x410] + + pop {r4-r10} + + /* Restore registers */ + mov pc, lr + + .type mx6_lpddr2_do_iram, #object +ENTRY(mx6_lpddr2_do_iram) + .word mx6_lpddr2_freq_change + .size mx6_lpddr2_freq_change, . - mx6_lpddr2_freq_change diff -Nur linux-3.10.30/arch/arm/mach-imx/mach-imx53.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mach-imx53.c --- linux-3.10.30/arch/arm/mach-imx/mach-imx53.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mach-imx53.c 2014-03-08 20:32:54.000000000 +0100 @@ -21,6 +21,7 @@ #include #include "common.h" +#include "hardware.h" #include "mx53.h" static void __init imx53_qsb_init(void) @@ -38,6 +39,8 @@ static void __init imx53_dt_init(void) { + mxc_arch_reset_init_dt(); + if (of_machine_is_compatible("fsl,imx53-qsb")) imx53_qsb_init(); diff -Nur linux-3.10.30/arch/arm/mach-imx/mach-imx6q.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mach-imx6q.c --- linux-3.10.30/arch/arm/mach-imx/mach-imx6q.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mach-imx6q.c 2014-03-08 20:32:54.000000000 +0100 @@ -10,12 +10,14 @@ * http://www.gnu.org/copyleft/gpl.html */ +#include #include +#include #include #include #include -#include #include +#include #include #include #include @@ -23,13 +25,15 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include +#include #include #include #include @@ -38,62 +42,67 @@ #include "cpuidle.h" #include "hardware.h" -static u32 chip_revision; +static struct flexcan_platform_data flexcan_pdata[2]; +static int flexcan_en_gpio; +static int flexcan_stby_gpio; +static int flexcan0_en; +static int flexcan1_en; +static void mx6q_flexcan_switch(void) +{ + if (flexcan0_en || flexcan1_en) { + /* + * The transceiver TJA1041A on sabreauto RevE baseboard will + * fail to transit to Normal state if EN/STBY is high by default + * after board power up. So we set the EN/STBY initial state to low + * first then to high to guarantee the state transition successfully. + */ + gpio_set_value_cansleep(flexcan_en_gpio, 0); + gpio_set_value_cansleep(flexcan_stby_gpio, 0); + + gpio_set_value_cansleep(flexcan_en_gpio, 1); + gpio_set_value_cansleep(flexcan_stby_gpio, 1); + } else { + /* + * avoid to disable CAN xcvr if any of the CAN interfaces + * are down. XCRV will be disabled only if both CAN2 + * interfaces are DOWN. + */ + gpio_set_value_cansleep(flexcan_en_gpio, 0); + gpio_set_value_cansleep(flexcan_stby_gpio, 0); + } +} -int imx6q_revision(void) +static void imx6q_flexcan0_switch_auto(int enable) { - return chip_revision; + flexcan0_en = enable; + mx6q_flexcan_switch(); } -static void __init imx6q_init_revision(void) +static void imx6q_flexcan1_switch_auto(int enable) { - u32 rev = imx_anatop_get_digprog(); - - switch (rev & 0xff) { - case 0: - chip_revision = IMX_CHIP_REVISION_1_0; - break; - case 1: - chip_revision = IMX_CHIP_REVISION_1_1; - break; - case 2: - chip_revision = IMX_CHIP_REVISION_1_2; - break; - default: - chip_revision = IMX_CHIP_REVISION_UNKNOWN; - } - - mxc_set_cpu_type(rev >> 16 & 0xff); + flexcan1_en = enable; + mx6q_flexcan_switch(); } -static void imx6q_restart(char mode, const char *cmd) +static int __init imx6q_flexcan_fixup_auto(void) { struct device_node *np; - void __iomem *wdog_base; - - np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-wdt"); - wdog_base = of_iomap(np, 0); - if (!wdog_base) - goto soft; - - imx_src_prepare_restart(); - /* enable wdog */ - writew_relaxed(1 << 2, wdog_base); - /* write twice to ensure the request will not get ignored */ - writew_relaxed(1 << 2, wdog_base); - - /* wait for reset to assert ... */ - mdelay(500); - - pr_err("Watchdog reset failed to assert reset\n"); - - /* delay to allow the serial port to show the message */ - mdelay(50); + np = of_find_node_by_path("/soc/aips-bus@02000000/can@02090000"); + if (!np) + return -ENODEV; + + flexcan_en_gpio = of_get_named_gpio(np, "trx-en-gpio", 0); + flexcan_stby_gpio = of_get_named_gpio(np, "trx-stby-gpio", 0); + if (gpio_is_valid(flexcan_en_gpio) && gpio_is_valid(flexcan_stby_gpio) && + !gpio_request_one(flexcan_en_gpio, GPIOF_DIR_OUT, "flexcan-trx-en") && + !gpio_request_one(flexcan_stby_gpio, GPIOF_DIR_OUT, "flexcan-trx-stby")) { + /* flexcan 0 & 1 are using the same GPIOs for transceiver */ + flexcan_pdata[0].transceiver_switch = imx6q_flexcan0_switch_auto; + flexcan_pdata[1].transceiver_switch = imx6q_flexcan1_switch_auto; + } -soft: - /* we'll take a jump through zero as a poor second */ - soft_restart(0); + return 0; } /* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */ @@ -113,36 +122,105 @@ return 0; } -static void __init imx6q_sabrelite_cko1_setup(void) +static void mmd_write_reg(struct phy_device *dev, int device, int reg, int val) { - struct clk *cko1_sel, *ahb, *cko1; - unsigned long rate; + phy_write(dev, 0x0d, device); + phy_write(dev, 0x0e, reg); + phy_write(dev, 0x0d, (1 << 14) | device); + phy_write(dev, 0x0e, val); +} - cko1_sel = clk_get_sys(NULL, "cko1_sel"); - ahb = clk_get_sys(NULL, "ahb"); - cko1 = clk_get_sys(NULL, "cko1"); - if (IS_ERR(cko1_sel) || IS_ERR(ahb) || IS_ERR(cko1)) { - pr_err("cko1 setup failed!\n"); - goto put_clk; - } - clk_set_parent(cko1_sel, ahb); - rate = clk_round_rate(cko1, 16000000); - clk_set_rate(cko1, rate); -put_clk: - if (!IS_ERR(cko1_sel)) - clk_put(cko1_sel); - if (!IS_ERR(ahb)) - clk_put(ahb); - if (!IS_ERR(cko1)) - clk_put(cko1); +static int ksz9031rn_phy_fixup(struct phy_device *dev) +{ + /* + * min rx data delay, max rx/tx clock delay, + * min rx/tx control delay + */ + mmd_write_reg(dev, 2, 4, 0); + mmd_write_reg(dev, 2, 5, 0); + mmd_write_reg(dev, 2, 8, 0x003ff); + + return 0; +} + +static int ar8031_phy_fixup(struct phy_device *dev) +{ + u16 val; + + /* disable phy AR8031 SmartEEE function. */ + phy_write(dev, 0xd, 0x3); + phy_write(dev, 0xe, 0x805d); + phy_write(dev, 0xd, 0x4003); + val = phy_read(dev, 0xe); + val &= ~(0x1 << 8); + phy_write(dev, 0xe, val); + + /* To enable AR8031 output a 125MHz clk from CLK_25M */ + phy_write(dev, 0xd, 0x7); + phy_write(dev, 0xe, 0x8016); + phy_write(dev, 0xd, 0x4007); + + val = phy_read(dev, 0xe); + val &= 0xffe3; + val |= 0x18; + phy_write(dev, 0xe, val); + + /* introduce tx clock delay */ + phy_write(dev, 0x1d, 0x5); + val = phy_read(dev, 0x1e); + val |= 0x0100; + phy_write(dev, 0x1e, val); + + return 0; } -static void __init imx6q_sabrelite_init(void) +#define PHY_ID_AR8031 0x004dd074 + +static int ar8035_phy_fixup(struct phy_device *dev) { - if (IS_BUILTIN(CONFIG_PHYLIB)) + u16 val; + + /* Ar803x phy SmartEEE feature cause link status generates glitch, + * which cause ethernet link down/up issue, so disable SmartEEE + */ + phy_write(dev, 0xd, 0x3); + phy_write(dev, 0xe, 0x805d); + phy_write(dev, 0xd, 0x4003); + + val = phy_read(dev, 0xe); + phy_write(dev, 0xe, val & ~(1 << 8)); + + /* + * Enable 125MHz clock from CLK_25M on the AR8031. This + * is fed in to the IMX6 on the ENET_REF_CLK (V22) pad. + * Also, introduce a tx clock delay. + * + * This is the same as is the AR8031 fixup. + */ + ar8031_phy_fixup(dev); + + /*check phy power*/ + val = phy_read(dev, 0x0); + if (val & BMCR_PDOWN) + phy_write(dev, 0x0, val & ~BMCR_PDOWN); + + return 0; +} + +#define PHY_ID_AR8035 0x004dd072 + +static void __init imx6q_enet_phy_init(void) +{ + if (IS_BUILTIN(CONFIG_PHYLIB)) { phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK, ksz9021rn_phy_fixup); - imx6q_sabrelite_cko1_setup(); + phy_register_fixup_for_uid(PHY_ID_KSZ9031, MICREL_PHY_ID_MASK, + ksz9031rn_phy_fixup); + phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffff, + ar8031_phy_fixup); + phy_register_fixup_for_uid(PHY_ID_AR8035, 0xffffffef, + ar8035_phy_fixup); + } } static void __init imx6q_1588_init(void) @@ -151,34 +229,175 @@ gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); if (!IS_ERR(gpr)) - regmap_update_bits(gpr, 0x4, 1 << 21, 1 << 21); + regmap_update_bits(gpr, IOMUXC_GPR1, + IMX6Q_GPR1_ENET_CLK_SEL_MASK, + IMX6Q_GPR1_ENET_CLK_SEL_ANATOP); else pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n"); } -static void __init imx6q_usb_init(void) + +static void __init imx6q_csi_mux_init(void) { - imx_anatop_usb_chrg_detect_disable(); + /* + * MX6Q SabreSD board: + * IPU1 CSI0 connects to parallel interface. + * Set GPR1 bit 19 to 0x1. + * + * MX6DL SabreSD board: + * IPU1 CSI0 connects to parallel interface. + * Set GPR13 bit 0-2 to 0x4. + * IPU1 CSI1 connects to MIPI CSI2 virtual channel 1. + * Set GPR13 bit 3-5 to 0x1. + */ + struct regmap *gpr; + + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + if (of_machine_is_compatible("fsl,imx6q-sabresd") || + of_machine_is_compatible("fsl,imx6q-sabreauto")) + regmap_update_bits(gpr, IOMUXC_GPR1, 1 << 19, 1 << 19); + else if (of_machine_is_compatible("fsl,imx6dl-sabresd") || + of_machine_is_compatible("fsl,imx6dl-sabreauto")) + regmap_update_bits(gpr, IOMUXC_GPR13, 0x3F, 0x0C); + } else { + pr_err("%s(): failed to find fsl,imx6q-iomux-gpr regmap\n", + __func__); + } } +/* + * Disable Hannstar LVDS panel CABC function. + * This function turns the panel's backlight density automatically + * according to the content shown on the panel which may cause + * annoying unstable backlight issue. + */ +static void __init imx6q_lvds_cabc_init(void) +{ + struct device_node *np = NULL; + int ret, lvds0_gpio, lvds1_gpio; + + np = of_find_node_by_name(NULL, "lvds_cabc_ctrl"); + if (!np) + return; + + lvds0_gpio = of_get_named_gpio(np, "lvds0-gpios", 0); + if (gpio_is_valid(lvds0_gpio)) { + ret = gpio_request_one(lvds0_gpio, GPIOF_OUT_INIT_LOW, + "LVDS0 CABC enable"); + if (ret) + pr_warn("failed to request LVDS0 CABC gpio\n"); + } + + lvds1_gpio = of_get_named_gpio(np, "lvds1-gpios", 0); + if (gpio_is_valid(lvds1_gpio)) { + ret = gpio_request_one(lvds1_gpio, GPIOF_OUT_INIT_LOW, + "LVDS1 CABC enable"); + if (ret) + pr_warn("failed to request LVDS1 CABC gpio\n"); + } +} + +#define OCOTP_MACn(n) (0x00000620 + (n) * 0x10) +void __init imx6_enet_mac_init(const char *compatible) +{ + struct device_node *ocotp_np, *enet_np; + void __iomem *base; + struct property *newmac; + u32 macaddr_low, macaddr_high; + u8 *macaddr; + + enet_np = of_find_compatible_node(NULL, NULL, compatible); + if (!enet_np) + return; + + if (of_get_mac_address(enet_np)) + goto put_enet_node; + + ocotp_np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp"); + if (!ocotp_np) { + pr_warn("failed to find ocotp node\n"); + goto put_enet_node; + } + + base = of_iomap(ocotp_np, 0); + if (!base) { + pr_warn("failed to map ocotp\n"); + goto put_ocotp_node; + } + + macaddr_high = readl_relaxed(base + OCOTP_MACn(0)); + macaddr_low = readl_relaxed(base + OCOTP_MACn(1)); + + newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL); + if (!newmac) + goto put_ocotp_node; + + newmac->value = newmac + 1; + newmac->length = 6; + newmac->name = kstrdup("local-mac-address", GFP_KERNEL); + if (!newmac->name) { + kfree(newmac); + goto put_ocotp_node; + } + + macaddr = newmac->value; + macaddr[5] = macaddr_high & 0xff; + macaddr[4] = (macaddr_high >> 8) & 0xff; + macaddr[3] = (macaddr_high >> 16) & 0xff; + macaddr[2] = (macaddr_high >> 24) & 0xff; + macaddr[1] = macaddr_low & 0xff; + macaddr[0] = (macaddr_low >> 8) & 0xff; + + of_update_property(enet_np, newmac); + +put_ocotp_node: + of_node_put(ocotp_np); +put_enet_node: + of_node_put(enet_np); +} + +static inline void imx6q_enet_init(void) +{ + imx6_enet_mac_init("fsl,imx6q-fec"); + imx6q_enet_phy_init(); + imx6q_1588_init(); +} + +/* Add auxdata to pass platform data */ +static const struct of_dev_auxdata imx6q_auxdata_lookup[] __initconst = { + OF_DEV_AUXDATA("fsl,imx6q-flexcan", 0x02090000, NULL, &flexcan_pdata[0]), + OF_DEV_AUXDATA("fsl,imx6q-flexcan", 0x02094000, NULL, &flexcan_pdata[1]), + { /* sentinel */ } +}; + static void __init imx6q_init_machine(void) { - if (of_machine_is_compatible("fsl,imx6q-sabrelite")) - imx6q_sabrelite_init(); + struct device *parent; + + mxc_arch_reset_init_dt(); + parent = imx_soc_device_init(); + if (parent == NULL) + pr_warn("failed to initialize soc device\n"); - of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + of_platform_populate(NULL, of_default_bus_match_table, + imx6q_auxdata_lookup, parent); + imx6q_enet_init(); imx_anatop_init(); - imx6q_pm_init(); - imx6q_usb_init(); - imx6q_1588_init(); + imx6_pm_init(); + imx6q_csi_mux_init(); + imx6q_lvds_cabc_init(); } #define OCOTP_CFG3 0x440 #define OCOTP_CFG3_SPEED_SHIFT 16 #define OCOTP_CFG3_SPEED_1P2GHZ 0x3 +#define OCOTP_CFG3_SPEED_1GHZ 0x2 +#define OCOTP_CFG3_SPEED_850MHZ 0x1 +#define OCOTP_CFG3_SPEED_800MHZ 0x0 -static void __init imx6q_opp_check_1p2ghz(struct device *cpu_dev) +static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev) { struct device_node *np; void __iomem *base; @@ -196,11 +415,36 @@ goto put_node; } + /* + * SPEED_GRADING[1:0] defines the max speed of ARM: + * 2b'11: 1200000000Hz; -- i.MX6Q only. + * 2b'10: 1000000000Hz; + * 2b'01: 850000000Hz; -- i.MX6Q Only, exclusive with 1GHz. + * 2b'00: 800000000Hz; + * We need to set the max speed of ARM according to fuse map. + */ val = readl_relaxed(base + OCOTP_CFG3); val >>= OCOTP_CFG3_SPEED_SHIFT; - if ((val & 0x3) != OCOTP_CFG3_SPEED_1P2GHZ) - if (opp_disable(cpu_dev, 1200000000)) - pr_warn("failed to disable 1.2 GHz OPP\n"); + val &= 3; + if (cpu_is_imx6q()) { + if (!val) { + /* fuses not set for IMX_CHIP_REVISION_1_0 */ + if (imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) + val = OCOTP_CFG3_SPEED_1GHZ; + } + if (val < OCOTP_CFG3_SPEED_1P2GHZ) + if (opp_disable(cpu_dev, 1200000000)) + pr_warn("failed to disable 1.2 GHz OPP\n"); + } + if (val < OCOTP_CFG3_SPEED_1GHZ) + if (opp_disable(cpu_dev, 996000000)) + pr_warn("failed to disable 1 GHz OPP\n"); + if (cpu_is_imx6q()) { + if (val < OCOTP_CFG3_SPEED_850MHZ || + val == OCOTP_CFG3_SPEED_1GHZ) + if (opp_disable(cpu_dev, 852000000)) + pr_warn("failed to disable 850 MHz OPP\n"); + } put_node: of_node_put(np); @@ -222,41 +466,60 @@ goto put_node; } - imx6q_opp_check_1p2ghz(cpu_dev); + imx6q_opp_check_speed_grading(cpu_dev); put_node: of_node_put(np); } static struct platform_device imx6q_cpufreq_pdev = { - .name = "imx6q-cpufreq", + .name = "imx6-cpufreq", }; static void __init imx6q_init_late(void) { + struct regmap *gpr; + + /* + * Need to force IOMUXC irq pending to meet CCM low power mode + * restriction, this is recommended by hardware team. + */ + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (!IS_ERR(gpr)) + regmap_update_bits(gpr, IOMUXC_GPR1, + IMX6Q_GPR1_GINT_MASK, + IMX6Q_GPR1_GINT_ASSERT); + /* * WAIT mode is broken on TO 1.0 and 1.1, so there is no point * to run cpuidle on them. */ - if (imx6q_revision() > IMX_CHIP_REVISION_1_1) + if ((cpu_is_imx6q() && imx_get_soc_revision() > IMX_CHIP_REVISION_1_1) + || (cpu_is_imx6dl() && imx_get_soc_revision() > + IMX_CHIP_REVISION_1_0)) imx6q_cpuidle_init(); - if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) { + if (IS_ENABLED(CONFIG_ARM_IMX6_CPUFREQ)) { imx6q_opp_init(&imx6q_cpufreq_pdev.dev); platform_device_register(&imx6q_cpufreq_pdev); } + + if (of_machine_is_compatible("fsl,imx6q-sabreauto") + || of_machine_is_compatible("fsl,imx6dl-sabreauto")) + imx6q_flexcan_fixup_auto(); } static void __init imx6q_map_io(void) { debug_ll_io_init(); imx_scu_map_io(); + imx6_pm_map_io(); } static void __init imx6q_init_irq(void) { - imx6q_init_revision(); - l2x0_of_init(0, ~0UL); + imx_init_revision_from_anatop(); + imx_init_l2cache(); imx_src_init(); imx_gpc_init(); irqchip_init(); @@ -264,10 +527,10 @@ static void __init imx6q_timer_init(void) { - mx6q_clocks_init(); + of_clk_init(NULL); clocksource_of_init(); imx_print_silicon_rev(cpu_is_imx6dl() ? "i.MX6DL" : "i.MX6Q", - imx6q_revision()); + imx_get_soc_revision()); } static const char *imx6q_dt_compat[] __initdata = { @@ -277,6 +540,12 @@ }; DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)") + /* + * i.MX6Q/DL maps system memory at 0x10000000 (offset 256MiB), and + * GPU has a limit on physical address that it accesses, which must + * be below 2GiB. + */ + .dma_zone_size = (SZ_2G - SZ_256M), .smp = smp_ops(imx_smp_ops), .map_io = imx6q_map_io, .init_irq = imx6q_init_irq, @@ -284,5 +553,5 @@ .init_machine = imx6q_init_machine, .init_late = imx6q_init_late, .dt_compat = imx6q_dt_compat, - .restart = imx6q_restart, + .restart = mxc_restart, MACHINE_END diff -Nur linux-3.10.30/arch/arm/mach-imx/mach-imx6sl.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mach-imx6sl.c --- linux-3.10.30/arch/arm/mach-imx/mach-imx6sl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mach-imx6sl.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "cpuidle.h" +#include "hardware.h" + +static struct platform_device imx6sl_cpufreq_pdev = { + .name = "imx6-cpufreq", +}; + +static void __init imx6sl_fec_clk_init(void) +{ + struct regmap *gpr; + + /* set FEC clock from internal PLL clock source */ + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sl-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + regmap_update_bits(gpr, IOMUXC_GPR1, + IMX6SL_GPR1_FEC_CLOCK_MUX2_SEL_MASK, 0); + regmap_update_bits(gpr, IOMUXC_GPR1, + IMX6SL_GPR1_FEC_CLOCK_MUX1_SEL_MASK, 0); + } else + pr_err("failed to find fsl,imx6sl-iomux-gpr regmap\n"); +} + +static inline void imx6sl_fec_init(void) +{ + imx6sl_fec_clk_init(); + imx6_enet_mac_init("fsl,imx6sl-fec"); +} + +static void __init imx6sl_init_machine(void) +{ + struct device *parent; + + mxc_arch_reset_init_dt(); + + parent = imx_soc_device_init(); + if (parent == NULL) + pr_warn("failed to initialize soc device\n"); + + of_platform_populate(NULL, of_default_bus_match_table, NULL, parent); + + imx6sl_fec_init(); + imx_anatop_init(); + imx6_pm_init(); +} + +static void __init imx6sl_opp_init(struct device *cpu_dev) +{ + struct device_node *np; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_warn("failed to find cpu0 node\n"); + return; + } + + cpu_dev->of_node = np; + if (of_init_opp_table(cpu_dev)) { + pr_warn("failed to init OPP table\n"); + goto put_node; + } + +put_node: + of_node_put(np); +} + +static void __init imx6sl_init_late(void) +{ + struct regmap *gpr; + + /* + * Need to force IOMUXC irq pending to meet CCM low power mode + * restriction, this is recommended by hardware team. + */ + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sl-iomuxc-gpr"); + if (!IS_ERR(gpr)) + regmap_update_bits(gpr, IOMUXC_GPR1, + IMX6Q_GPR1_GINT_MASK, + IMX6Q_GPR1_GINT_ASSERT); + else + pr_err("failed to find fsl,imx6sl-iomux-gpr regmap\n"); + + /* Init CPUIDLE */ + imx6sl_cpuidle_init(); + + if (IS_ENABLED(CONFIG_ARM_IMX6_CPUFREQ)) { + imx6sl_opp_init(&imx6sl_cpufreq_pdev.dev); + platform_device_register(&imx6sl_cpufreq_pdev); + } + +} + +static void __init imx6sl_map_io(void) +{ + debug_ll_io_init(); + imx6_pm_map_io(); +} + +static void __init imx6sl_init_irq(void) +{ + imx_init_revision_from_anatop(); + imx_init_l2cache(); + imx_src_init(); + imx_gpc_init(); + irqchip_init(); +} + +static void __init imx6sl_timer_init(void) +{ + of_clk_init(NULL); +} + +static const char *imx6sl_dt_compat[] __initdata = { + "fsl,imx6sl", + NULL, +}; + +DT_MACHINE_START(IMX6SL, "Freescale i.MX6 SoloLite (Device Tree)") + .map_io = imx6sl_map_io, + .init_irq = imx6sl_init_irq, + .init_time = imx6sl_timer_init, + .init_machine = imx6sl_init_machine, + .init_late = imx6sl_init_late, + .dt_compat = imx6sl_dt_compat, + .restart = mxc_restart, +MACHINE_END diff -Nur linux-3.10.30/arch/arm/mach-imx/mach-vf610.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mach-vf610.c --- linux-3.10.30/arch/arm/mach-imx/mach-vf610.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mach-vf610.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +static void __init vf610_init_machine(void) +{ + mxc_arch_reset_init_dt(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static void __init vf610_init_irq(void) +{ + l2x0_of_init(0, ~0UL); + irqchip_init(); +} + +static void __init vf610_init_time(void) +{ + of_clk_init(NULL); + clocksource_of_init(); +} + +static const char *vf610_dt_compat[] __initdata = { + "fsl,vf610", + NULL, +}; + +DT_MACHINE_START(VYBRID_VF610, "Freescale Vybrid VF610 (Device Tree)") + .map_io = debug_ll_io_init, + .init_irq = vf610_init_irq, + .init_time = vf610_init_time, + .init_machine = vf610_init_machine, + .dt_compat = vf610_dt_compat, + .restart = mxc_restart, +MACHINE_END diff -Nur linux-3.10.30/arch/arm/mach-imx/mm-imx1.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx1.c --- linux-3.10.30/arch/arm/mach-imx/mm-imx1.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx1.c 2014-03-08 20:32:54.000000000 +0100 @@ -39,7 +39,6 @@ void __init imx1_init_early(void) { mxc_set_cpu_type(MXC_CPU_MX1); - mxc_arch_reset_init(MX1_IO_ADDRESS(MX1_WDT_BASE_ADDR)); imx_iomuxv1_init(MX1_IO_ADDRESS(MX1_GPIO_BASE_ADDR), MX1_NUM_GPIO_PORT); } @@ -51,6 +50,7 @@ void __init imx1_soc_init(void) { + mxc_arch_reset_init(MX1_IO_ADDRESS(MX1_WDT_BASE_ADDR)); mxc_device_init(); mxc_register_gpio("imx1-gpio", 0, MX1_GPIO1_BASE_ADDR, SZ_256, diff -Nur linux-3.10.30/arch/arm/mach-imx/mm-imx21.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx21.c --- linux-3.10.30/arch/arm/mach-imx/mm-imx21.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx21.c 2014-03-08 20:32:54.000000000 +0100 @@ -66,7 +66,6 @@ void __init imx21_init_early(void) { mxc_set_cpu_type(MXC_CPU_MX21); - mxc_arch_reset_init(MX21_IO_ADDRESS(MX21_WDOG_BASE_ADDR)); imx_iomuxv1_init(MX21_IO_ADDRESS(MX21_GPIO_BASE_ADDR), MX21_NUM_GPIO_PORT); } @@ -82,6 +81,7 @@ void __init imx21_soc_init(void) { + mxc_arch_reset_init(MX21_IO_ADDRESS(MX21_WDOG_BASE_ADDR)); mxc_device_init(); mxc_register_gpio("imx21-gpio", 0, MX21_GPIO1_BASE_ADDR, SZ_256, MX21_INT_GPIO, 0); diff -Nur linux-3.10.30/arch/arm/mach-imx/mm-imx25.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx25.c --- linux-3.10.30/arch/arm/mach-imx/mm-imx25.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx25.c 2014-03-08 20:32:54.000000000 +0100 @@ -54,7 +54,6 @@ { mxc_set_cpu_type(MXC_CPU_MX25); mxc_iomux_v3_init(MX25_IO_ADDRESS(MX25_IOMUXC_BASE_ADDR)); - mxc_arch_reset_init(MX25_IO_ADDRESS(MX25_WDOG_BASE_ADDR)); } void __init mx25_init_irq(void) @@ -89,6 +88,7 @@ void __init imx25_soc_init(void) { + mxc_arch_reset_init(MX25_IO_ADDRESS(MX25_WDOG_BASE_ADDR)); mxc_device_init(); /* i.mx25 has the i.mx35 type gpio */ diff -Nur linux-3.10.30/arch/arm/mach-imx/mm-imx27.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx27.c --- linux-3.10.30/arch/arm/mach-imx/mm-imx27.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx27.c 2014-03-08 20:32:54.000000000 +0100 @@ -66,7 +66,6 @@ void __init imx27_init_early(void) { mxc_set_cpu_type(MXC_CPU_MX27); - mxc_arch_reset_init(MX27_IO_ADDRESS(MX27_WDOG_BASE_ADDR)); imx_iomuxv1_init(MX27_IO_ADDRESS(MX27_GPIO_BASE_ADDR), MX27_NUM_GPIO_PORT); } @@ -82,6 +81,7 @@ void __init imx27_soc_init(void) { + mxc_arch_reset_init(MX27_IO_ADDRESS(MX27_WDOG_BASE_ADDR)); mxc_device_init(); /* i.mx27 has the i.mx21 type gpio */ diff -Nur linux-3.10.30/arch/arm/mach-imx/mm-imx3.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx3.c --- linux-3.10.30/arch/arm/mach-imx/mm-imx3.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx3.c 2014-03-08 20:32:54.000000000 +0100 @@ -138,7 +138,6 @@ void __init imx31_init_early(void) { mxc_set_cpu_type(MXC_CPU_MX31); - mxc_arch_reset_init(MX31_IO_ADDRESS(MX31_WDOG_BASE_ADDR)); arch_ioremap_caller = imx3_ioremap_caller; arm_pm_idle = imx3_idle; mx3_ccm_base = MX31_IO_ADDRESS(MX31_CCM_BASE_ADDR); @@ -174,6 +173,7 @@ imx3_init_l2x0(); + mxc_arch_reset_init(MX31_IO_ADDRESS(MX31_WDOG_BASE_ADDR)); mxc_device_init(); mxc_register_gpio("imx31-gpio", 0, MX31_GPIO1_BASE_ADDR, SZ_16K, MX31_INT_GPIO1, 0); @@ -216,7 +216,6 @@ { mxc_set_cpu_type(MXC_CPU_MX35); mxc_iomux_v3_init(MX35_IO_ADDRESS(MX35_IOMUXC_BASE_ADDR)); - mxc_arch_reset_init(MX35_IO_ADDRESS(MX35_WDOG_BASE_ADDR)); arm_pm_idle = imx3_idle; arch_ioremap_caller = imx3_ioremap_caller; mx3_ccm_base = MX35_IO_ADDRESS(MX35_CCM_BASE_ADDR); @@ -272,6 +271,7 @@ imx3_init_l2x0(); + mxc_arch_reset_init(MX35_IO_ADDRESS(MX35_WDOG_BASE_ADDR)); mxc_device_init(); mxc_register_gpio("imx35-gpio", 0, MX35_GPIO1_BASE_ADDR, SZ_16K, MX35_INT_GPIO1, 0); diff -Nur linux-3.10.30/arch/arm/mach-imx/mm-imx5.c linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx5.c --- linux-3.10.30/arch/arm/mach-imx/mm-imx5.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mm-imx5.c 2014-03-08 20:32:54.000000000 +0100 @@ -83,7 +83,6 @@ imx51_ipu_mipi_setup(); mxc_set_cpu_type(MXC_CPU_MX51); mxc_iomux_v3_init(MX51_IO_ADDRESS(MX51_IOMUXC_BASE_ADDR)); - mxc_arch_reset_init(MX51_IO_ADDRESS(MX51_WDOG1_BASE_ADDR)); imx_src_init(); } @@ -91,7 +90,6 @@ { mxc_set_cpu_type(MXC_CPU_MX53); mxc_iomux_v3_init(MX53_IO_ADDRESS(MX53_IOMUXC_BASE_ADDR)); - mxc_arch_reset_init(MX53_IO_ADDRESS(MX53_WDOG1_BASE_ADDR)); imx_src_init(); } @@ -129,6 +127,7 @@ void __init imx51_soc_init(void) { + mxc_arch_reset_init(MX51_IO_ADDRESS(MX51_WDOG1_BASE_ADDR)); mxc_device_init(); /* i.mx51 has the i.mx35 type gpio */ diff -Nur linux-3.10.30/arch/arm/mach-imx/mx6.h linux-3.10.30-cubox-i/arch/arm/mach-imx/mx6.h --- linux-3.10.30/arch/arm/mach-imx/mx6.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mx6.h 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,35 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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 __ASM_ARCH_MXC_IOMAP_H__ +#define __ASM_ARCH_MXC_IOMAP_H__ + +#define MX6Q_IO_P2V(x) IMX_IO_P2V(x) +#define MX6Q_IO_ADDRESS(x) IOMEM(MX6Q_IO_P2V(x)) + +#define MX6Q_L2_BASE_ADDR 0x00a02000 +#define MX6Q_L2_SIZE 0x1000 +#define MX6Q_IOMUXC_BASE_ADDR 0x020e0000 +#define MX6Q_IOMUXC_SIZE 0x4000 +#define MX6Q_SRC_BASE_ADDR 0x020d8000 +#define MX6Q_SRC_SIZE 0x4000 +#define MX6Q_CCM_BASE_ADDR 0x020c4000 +#define MX6Q_CCM_SIZE 0x4000 +#define MX6Q_ANATOP_BASE_ADDR 0x020c8000 +#define MX6Q_ANATOP_SIZE 0x1000 +#define MX6Q_GPC_BASE_ADDR 0x020dc000 +#define MX6Q_GPC_SIZE 0x4000 +#define MX6Q_MMDC_P0_BASE_ADDR 0x021b0000 +#define MX6Q_MMDC_P0_SIZE 0x4000 +#define MX6Q_MMDC_P1_BASE_ADDR 0x021b4000 +#define MX6Q_MMDC_P1_SIZE 0x4000 + +#define MX6_SUSPEND_IRAM_SIZE 0x1000 +#endif diff -Nur linux-3.10.30/arch/arm/mach-imx/mxc.h linux-3.10.30-cubox-i/arch/arm/mach-imx/mxc.h --- linux-3.10.30/arch/arm/mach-imx/mxc.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/mxc.h 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007, 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2007, 2010, 2013 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) * * This program is free software; you can redistribute it and/or @@ -34,6 +34,7 @@ #define MXC_CPU_MX35 35 #define MXC_CPU_MX51 51 #define MXC_CPU_MX53 53 +#define MXC_CPU_IMX6SL 0x60 #define MXC_CPU_IMX6DL 0x61 #define MXC_CPU_IMX6Q 0x63 @@ -41,6 +42,8 @@ #define IMX_CHIP_REVISION_1_1 0x11 #define IMX_CHIP_REVISION_1_2 0x12 #define IMX_CHIP_REVISION_1_3 0x13 +#define IMX_CHIP_REVISION_1_4 0x14 +#define IMX_CHIP_REVISION_1_5 0x15 #define IMX_CHIP_REVISION_2_0 0x20 #define IMX_CHIP_REVISION_2_1 0x21 #define IMX_CHIP_REVISION_2_2 0x22 @@ -152,6 +155,11 @@ #endif #ifndef __ASSEMBLY__ +static inline bool cpu_is_imx6sl(void) +{ + return __mxc_cpu_type == MXC_CPU_IMX6SL; +} + static inline bool cpu_is_imx6dl(void) { return __mxc_cpu_type == MXC_CPU_IMX6DL; @@ -171,6 +179,7 @@ extern struct cpu_op *(*get_cpu_op)(int *op); #endif +#define cpu_is_imx6() (cpu_is_imx6q() || cpu_is_imx6dl() || cpu_is_imx6sl()) #define cpu_is_mx3() (cpu_is_mx31() || cpu_is_mx35()) #define cpu_is_mx2() (cpu_is_mx21() || cpu_is_mx27()) diff -Nur linux-3.10.30/arch/arm/mach-imx/pm-imx6.c linux-3.10.30-cubox-i/arch/arm/mach-imx/pm-imx6.c --- linux-3.10.30/arch/arm/mach-imx/pm-imx6.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/pm-imx6.c 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,379 @@ +/* + * Copyright 2011-2013 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "hardware.h" + +#define CCR 0x0 +#define BM_CCR_WB_COUNT (0x7 << 16) +#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21) +#define BM_CCR_RBC_EN (0x1 << 27) + +#define CLPCR 0x54 +#define BP_CLPCR_LPM 0 +#define BM_CLPCR_LPM (0x3 << 0) +#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2) +#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5) +#define BM_CLPCR_SBYOS (0x1 << 6) +#define BM_CLPCR_DIS_REF_OSC (0x1 << 7) +#define BM_CLPCR_VSTBY (0x1 << 8) +#define BP_CLPCR_STBY_COUNT 9 +#define BM_CLPCR_STBY_COUNT (0x3 << 9) +#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11) +#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16) +#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17) +#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19) +#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21) +#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22) +#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23) +#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24) +#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25) +#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26) +#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27) + +#define CGPR 0x64 +#define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17) + +#define MX6_INT_IOMUXC 32 + +static struct gen_pool *iram_pool; +static void *suspend_iram_base; +static unsigned long iram_size, iram_paddr; +static int (*suspend_in_iram_fn)(void *iram_vbase, + unsigned long iram_pbase, unsigned int cpu_type); +static unsigned int cpu_type; +static void __iomem *ccm_base; + +void imx6_set_cache_lpm_in_wait(bool enable) +{ + if ((cpu_is_imx6q() && imx_get_soc_revision() > + IMX_CHIP_REVISION_1_1) || + (cpu_is_imx6dl() && imx_get_soc_revision() > + IMX_CHIP_REVISION_1_0)) { + u32 val; + + val = readl_relaxed(ccm_base + CGPR); + if (enable) + val |= BM_CGPR_INT_MEM_CLK_LPM; + else + val &= ~BM_CGPR_INT_MEM_CLK_LPM; + writel_relaxed(val, ccm_base + CGPR); + } +} + +static void imx6_enable_rbc(bool enable) +{ + u32 val; + + /* + * need to mask all interrupts in GPC before + * operating RBC configurations + */ + imx_gpc_mask_all(); + + /* configure RBC enable bit */ + val = readl_relaxed(ccm_base + CCR); + val &= ~BM_CCR_RBC_EN; + val |= enable ? BM_CCR_RBC_EN : 0; + writel_relaxed(val, ccm_base + CCR); + + /* configure RBC count */ + val = readl_relaxed(ccm_base + CCR); + val &= ~BM_CCR_RBC_BYPASS_COUNT; + val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0; + writel(val, ccm_base + CCR); + + /* + * need to delay at least 2 cycles of CKIL(32K) + * due to hardware design requirement, which is + * ~61us, here we use 65us for safe + */ + udelay(65); + + /* restore GPC interrupt mask settings */ + imx_gpc_restore_all(); +} + +static void imx6_enable_wb(bool enable) +{ + u32 val; + + /* configure well bias enable bit */ + val = readl_relaxed(ccm_base + CLPCR); + val &= ~BM_CLPCR_WB_PER_AT_LPM; + val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0; + writel_relaxed(val, ccm_base + CLPCR); + + /* configure well bias count */ + val = readl_relaxed(ccm_base + CCR); + val &= ~BM_CCR_WB_COUNT; + val |= enable ? BM_CCR_WB_COUNT : 0; + writel_relaxed(val, ccm_base + CCR); +} + +int imx6_set_lpm(enum mxc_cpu_pwr_mode mode) +{ + u32 val = readl_relaxed(ccm_base + CLPCR); + struct irq_desc *desc = irq_to_desc(MX6_INT_IOMUXC); + + /* + * CCM state machine has restriction, before enabling + * LPM mode, need to make sure last LPM mode is waked up + * by dsm_wakeup_signal, which means the wakeup source + * must be seen by GPC, then CCM will clean its state machine + * and re-sample necessary signal to decide whether it can + * enter LPM mode. We force irq #32 to be always pending, + * unmask it before we enable LPM mode and mask it after LPM + * is enabled, this flow will make sure CCM state machine in + * reliable status before entering LPM mode. Otherwise, CCM + * may enter LPM mode by mistake which will cause system bus + * locked by CPU access not finished, as when CCM enter + * LPM mode, CPU will stop running. + */ + imx_gpc_irq_unmask(&desc->irq_data); + + val &= ~BM_CLPCR_LPM; + switch (mode) { + case WAIT_CLOCKED: + break; + case WAIT_UNCLOCKED: + val |= 0x1 << BP_CLPCR_LPM; + val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; + val &= ~BM_CLPCR_VSTBY; + val &= ~BM_CLPCR_SBYOS; + if (cpu_is_imx6sl()) + val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; + else + val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; + break; + case STOP_POWER_ON: + val |= 0x2 << BP_CLPCR_LPM; + val &= ~BM_CLPCR_VSTBY; + val &= ~BM_CLPCR_SBYOS; + if (cpu_is_imx6sl()) { + val |= BM_CLPCR_BYPASS_PMIC_READY; + val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; + } else { + val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; + } + break; + case WAIT_UNCLOCKED_POWER_OFF: + val |= 0x1 << BP_CLPCR_LPM; + val &= ~BM_CLPCR_VSTBY; + val &= ~BM_CLPCR_SBYOS; + break; + case STOP_POWER_OFF: + val |= 0x2 << BP_CLPCR_LPM; + val |= 0x3 << BP_CLPCR_STBY_COUNT; + val |= BM_CLPCR_VSTBY; + val |= BM_CLPCR_SBYOS; + if (cpu_is_imx6sl()) { + val |= BM_CLPCR_BYPASS_PMIC_READY; + val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; + } else { + val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; + } + break; + default: + imx_gpc_irq_mask(&desc->irq_data); + return -EINVAL; + } + + writel_relaxed(val, ccm_base + CLPCR); + imx_gpc_irq_mask(&desc->irq_data); + + return 0; +} + +static int imx6_suspend_finish(unsigned long val) +{ + /* + * call low level suspend function in iram, + * as we need to float DDR IO. + */ + local_flush_tlb_all(); + suspend_in_iram_fn(suspend_iram_base, iram_paddr, cpu_type); + return 0; +} + +static int imx6_pm_enter(suspend_state_t state) +{ + struct regmap *g; + + /* + * L2 can exit by 'reset' or Inband beacon (from remote EP) + * toggling phy_powerdown has same effect as 'inband beacon' + * So, toggle bit18 of GPR1, used as a workaround of errata + * "PCIe PCIe does not support L2 Power Down" + */ + if (IS_ENABLED(CONFIG_PCI_IMX6)) { + g = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(g)) { + pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n"); + return PTR_ERR(g); + } + regmap_update_bits(g, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD, + IMX6Q_GPR1_PCIE_TEST_PD); + } + + switch (state) { + case PM_SUSPEND_STANDBY: + imx6_set_lpm(STOP_POWER_ON); + imx6_set_cache_lpm_in_wait(true); + imx_gpc_pre_suspend(false); + if (cpu_is_imx6sl()) + imx6sl_set_wait_clk(true); + /* Zzz ... */ + cpu_do_idle(); + if (cpu_is_imx6sl()) + imx6sl_set_wait_clk(false); + imx_gpc_post_resume(); + imx6_set_lpm(WAIT_CLOCKED); + break; + case PM_SUSPEND_MEM: + imx6_enable_wb(true); + imx6_set_cache_lpm_in_wait(false); + imx6_set_lpm(STOP_POWER_OFF); + imx_gpc_pre_suspend(true); + imx_anatop_pre_suspend(); + imx_set_cpu_jump(0, v7_cpu_resume); + /* Zzz ... */ + cpu_suspend(0, imx6_suspend_finish); + if (!cpu_is_imx6sl()) + imx_smp_prepare(); + imx_anatop_post_resume(); + imx_gpc_post_resume(); + imx6_enable_rbc(false); + imx6_enable_wb(false); + imx6_set_cache_lpm_in_wait(true); + imx6_set_lpm(WAIT_CLOCKED); + break; + default: + return -EINVAL; + } + + /* + * L2 can exit by 'reset' or Inband beacon (from remote EP) + * toggling phy_powerdown has same effect as 'inband beacon' + * So, toggle bit18 of GPR1, used as a workaround of errata + * "PCIe PCIe does not support L2 Power Down" + */ + if (IS_ENABLED(CONFIG_PCI_IMX6)) { + regmap_update_bits(g, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD, + !IMX6Q_GPR1_PCIE_TEST_PD); + } + + return 0; +} + +static struct map_desc imx6_pm_io_desc[] __initdata = { + imx_map_entry(MX6Q, MMDC_P0, MT_DEVICE), + imx_map_entry(MX6Q, MMDC_P1, MT_DEVICE), + imx_map_entry(MX6Q, SRC, MT_DEVICE), + imx_map_entry(MX6Q, IOMUXC, MT_DEVICE), + imx_map_entry(MX6Q, CCM, MT_DEVICE), + imx_map_entry(MX6Q, ANATOP, MT_DEVICE), + imx_map_entry(MX6Q, GPC, MT_DEVICE), + imx_map_entry(MX6Q, L2, MT_DEVICE), +}; + +void __init imx6_pm_map_io(void) +{ + iotable_init(imx6_pm_io_desc, ARRAY_SIZE(imx6_pm_io_desc)); +} + +static int imx6_pm_valid(suspend_state_t state) +{ + return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM); +} + +static const struct platform_suspend_ops imx6_pm_ops = { + .enter = imx6_pm_enter, + .valid = imx6_pm_valid, +}; + +void imx6_pm_set_ccm_base(void __iomem *base) +{ + if (!base) + pr_warn("ccm base is NULL!\n"); + ccm_base = base; +} + +void __init imx6_pm_init(void) +{ + struct device_node *node; + unsigned long iram_base; + struct platform_device *pdev; + + node = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!node) { + pr_err("failed to find ocram node!\n"); + return; + } + + pdev = of_find_device_by_node(node); + if (!pdev) { + pr_err("failed to find ocram device!\n"); + return; + } + + iram_pool = dev_get_gen_pool(&pdev->dev); + if (!iram_pool) { + pr_err("iram pool unavailable!\n"); + return; + } + + iram_size = MX6_SUSPEND_IRAM_SIZE; + + iram_base = gen_pool_alloc(iram_pool, iram_size); + if (!iram_base) { + pr_err("unable to alloc iram!\n"); + return; + } + + iram_paddr = gen_pool_virt_to_phys(iram_pool, iram_base); + + suspend_iram_base = __arm_ioremap(iram_paddr, iram_size, + MT_MEMORY_NONCACHED); + + suspend_in_iram_fn = (void *)fncpy(suspend_iram_base, + &imx6_suspend, iram_size); + + suspend_set_ops(&imx6_pm_ops); + + /* Set cpu_type for DSM */ + if (cpu_is_imx6q()) + cpu_type = MXC_CPU_IMX6Q; + else if (cpu_is_imx6dl()) + cpu_type = MXC_CPU_IMX6DL; + else + cpu_type = MXC_CPU_IMX6SL; +} diff -Nur linux-3.10.30/arch/arm/mach-imx/pm-imx6q.c linux-3.10.30-cubox-i/arch/arm/mach-imx/pm-imx6q.c --- linux-3.10.30/arch/arm/mach-imx/pm-imx6q.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/pm-imx6q.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,61 +0,0 @@ -/* - * Copyright 2011-2013 Freescale Semiconductor, Inc. - * Copyright 2011 Linaro Ltd. - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "hardware.h" - -static int imx6q_suspend_finish(unsigned long val) -{ - cpu_do_idle(); - return 0; -} - -static int imx6q_pm_enter(suspend_state_t state) -{ - switch (state) { - case PM_SUSPEND_MEM: - imx6q_set_lpm(STOP_POWER_OFF); - imx_gpc_pre_suspend(); - imx_anatop_pre_suspend(); - imx_set_cpu_jump(0, v7_cpu_resume); - /* Zzz ... */ - cpu_suspend(0, imx6q_suspend_finish); - imx_smp_prepare(); - imx_anatop_post_resume(); - imx_gpc_post_resume(); - imx6q_set_lpm(WAIT_CLOCKED); - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct platform_suspend_ops imx6q_pm_ops = { - .enter = imx6q_pm_enter, - .valid = suspend_valid_only_mem, -}; - -void __init imx6q_pm_init(void) -{ - suspend_set_ops(&imx6q_pm_ops); -} diff -Nur linux-3.10.30/arch/arm/mach-imx/src.c linux-3.10.30-cubox-i/arch/arm/mach-imx/src.c --- linux-3.10.30/arch/arm/mach-imx/src.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/src.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2011, 2013 Freescale Semiconductor, Inc. * Copyright 2011 Linaro Ltd. * * The code contained herein is licensed under the GNU General Public @@ -91,6 +91,7 @@ spin_lock(&scr_lock); val = readl_relaxed(src_base + SRC_SCR); val = enable ? val | mask : val & ~mask; + val |= 1 << (BP_SRC_SCR_CORE1_RST + cpu - 1); writel_relaxed(val, src_base + SRC_SCR); spin_unlock(&scr_lock); } @@ -114,21 +115,6 @@ writel_relaxed(arg, src_base + SRC_GPR1 + cpu * 8 + 4); } -void imx_src_prepare_restart(void) -{ - u32 val; - - /* clear enable bits of secondary cores */ - spin_lock(&scr_lock); - val = readl_relaxed(src_base + SRC_SCR); - val &= ~(0x7 << BP_SRC_SCR_CORE1_ENABLE); - writel_relaxed(val, src_base + SRC_SCR); - spin_unlock(&scr_lock); - - /* clear persistent entry register of primary core */ - writel_relaxed(0, src_base + SRC_GPR1); -} - void __init imx_src_init(void) { struct device_node *np; diff -Nur linux-3.10.30/arch/arm/mach-imx/suspend-imx6.S linux-3.10.30-cubox-i/arch/arm/mach-imx/suspend-imx6.S --- linux-3.10.30/arch/arm/mach-imx/suspend-imx6.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/suspend-imx6.S 2014-03-08 20:32:54.000000000 +0100 @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include "hardware.h" + +#define MX6Q_SRC_GPR1 0x20 +#define MX6Q_SRC_GPR2 0x24 +#define MX6Q_MMDC_MAPSR 0x404 +#define MX6Q_MMDC_MPDGCTRL0 0x83c +#define MX6Q_GPC_IMR1 0x08 +#define MX6Q_GPC_IMR2 0x0c +#define MX6Q_GPC_IMR3 0x10 +#define MX6Q_GPC_IMR4 0x14 +#define MX6Q_CCM_CCR 0x0 +#define MX6Q_ANATOP_CORE 0x140 + + .align 3 + + .macro imx6sl_ddr_io_save + + ldr r4, [r8, #0x30c] /* DRAM_DQM0 */ + ldr r5, [r8, #0x310] /* DRAM_DQM1 */ + ldr r6, [r8, #0x314] /* DRAM_DQM2 */ + ldr r7, [r8, #0x318] /* DRAM_DQM3 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x5c4] /* GPR_B0DS */ + ldr r5, [r8, #0x5cc] /* GPR_B1DS */ + ldr r6, [r8, #0x5d4] /* GPR_B2DS */ + ldr r7, [r8, #0x5d8] /* GPR_B3DS */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x300] /* DRAM_CAS */ + ldr r5, [r8, #0x31c] /* DRAM_RAS */ + ldr r6, [r8, #0x338] /* DRAM_SDCLK_0 */ + ldr r7, [r8, #0x5ac] /* GPR_ADDS*/ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x5b0] /* DDRMODE_CTL */ + ldr r5, [r8, #0x5c0] /* DDRMODE */ + ldr r6, [r8, #0x33c] /* DRAM_SODT0*/ + ldr r7, [r8, #0x340] /* DRAM_SODT1*/ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x330] /* DRAM_SDCKE0 */ + ldr r5, [r8, #0x334] /* DRAM_SDCKE1 */ + ldr r6, [r8, #0x320] /* DRAM_RESET */ + stmfd r10!, {r4-r6} + + .endm + + .macro imx6sl_ddr_io_restore + + ldmea r10!, {r4-r7} + str r4, [r8, #0x30c] /* DRAM_DQM0 */ + str r5, [r8, #0x310] /* DRAM_DQM1 */ + str r6, [r8, #0x314] /* DRAM_DQM2 */ + str r7, [r8, #0x318] /* DRAM_DQM3 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x5c4] /* GPR_B0DS */ + str r5, [r8, #0x5cc] /* GPR_B1DS */ + str r6, [r8, #0x5d4] /* GPR_B2DS */ + str r7, [r8, #0x5d8] /* GPR_B3DS */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x300] /* DRAM_CAS */ + str r5, [r8, #0x31c] /* DRAM_RAS */ + str r6, [r8, #0x338] /* DRAM_SDCLK_0 */ + str r7, [r8, #0x5ac] /* GPR_ADDS*/ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x5b0] /* DDRMODE_CTL */ + str r5, [r8, #0x5c0] /* DDRMODE */ + str r6, [r8, #0x33c] /* DRAM_SODT0*/ + str r7, [r8, #0x340] /* DRAM_SODT1*/ + + ldmea r10!, {r4-r6} + str r4, [r8, #0x330] /* DRAM_SDCKE0 */ + str r5, [r8, #0x334] /* DRAM_SDCKE1 */ + str r6, [r8, #0x320] /* DRAM_RESET */ + + .endm + + .macro imx6sl_ddr_io_set_lpm + + mov r10, #0 + str r10, [r8, #0x30c] /* DRAM_DQM0 */ + str r10, [r8, #0x310] /* DRAM_DQM1 */ + str r10, [r8, #0x314] /* DRAM_DQM2 */ + str r10, [r8, #0x318] /* DRAM_DQM3 */ + + str r10, [r8, #0x5c4] /* GPR_B0DS */ + str r10, [r8, #0x5cc] /* GPR_B1DS */ + str r10, [r8, #0x5d4] /* GPR_B2DS */ + str r10, [r8, #0x5d8] /* GPR_B3DS */ + + str r10, [r8, #0x300] /* DRAM_CAS */ + str r10, [r8, #0x31c] /* DRAM_RAS */ + str r10, [r8, #0x338] /* DRAM_SDCLK_0 */ + str r10, [r8, #0x5ac] /* GPR_ADDS*/ + + str r10, [r8, #0x5b0] /* DDRMODE_CTL */ + str r10, [r8, #0x5c0] /* DDRMODE */ + str r10, [r8, #0x33c] /* DRAM_SODT0*/ + str r10, [r8, #0x340] /* DRAM_SODT1*/ + + mov r10, #0x80000 + str r10, [r8, #0x320] /* DRAM_RESET */ + mov r10, #0x1000 + str r10, [r8, #0x330] /* DRAM_SDCKE0 */ + str r10, [r8, #0x334] /* DRAM_SDCKE1 */ + + .endm + + .macro imx6dl_ddr_io_save + + ldr r4, [r8, #0x470] /* DRAM_DQM0 */ + ldr r5, [r8, #0x474] /* DRAM_DQM1 */ + ldr r6, [r8, #0x478] /* DRAM_DQM2 */ + ldr r7, [r8, #0x47c] /* DRAM_DQM3 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x480] /* DRAM_DQM4 */ + ldr r5, [r8, #0x484] /* DRAM_DQM5 */ + ldr r6, [r8, #0x488] /* DRAM_DQM6 */ + ldr r7, [r8, #0x48c] /* DRAM_DQM7 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x464] /* DRAM_CAS */ + ldr r5, [r8, #0x490] /* DRAM_RAS */ + ldr r6, [r8, #0x4ac] /* DRAM_SDCLK_0 */ + ldr r7, [r8, #0x4b0] /* DRAM_SDCLK_1 */ + stmfd r10!, {r4-r7} + + ldr r5, [r8, #0x750] /* DDRMODE_CTL */ + ldr r6, [r8, #0x760] /* DDRMODE */ + stmfd r10!, {r5-r6} + + ldr r4, [r8, #0x4bc] /* DRAM_SDQS0 */ + ldr r5, [r8, #0x4c0] /* DRAM_SDQS1 */ + ldr r6, [r8, #0x4c4] /* DRAM_SDQS2 */ + ldr r7, [r8, #0x4c8] /* DRAM_SDQS3 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x4cc] /* DRAM_SDQS4 */ + ldr r5, [r8, #0x4d0] /* DRAM_SDQS5 */ + ldr r6, [r8, #0x4d4] /* DRAM_SDQS6 */ + ldr r7, [r8, #0x4d8] /* DRAM_SDQS7 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x764] /* GPR_B0DS */ + ldr r5, [r8, #0x770] /* GPR_B1DS */ + ldr r6, [r8, #0x778] /* GPR_B2DS */ + ldr r7, [r8, #0x77c] /* GPR_B3DS */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x780] /* GPR_B4DS */ + ldr r5, [r8, #0x784] /* GPR_B5DS */ + ldr r6, [r8, #0x78c] /* GPR_B6DS */ + ldr r7, [r8, #0x748] /* GPR_B7DS */ + stmfd r10!, {r4-r7} + + ldr r5, [r8, #0x74c] /* GPR_ADDS*/ + ldr r6, [r8, #0x4b4] /* DRAM_SODT0*/ + ldr r7, [r8, #0x4b8] /* DRAM_SODT1*/ + stmfd r10!, {r5-r7} + + .endm + + .macro imx6dl_ddr_io_restore + + ldmea r10!, {r4-r7} + str r4, [r8, #0x470] /* DRAM_DQM0 */ + str r5, [r8, #0x474] /* DRAM_DQM1 */ + str r6, [r8, #0x478] /* DRAM_DQM2 */ + str r7, [r8, #0x47c] /* DRAM_DQM3 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x480] /* DRAM_DQM4 */ + str r5, [r8, #0x484] /* DRAM_DQM5 */ + str r6, [r8, #0x488] /* DRAM_DQM6 */ + str r7, [r8, #0x48c] /* DRAM_DQM7 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x464] /* DRAM_CAS */ + str r5, [r8, #0x490] /* DRAM_RAS */ + str r6, [r8, #0x4ac] /* DRAM_SDCLK_0 */ + str r7, [r8, #0x4b0] /* DRAM_SDCLK_1 */ + + ldmea r10!, {r5-r6} + str r5, [r8, #0x750] /* DDRMODE_CTL */ + str r6, [r8, #0x760] /* DDRMODE */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x4bc] /* DRAM_SDQS0 */ + str r5, [r8, #0x4c0] /* DRAM_SDQS1 */ + str r6, [r8, #0x4c4] /* DRAM_SDQS2 */ + str r7, [r8, #0x4c8] /* DRAM_SDQS3 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x4cc] /* DRAM_SDQS4 */ + str r5, [r8, #0x4d0] /* DRAM_SDQS5 */ + str r6, [r8, #0x4d4] /* DRAM_SDQS6 */ + str r7, [r8, #0x4d8] /* DRAM_SDQS7 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x764] /* GPR_B0DS */ + str r5, [r8, #0x770] /* GPR_B1DS */ + str r6, [r8, #0x778] /* GPR_B2DS */ + str r7, [r8, #0x77c] /* GPR_B3DS */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x780] /* GPR_B4DS */ + str r5, [r8, #0x784] /* GPR_B5DS */ + str r6, [r8, #0x78c] /* GPR_B6DS */ + str r7, [r8, #0x748] /* GPR_B7DS */ + + ldmea r10!, {r5-r7} + str r5, [r8, #0x74c] /* GPR_ADDS*/ + str r6, [r8, #0x4b4] /* DRAM_SODT0*/ + str r7, [r8, #0x4b8] /* DRAM_SODT1*/ + + .endm + + .macro imx6dl_ddr_io_set_lpm + + mov r10, #0 + str r10, [r8, #0x470] /* DRAM_DQM0 */ + str r10, [r8, #0x474] /* DRAM_DQM1 */ + str r10, [r8, #0x478] /* DRAM_DQM2 */ + str r10, [r8, #0x47c] /* DRAM_DQM3 */ + + str r10, [r8, #0x480] /* DRAM_DQM4 */ + str r10, [r8, #0x484] /* DRAM_DQM5 */ + str r10, [r8, #0x488] /* DRAM_DQM6 */ + str r10, [r8, #0x48c] /* DRAM_DQM7 */ + + str r10, [r8, #0x464] /* DRAM_CAS */ + str r10, [r8, #0x490] /* DRAM_RAS */ + str r10, [r8, #0x4ac] /* DRAM_SDCLK_0 */ + str r10, [r8, #0x4b0] /* DRAM_SDCLK_1 */ + + str r10, [r8, #0x750] /* DDRMODE_CTL */ + str r10, [r8, #0x760] /* DDRMODE */ + + str r10, [r8, #0x4bc] /* DRAM_SDQS0 */ + str r10, [r8, #0x4c0] /* DRAM_SDQS1 */ + str r10, [r8, #0x4c4] /* DRAM_SDQS2 */ + str r10, [r8, #0x4c8] /* DRAM_SDQS3 */ + + str r10, [r8, #0x4cc] /* DRAM_SDQS4 */ + str r10, [r8, #0x4d0] /* DRAM_SDQS5 */ + str r10, [r8, #0x4d4] /* DRAM_SDQS6 */ + str r10, [r8, #0x4d8] /* DRAM_SDQS7 */ + + str r10, [r8, #0x764] /* GPR_B0DS */ + str r10, [r8, #0x770] /* GPR_B1DS */ + str r10, [r8, #0x778] /* GPR_B2DS */ + str r10, [r8, #0x77c] /* GPR_B3DS */ + + str r10, [r8, #0x780] /* GPR_B4DS */ + str r10, [r8, #0x784] /* GPR_B5DS */ + str r10, [r8, #0x78c] /* GPR_B6DS */ + str r10, [r8, #0x748] /* GPR_B7DS */ + + str r10, [r8, #0x74c] /* GPR_ADDS*/ + str r10, [r8, #0x4b4] /* DRAM_SODT0*/ + str r10, [r8, #0x4b8] /* DRAM_SODT1*/ + + .endm + + .macro imx6dq_ddr_io_save + + ldr r4, [r8, #0x5ac] /* DRAM_DQM0 */ + ldr r5, [r8, #0x5b4] /* DRAM_DQM1 */ + ldr r6, [r8, #0x528] /* DRAM_DQM2 */ + ldr r7, [r8, #0x520] /* DRAM_DQM3 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x514] /* DRAM_DQM4 */ + ldr r5, [r8, #0x510] /* DRAM_DQM5 */ + ldr r6, [r8, #0x5bc] /* DRAM_DQM6 */ + ldr r7, [r8, #0x5c4] /* DRAM_DQM7 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x56c] /* DRAM_CAS */ + ldr r5, [r8, #0x578] /* DRAM_RAS */ + ldr r6, [r8, #0x588] /* DRAM_SDCLK_0 */ + ldr r7, [r8, #0x594] /* DRAM_SDCLK_1 */ + stmfd r10!, {r4-r7} + + ldr r5, [r8, #0x750] /* DDRMODE_CTL */ + ldr r6, [r8, #0x774] /* DDRMODE */ + stmfd r10!, {r5-r6} + + ldr r4, [r8, #0x5a8] /* DRAM_SDQS0 */ + ldr r5, [r8, #0x5b0] /* DRAM_SDQS1 */ + ldr r6, [r8, #0x524] /* DRAM_SDQS2 */ + ldr r7, [r8, #0x51c] /* DRAM_SDQS3 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x518] /* DRAM_SDQS4 */ + ldr r5, [r8, #0x50c] /* DRAM_SDQS5 */ + ldr r6, [r8, #0x5b8] /* DRAM_SDQS6 */ + ldr r7, [r8, #0x5c0] /* DRAM_SDQS7 */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x784] /* GPR_B0DS */ + ldr r5, [r8, #0x788] /* GPR_B1DS */ + ldr r6, [r8, #0x794] /* GPR_B2DS */ + ldr r7, [r8, #0x79c] /* GPR_B3DS */ + stmfd r10!, {r4-r7} + + ldr r4, [r8, #0x7a0] /* GPR_B4DS */ + ldr r5, [r8, #0x7a4] /* GPR_B5DS */ + ldr r6, [r8, #0x7a8] /* GPR_B6DS */ + ldr r7, [r8, #0x748] /* GPR_B7DS */ + stmfd r10!, {r4-r7} + + ldr r5, [r8, #0x74c] /* GPR_ADDS*/ + ldr r6, [r8, #0x59c] /* DRAM_SODT0*/ + ldr r7, [r8, #0x5a0] /* DRAM_SODT1*/ + stmfd r10!, {r5-r7} + + .endm + + .macro imx6dq_ddr_io_restore + + ldmea r10!, {r4-r7} + str r4, [r8, #0x5ac] /* DRAM_DQM0 */ + str r5, [r8, #0x5b4] /* DRAM_DQM1 */ + str r6, [r8, #0x528] /* DRAM_DQM2 */ + str r7, [r8, #0x520] /* DRAM_DQM3 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x514] /* DRAM_DQM4 */ + str r5, [r8, #0x510] /* DRAM_DQM5 */ + str r6, [r8, #0x5bc] /* DRAM_DQM6 */ + str r7, [r8, #0x5c4] /* DRAM_DQM7 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x56c] /* DRAM_CAS */ + str r5, [r8, #0x578] /* DRAM_RAS */ + str r6, [r8, #0x588] /* DRAM_SDCLK_0 */ + str r7, [r8, #0x594] /* DRAM_SDCLK_1 */ + + ldmea r10!, {r5-r6} + str r5, [r8, #0x750] /* DDRMODE_CTL */ + str r6, [r8, #0x774] /* DDRMODE */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x5a8] /* DRAM_SDQS0 */ + str r5, [r8, #0x5b0] /* DRAM_SDQS1 */ + str r6, [r8, #0x524] /* DRAM_SDQS2 */ + str r7, [r8, #0x51c] /* DRAM_SDQS3 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x518] /* DRAM_SDQS4 */ + str r5, [r8, #0x50c] /* DRAM_SDQS5 */ + str r6, [r8, #0x5b8] /* DRAM_SDQS6 */ + str r7, [r8, #0x5c0] /* DRAM_SDQS7 */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x784] /* GPR_B0DS */ + str r5, [r8, #0x788] /* GPR_B1DS */ + str r6, [r8, #0x794] /* GPR_B2DS */ + str r7, [r8, #0x79c] /* GPR_B3DS */ + + ldmea r10!, {r4-r7} + str r4, [r8, #0x7a0] /* GPR_B4DS */ + str r5, [r8, #0x7a4] /* GPR_B5DS */ + str r6, [r8, #0x7a8] /* GPR_B6DS */ + str r7, [r8, #0x748] /* GPR_B7DS */ + + ldmea r10!, {r5-r7} + str r5, [r8, #0x74c] /* GPR_ADDS*/ + str r6, [r8, #0x59c] /* DRAM_SODT0*/ + str r7, [r8, #0x5a0] /* DRAM_SODT1*/ + + .endm + + .macro imx6dq_ddr_io_set_lpm + + mov r10, #0 + str r10, [r8, #0x5ac] /* DRAM_DQM0 */ + str r10, [r8, #0x5b4] /* DRAM_DQM1 */ + str r10, [r8, #0x528] /* DRAM_DQM2 */ + str r10, [r8, #0x520] /* DRAM_DQM3 */ + + str r10, [r8, #0x514] /* DRAM_DQM4 */ + str r10, [r8, #0x510] /* DRAM_DQM5 */ + str r10, [r8, #0x5bc] /* DRAM_DQM6 */ + str r10, [r8, #0x5c4] /* DRAM_DQM7 */ + + str r10, [r8, #0x56c] /* DRAM_CAS */ + str r10, [r8, #0x578] /* DRAM_RAS */ + str r10, [r8, #0x588] /* DRAM_SDCLK_0 */ + str r10, [r8, #0x594] /* DRAM_SDCLK_1 */ + + str r10, [r8, #0x750] /* DDRMODE_CTL */ + str r10, [r8, #0x774] /* DDRMODE */ + + str r10, [r8, #0x5a8] /* DRAM_SDQS0 */ + str r10, [r8, #0x5b0] /* DRAM_SDQS1 */ + str r10, [r8, #0x524] /* DRAM_SDQS2 */ + str r10, [r8, #0x51c] /* DRAM_SDQS3 */ + + str r10, [r8, #0x518] /* DRAM_SDQS4 */ + str r10, [r8, #0x50c] /* DRAM_SDQS5 */ + str r10, [r8, #0x5b8] /* DRAM_SDQS6 */ + str r10, [r8, #0x5c0] /* DRAM_SDQS7 */ + + str r10, [r8, #0x784] /* GPR_B0DS */ + str r10, [r8, #0x788] /* GPR_B1DS */ + str r10, [r8, #0x794] /* GPR_B2DS */ + str r10, [r8, #0x79c] /* GPR_B3DS */ + + str r10, [r8, #0x7a0] /* GPR_B4DS */ + str r10, [r8, #0x7a4] /* GPR_B5DS */ + str r10, [r8, #0x7a8] /* GPR_B6DS */ + str r10, [r8, #0x748] /* GPR_B7DS */ + + str r10, [r8, #0x74c] /* GPR_ADDS*/ + str r10, [r8, #0x59c] /* DRAM_SODT0*/ + str r10, [r8, #0x5a0] /* DRAM_SODT1*/ + + .endm + + .macro sync_l2_cache + + /* sync L2 cache to drain L2's buffers to DRAM. */ +#ifdef CONFIG_CACHE_L2X0 + ldr r8, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR) + mov r5, #0x0 + str r5, [r8, #L2X0_CACHE_SYNC] +1: + ldr r5, [r8, #L2X0_CACHE_SYNC] + ands r5, r5, #0x1 + bne 1b +#endif + .endm + +ENTRY(imx6_suspend) + + /* + * counting the resume address in iram + * to set it in SRC register. + */ + ldr r4, =imx6_suspend + ldr r5, =resume + sub r5, r5, r4 + add r9, r1, r5 + + /* + * make sure TLB contain the addr we want, + * as we will access after DDR IO floated. + */ + + ldr r8, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR) + ldr r7, [r8, #MX6Q_ANATOP_CORE] + ldr r8, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR) + ldr r7, [r8, #0x0] + ldr r8, =IMX_IO_P2V(MX6Q_GPC_BASE_ADDR) + ldr r7, [r8, #0x0] + + /* use r8 to store the IO address */ + ldr r8, =IMX_IO_P2V(MX6Q_SRC_BASE_ADDR) + + /* + * read previous resume address from SRC + * register, which is v7_cpu_resume, this + * is for the jump when we finish DDR IO + * restore. + */ + ldr r5, [r8, #MX6Q_SRC_GPR1] + add r10, r0, #MX6_SUSPEND_IRAM_SIZE + stmfd r10!, {r5} + + /* save cpu type */ + stmfd r10!, {r2} + + str r9, [r8, #MX6Q_SRC_GPR1] + add r3, r1, #MX6_SUSPEND_IRAM_SIZE + str r3, [r8, #MX6Q_SRC_GPR2] + + ldr r8, =IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR) + + cmp r2, #MXC_CPU_IMX6Q + bne dl_io_dsm_save + imx6dq_ddr_io_save + b ddr_io_save_dsm_done +dl_io_dsm_save: + cmp r2, #MXC_CPU_IMX6DL + bne sl_io_save + imx6dl_ddr_io_save + b ddr_io_save_dsm_done +sl_io_save: + imx6sl_ddr_io_save +ddr_io_save_dsm_done: + + /* need to sync L2 cache before DSM. */ + sync_l2_cache + + ldr r8, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR) + /* + * put DDR explicitly into self-refresh and + * disable Automatic power savings. + */ + ldr r7, [r8, #MX6Q_MMDC_MAPSR] + orr r7, r7, #0x01 + str r7, [r8, #MX6Q_MMDC_MAPSR] + + /* make the DDR explicitly enter self-refresh. */ + ldr r7, [r8, #MX6Q_MMDC_MAPSR] + orr r7, r7, #(1 << 21) + str r7, [r8, #MX6Q_MMDC_MAPSR] + +poll_dvfs_set_1: + ldr r7, [r8, #0x404] + ands r7, r7, #(1 << 25) + beq poll_dvfs_set_1 + + ldr r8, =IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR) + + cmp r2, #MXC_CPU_IMX6Q + bne dl_io_dsm_set_lpm + imx6dq_ddr_io_set_lpm + b ddr_io_set_lpm_dsm_done +dl_io_dsm_set_lpm: + cmp r2, #MXC_CPU_IMX6DL + bne sl_io_dsm_set_lpm + imx6dl_ddr_io_set_lpm + b ddr_io_set_lpm_dsm_done +sl_io_dsm_set_lpm: + imx6sl_ddr_io_set_lpm +ddr_io_set_lpm_dsm_done: + + /* + * mask all GPC interrupts before + * enabling the RBC counters to + * avoid the counter starting too + * early if an interupt is already + * pending. + */ + ldr r8, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR) + /* save CCM base in r9 */ + mov r9, r8 + ldr r8, =IMX_IO_P2V(MX6Q_GPC_BASE_ADDR) + ldr r4, [r8, #MX6Q_GPC_IMR1] + ldr r5, [r8, #MX6Q_GPC_IMR2] + ldr r6, [r8, #MX6Q_GPC_IMR3] + ldr r7, [r8, #MX6Q_GPC_IMR4] + + ldr r3, =0xffffffff + str r3, [r8, #MX6Q_GPC_IMR1] + str r3, [r8, #MX6Q_GPC_IMR2] + str r3, [r8, #MX6Q_GPC_IMR3] + str r3, [r8, #MX6Q_GPC_IMR4] + + /* + * enable the RBC bypass counter here + * to hold off the interrupts. RBC counter + * = 32 (1ms), Minimum RBC delay should be + * 400us for the analog LDOs to power down. + */ + ldr r3, [r9, #MX6Q_CCM_CCR] + bic r3, r3, #(0x3f << 21) + orr r3, r3, #(0x20 << 21) + str r3, [r9, #MX6Q_CCM_CCR] + + /* enable the counter. */ + ldr r3, [r9, #MX6Q_CCM_CCR] + orr r3, r3, #(0x1 << 27) + str r3, [r9, #MX6Q_CCM_CCR] + + /* unmask all the GPC interrupts. */ + str r4, [r8, #MX6Q_GPC_IMR1] + str r5, [r8, #MX6Q_GPC_IMR2] + str r6, [r8, #MX6Q_GPC_IMR3] + str r7, [r8, #MX6Q_GPC_IMR4] + + /* + * now delay for a short while (3usec) + * ARM is at 1GHz at this point + * so a short loop should be enough. + * this delay is required to ensure that + * the RBC counter can start counting in + * case an interrupt is already pending + * or in case an interrupt arrives just + * as ARM is about to assert DSM_request. + */ + ldr r4, =2000 +rbc_loop: + sub r4, r4, #0x1 + cmp r4, #0x0 + bne rbc_loop + + /* + * if internal ldo(VDDARM) bypassed,analog bypass + * it for DSM(0x1e) and restore it when resume(0x1f). + */ + ldr r8, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR) + ldr r7, [r8, #MX6Q_ANATOP_CORE] + and r7, r7, #0x1f + cmp r7, #0x1f + bne ldo_check_done1 +ldo_analog_bypass: + ldr r7, [r8, #MX6Q_ANATOP_CORE] + bic r7, r7, #0x1f + orr r7, r7, #0x1e + str r7, [r8, #MX6Q_ANATOP_CORE] +ldo_check_done1: + + /* Zzz, enter stop mode */ + wfi + nop + nop + nop + nop + + /* + * run to here means there is pending + * wakeup source, system should auto + * resume, we need to restore DDR IO first + */ + + /* restore it with 0x1f if use ldo bypass mode.*/ + ldr r8, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR) + ldr r7, [r8, #MX6Q_ANATOP_CORE] + and r7, r7, #0x1f + cmp r7, #0x1e + bne ldo_check_done2 +ldo_bypass_restore: + ldr r7, [r8, #MX6Q_ANATOP_CORE] + orr r7, r7, #0x1f + str r7, [r8, #MX6Q_ANATOP_CORE] +ldo_check_done2: + + add r10, r0, #MX6_SUSPEND_IRAM_SIZE + /* skip the lr saved in iram */ + sub r10, r10, #0x4 + /* skip the cpu type saved in iram */ + sub r10, r10, #0x4 + + ldr r8, =IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR) + + cmp r2, #MXC_CPU_IMX6Q + bne dl_io_restore + imx6dq_ddr_io_restore + b ddr_io_restore_done +dl_io_restore: + cmp r2, #MXC_CPU_IMX6DL + bne sl_io_restore + imx6dl_ddr_io_restore + b ddr_io_restore_done +sl_io_restore: + imx6sl_ddr_io_restore + ldr r8, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR) + /* reset read FIFO, RST_RD_FIFO */ + ldr r7, =MX6Q_MMDC_MPDGCTRL0 + ldr r6, [r8, r7] + orr r6, r6, #(1 << 31) + str r6, [r8, r7] +fifo_reset1_wait: + ldr r6, [r8, r7] + and r6, r6, #(1 << 31) + cmp r6, #0 + bne fifo_reset1_wait + + /* reset FIFO a second time */ + ldr r6, [r8, r7] + orr r6, r6, #(1 << 31) + str r6, [r8, r7] +fifo_reset2_wait: + ldr r6, [r8, r7] + and r6, r6, #(1 << 31) + cmp r6, #0 + bne fifo_reset2_wait +ddr_io_restore_done: + + ldr r8, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR) + /* let DDR out of self-refresh. */ + ldr r7, [r8, #MX6Q_MMDC_MAPSR] + bic r7, r7, #(1 << 21) + str r7, [r8, #MX6Q_MMDC_MAPSR] + +poll_dvfs_clear_2: + ldr r7, [r8, #MX6Q_MMDC_MAPSR] + ands r7, r7, #(1 << 25) + bne poll_dvfs_clear_2 + /* enable DDR auto power saving */ + ldr r7, [r8, #MX6Q_MMDC_MAPSR] + bic r7, r7, #0x1 + str r7, [r8, #MX6Q_MMDC_MAPSR] + /* return to suspend finish */ + mov pc, lr + +resume: + /* invalidate L1 I-cache first */ + mov r1, #0x0 + mcr p15, 0, r1, c7, c5, 0 + mcr p15, 0, r1, c7, c5, 0 + mcr p15, 0, r1, c7, c5, 6 + /* enable the Icache and branch prediction */ + mov r1, #0x1800 + mcr p15, 0, r1, c1, c0, 0 + isb + + /* restore it with 0x1f if use ldo bypass mode.*/ + ldr r8, =MX6Q_ANATOP_BASE_ADDR + ldr r7, [r8, #MX6Q_ANATOP_CORE] + and r7, r7, #0x1f + cmp r7, #0x1e + bne ldo_check_done3 + ldr r7, [r8, #MX6Q_ANATOP_CORE] + orr r7, r7, #0x1f + str r7, [r8, #MX6Q_ANATOP_CORE] +ldo_check_done3: + + ldr r5, =MX6Q_SRC_BASE_ADDR + ldr r10, [r5, #MX6Q_SRC_GPR2] + ldmea r10!, {lr} + + /* get cpu tpye */ + ldmea r10!, {r2} + + /* clear core0's entry and parameter */ + ldr r8, =MX6Q_SRC_BASE_ADDR + mov r7, #0 + str r7, [r8, #MX6Q_SRC_GPR1] + str r7, [r8, #MX6Q_SRC_GPR2] + + ldr r8, =MX6Q_IOMUXC_BASE_ADDR + + cmp r2, #MXC_CPU_IMX6Q + bne dl_io_dsm_restore + imx6dq_ddr_io_restore + b ddr_io_restore_dsm_done +dl_io_dsm_restore: + cmp r2, #MXC_CPU_IMX6DL + bne sl_io_dsm_restore + imx6dl_ddr_io_restore + b ddr_io_restore_dsm_done +sl_io_dsm_restore: + imx6sl_ddr_io_restore + ldr r8, =MX6Q_MMDC_P0_BASE_ADDR + /* reset read FIFO, RST_RD_FIFO */ + ldr r7, =MX6Q_MMDC_MPDGCTRL0 + ldr r6, [r8, r7] + orr r6, r6, #(1 << 31) + str r6, [r8, r7] +dsm_fifo_reset1_wait: + ldr r6, [r8, r7] + and r6, r6, #(1 << 31) + cmp r6, #0 + bne dsm_fifo_reset1_wait + + /* reset FIFO a second time */ + ldr r6, [r8, r7] + orr r6, r6, #(1 << 31) + str r6, [r8, r7] +dsm_fifo_reset2_wait: + ldr r6, [r8, r7] + and r6, r6, #(1 << 31) + cmp r6, #0 + bne dsm_fifo_reset2_wait +ddr_io_restore_dsm_done: + + ldr r8, =MX6Q_MMDC_P0_BASE_ADDR + /* let DDR out of self-refresh */ + ldr r7, [r8, #MX6Q_MMDC_MAPSR] + bic r7, r7, #(1 << 21) + str r7, [r8, #MX6Q_MMDC_MAPSR] + +poll_dvfs_clear_1: + ldr r7, [r8, #MX6Q_MMDC_MAPSR] + ands r7, r7, #(1 << 25) + bne poll_dvfs_clear_1 + /* enable DDR auto power saving */ + ldr r7, [r8, #MX6Q_MMDC_MAPSR] + bic r7, r7, #0x1 + str r7, [r8, #MX6Q_MMDC_MAPSR] + mov pc, lr diff -Nur linux-3.10.30/arch/arm/mach-imx/system.c linux-3.10.30-cubox-i/arch/arm/mach-imx/system.c --- linux-3.10.30/arch/arm/mach-imx/system.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/system.c 2014-03-08 20:32:54.000000000 +0100 @@ -1,7 +1,7 @@ /* * Copyright (C) 1999 ARM Limited * Copyright (C) 2000 Deep Blue Solutions Ltd - * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2006-2013 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Juergen Beisert, kernel@pengutronix.de * Copyright 2009 Ilya Yanok, Emcraft Systems Ltd, yanok@emcraft.com * @@ -21,15 +21,20 @@ #include #include #include +#include +#include #include #include #include +#include #include "common.h" #include "hardware.h" static void __iomem *wdog_base; +static struct clk *wdog_clk; +static u32 wdog_source = 1; /* use WDOG1 default */ /* * Reset the system. It is called by machine_restart(). @@ -38,24 +43,32 @@ { unsigned int wcr_enable; - if (cpu_is_mx1()) { - wcr_enable = (1 << 0); - } else { - struct clk *clk; + if (wdog_clk) + clk_enable(wdog_clk); - clk = clk_get_sys("imx2-wdt.0", NULL); - if (!IS_ERR(clk)) - clk_prepare_enable(clk); + if (cpu_is_mx1()) + wcr_enable = (1 << 0); + /* + * Some i.MX6 boards use WDOG2 to reset external pmic in bypass mode, + * so do WDOG2 reset here. Do not set SRS, since we will + * trigger external POR later. Use WDOG1 to reset in ldo-enable + * mode. You can set it by "fsl,wdog-reset" in dts. + */ + else if (wdog_source == 2 && (cpu_is_imx6q() || cpu_is_imx6dl() || + cpu_is_imx6sl())) + wcr_enable = 0x14; + else wcr_enable = (1 << 2); - } /* Assert SRS signal */ __raw_writew(wcr_enable, wdog_base); + /* write twice to ensure the request will not get ignored */ + __raw_writew(wcr_enable, wdog_base); /* wait for reset to assert... */ mdelay(500); - printk(KERN_ERR "Watchdog reset failed to assert reset\n"); + pr_err("%s: Watchdog reset failed to assert reset\n", __func__); /* delay to allow the serial port to show the message */ mdelay(50); @@ -64,7 +77,94 @@ soft_restart(0); } -void mxc_arch_reset_init(void __iomem *base) +void __init mxc_arch_reset_init(void __iomem *base) { wdog_base = base; + + wdog_clk = clk_get_sys("imx2-wdt.0", NULL); + if (IS_ERR(wdog_clk)) { + pr_warn("%s: failed to get wdog clock\n", __func__); + wdog_clk = NULL; + return; + } + + clk_prepare(wdog_clk); +} + +void __init mxc_arch_reset_init_dt(void) +{ + struct device_node *np = NULL; + + if (cpu_is_imx6q() || cpu_is_imx6dl()) + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); + else if (cpu_is_imx6sl()) + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-gpc"); + + if (np) + of_property_read_u32(np, "fsl,wdog-reset", &wdog_source); + pr_info("Use WDOG%d as reset source\n", wdog_source); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx21-wdt"); + wdog_base = of_iomap(np, 0); + WARN_ON(!wdog_base); + + /* Some i.MX6 boards use WDOG2 to reset board in ldo-bypass mode */ + if (wdog_source == 2 && (cpu_is_imx6q() || cpu_is_imx6dl() || + cpu_is_imx6sl())) { + np = of_find_compatible_node(np, NULL, "fsl,imx21-wdt"); + wdog_base = of_iomap(np, 0); + WARN_ON(!wdog_base); + } + + wdog_clk = of_clk_get(np, 0); + if (IS_ERR(wdog_clk)) { + pr_warn("%s: failed to get wdog clock\n", __func__); + wdog_clk = NULL; + return; + } + + clk_prepare(wdog_clk); +} + +#ifdef CONFIG_CACHE_L2X0 +void __init imx_init_l2cache(void) +{ + void __iomem *l2x0_base; + struct device_node *np; + unsigned int val; + + np = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); + if (!np) + goto out; + + l2x0_base = of_iomap(np, 0); + if (!l2x0_base) { + of_node_put(np); + goto out; + } + + /* Configure the L2 PREFETCH and POWER registers */ + val = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL); + val |= 0x30000000; + /* + * The L2 cache controller(PL310) version on the i.MX6D/Q is r3p1-50rel0 + * The L2 cache controller(PL310) version on the i.MX6DL/SOLO/SL is r3p2 + * But according to ARM PL310 errata: 752271 + * ID: 752271: Double linefill feature can cause data corruption + * Fault Status: Present in: r3p0, r3p1, r3p1-50rel0. Fixed in r3p2 + * Workaround: The only workaround to this erratum is to disable the + * double linefill feature. This is the default behavior. + */ + if (!of_machine_is_compatible("fsl,imx6q")) + val |= 0x40800000; + writel_relaxed(val, l2x0_base + L2X0_PREFETCH_CTRL); + val = L2X0_DYNAMIC_CLK_GATING_EN | L2X0_STNDBY_MODE_EN; + writel_relaxed(val, l2x0_base + L2X0_POWER_CTRL); + + iounmap(l2x0_base); + of_node_put(np); + +out: + l2x0_of_init(0, ~0UL); } +#endif diff -Nur linux-3.10.30/arch/arm/mach-imx/time.c linux-3.10.30-cubox-i/arch/arm/mach-imx/time.c --- linux-3.10.30/arch/arm/mach-imx/time.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-imx/time.c 2014-03-08 20:32:54.000000000 +0100 @@ -60,7 +60,11 @@ #define V2_TCTL_WAITEN (1 << 3) /* Wait enable mode */ #define V2_TCTL_CLK_IPG (1 << 6) #define V2_TCTL_CLK_PER (2 << 6) +#define V2_TCTL_CLK_OSC_DIV8 (5 << 6) +#define V2_TCTL_CLK_OSC (7 << 6) +#define V2_TCTL_24MEN (1 << 10) #define V2_TCTL_FRR (1 << 9) +#define V2_TPRER_PRE24M 12 #define V2_IR 0x0c #define V2_TSTAT 0x08 #define V2_TSTAT_OF1 (1 << 0) @@ -277,11 +281,20 @@ void __init mxc_timer_init(void __iomem *base, int irq) { - uint32_t tctl_val; + uint32_t tctl_val, tprer_val; struct clk *timer_clk; struct clk *timer_ipg_clk; - timer_clk = clk_get_sys("imx-gpt.0", "per"); + /* + * gpt clk source from 24M OSC on imx6q > TO1.0 and + * imx6dl, others from per clk. + */ + if ((cpu_is_imx6q() && imx_get_soc_revision() > IMX_CHIP_REVISION_1_0) + || cpu_is_imx6dl()) + timer_clk = clk_get_sys("imx-gpt.0", "gpt_3m"); + else + timer_clk = clk_get_sys("imx-gpt.0", "per"); + if (IS_ERR(timer_clk)) { pr_err("i.MX timer: unable to get clk\n"); return; @@ -302,10 +315,24 @@ __raw_writel(0, timer_base + MXC_TCTL); __raw_writel(0, timer_base + MXC_TPRER); /* see datasheet note */ - if (timer_is_v2()) - tctl_val = V2_TCTL_CLK_PER | V2_TCTL_FRR | V2_TCTL_WAITEN | MXC_TCTL_TEN; - else + if (timer_is_v2()) { + if ((cpu_is_imx6q() && imx_get_soc_revision() > + IMX_CHIP_REVISION_1_0) || cpu_is_imx6dl()) { + tctl_val = V2_TCTL_CLK_OSC_DIV8 | V2_TCTL_FRR | + V2_TCTL_WAITEN | MXC_TCTL_TEN; + if (cpu_is_imx6dl()) { + /* 24 / 8 = 3 MHz */ + tprer_val = 7 << V2_TPRER_PRE24M; + __raw_writel(tprer_val, timer_base + MXC_TPRER); + tctl_val |= V2_TCTL_24MEN; + } + } else { + tctl_val = V2_TCTL_CLK_PER | V2_TCTL_FRR | + V2_TCTL_WAITEN | MXC_TCTL_TEN; + } + } else { tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN; + } __raw_writel(tctl_val, timer_base + MXC_TCTL); diff -Nur linux-3.10.30/arch/arm/mach-integrator/Makefile linux-3.10.30-cubox-i/arch/arm/mach-integrator/Makefile --- linux-3.10.30/arch/arm/mach-integrator/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-integrator/Makefile 2014-03-08 20:32:54.000000000 +0100 @@ -8,5 +8,5 @@ obj-$(CONFIG_ARCH_INTEGRATOR_AP) += integrator_ap.o obj-$(CONFIG_ARCH_INTEGRATOR_CP) += integrator_cp.o -obj-$(CONFIG_PCI) += pci_v3.o pci.o +obj-$(CONFIG_PCI) += pci_v3.o obj-$(CONFIG_INTEGRATOR_IMPD1) += impd1.o diff -Nur linux-3.10.30/arch/arm/mach-integrator/pci.c linux-3.10.30-cubox-i/arch/arm/mach-integrator/pci.c --- linux-3.10.30/arch/arm/mach-integrator/pci.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-integrator/pci.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,113 +0,0 @@ -/* - * linux/arch/arm/mach-integrator/pci-integrator.c - * - * Copyright (C) 1999 ARM Limited - * Copyright (C) 2000 Deep Blue Solutions Ltd - * - * 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 - * - * - * PCI functions for Integrator - */ -#include -#include -#include -#include - -#include -#include - -#include - -/* - * A small note about bridges and interrupts. The DECchip 21050 (and - * later) adheres to the PCI-PCI bridge specification. This says that - * the interrupts on the other side of a bridge are swizzled in the - * following manner: - * - * Dev Interrupt Interrupt - * Pin on Pin on - * Device Connector - * - * 4 A A - * B B - * C C - * D D - * - * 5 A B - * B C - * C D - * D A - * - * 6 A C - * B D - * C A - * D B - * - * 7 A D - * B A - * C B - * D C - * - * Where A = pin 1, B = pin 2 and so on and pin=0 = default = A. - * Thus, each swizzle is ((pin-1) + (device#-4)) % 4 - */ - -/* - * This routine handles multiple bridges. - */ -static u8 __init integrator_swizzle(struct pci_dev *dev, u8 *pinp) -{ - if (*pinp == 0) - *pinp = 1; - - return pci_common_swizzle(dev, pinp); -} - -static int irq_tab[4] __initdata = { - IRQ_AP_PCIINT0, IRQ_AP_PCIINT1, IRQ_AP_PCIINT2, IRQ_AP_PCIINT3 -}; - -/* - * map the specified device/slot/pin to an IRQ. This works out such - * that slot 9 pin 1 is INT0, pin 2 is INT1, and slot 10 pin 1 is INT1. - */ -static int __init integrator_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - int intnr = ((slot - 9) + (pin - 1)) & 3; - - return irq_tab[intnr]; -} - -extern void pci_v3_init(void *); - -static struct hw_pci integrator_pci __initdata = { - .swizzle = integrator_swizzle, - .map_irq = integrator_map_irq, - .setup = pci_v3_setup, - .nr_controllers = 1, - .ops = &pci_v3_ops, - .preinit = pci_v3_preinit, - .postinit = pci_v3_postinit, -}; - -static int __init integrator_pci_init(void) -{ - if (machine_is_integrator()) - pci_common_init(&integrator_pci); - return 0; -} - -subsys_initcall(integrator_pci_init); diff -Nur linux-3.10.30/arch/arm/mach-integrator/pci_v3.c linux-3.10.30-cubox-i/arch/arm/mach-integrator/pci_v3.c --- linux-3.10.30/arch/arm/mach-integrator/pci_v3.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-integrator/pci_v3.c 2014-03-08 20:32:54.000000000 +0100 @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -337,7 +338,7 @@ return PCIBIOS_SUCCESSFUL; } -struct pci_ops pci_v3_ops = { +static struct pci_ops pci_v3_ops = { .read = v3_read_config, .write = v3_write_config, }; @@ -471,7 +472,7 @@ return IRQ_HANDLED; } -int __init pci_v3_setup(int nr, struct pci_sys_data *sys) +static int __init pci_v3_setup(int nr, struct pci_sys_data *sys) { int ret = 0; @@ -490,7 +491,7 @@ * V3_LB_BASE? - local bus address * V3_LB_MAP? - pci bus address */ -void __init pci_v3_preinit(void) +static void __init pci_v3_preinit(void) { unsigned long flags; unsigned int temp; @@ -589,7 +590,7 @@ raw_spin_unlock_irqrestore(&v3_lock, flags); } -void __init pci_v3_postinit(void) +static void __init pci_v3_postinit(void) { unsigned int pci_cmd; @@ -610,3 +611,82 @@ register_isa_ports(PHYS_PCI_MEM_BASE, PHYS_PCI_IO_BASE, 0); } + +/* + * A small note about bridges and interrupts. The DECchip 21050 (and + * later) adheres to the PCI-PCI bridge specification. This says that + * the interrupts on the other side of a bridge are swizzled in the + * following manner: + * + * Dev Interrupt Interrupt + * Pin on Pin on + * Device Connector + * + * 4 A A + * B B + * C C + * D D + * + * 5 A B + * B C + * C D + * D A + * + * 6 A C + * B D + * C A + * D B + * + * 7 A D + * B A + * C B + * D C + * + * Where A = pin 1, B = pin 2 and so on and pin=0 = default = A. + * Thus, each swizzle is ((pin-1) + (device#-4)) % 4 + */ + +/* + * This routine handles multiple bridges. + */ +static u8 __init integrator_swizzle(struct pci_dev *dev, u8 *pinp) +{ + if (*pinp == 0) + *pinp = 1; + + return pci_common_swizzle(dev, pinp); +} + +static int irq_tab[4] __initdata = { + IRQ_AP_PCIINT0, IRQ_AP_PCIINT1, IRQ_AP_PCIINT2, IRQ_AP_PCIINT3 +}; + +/* + * map the specified device/slot/pin to an IRQ. This works out such + * that slot 9 pin 1 is INT0, pin 2 is INT1, and slot 10 pin 1 is INT1. + */ +static int __init integrator_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + int intnr = ((slot - 9) + (pin - 1)) & 3; + + return irq_tab[intnr]; +} + +static struct hw_pci integrator_pci __initdata = { + .swizzle = integrator_swizzle, + .map_irq = integrator_map_irq, + .setup = pci_v3_setup, + .nr_controllers = 1, + .ops = &pci_v3_ops, + .preinit = pci_v3_preinit, + .postinit = pci_v3_postinit, +}; + +static int __init integrator_pci_init(void) +{ + if (machine_is_integrator()) + pci_common_init(&integrator_pci); + return 0; +} + +subsys_initcall(integrator_pci_init); diff -Nur linux-3.10.30/arch/arm/mach-vexpress/Kconfig linux-3.10.30-cubox-i/arch/arm/mach-vexpress/Kconfig --- linux-3.10.30/arch/arm/mach-vexpress/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/Kconfig 2014-03-08 20:32:56.000000000 +0100 @@ -1,5 +1,7 @@ config ARCH_VEXPRESS bool "ARM Ltd. Versatile Express family" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_HAS_OPP select ARCH_REQUIRE_GPIOLIB select ARM_AMBA select ARM_GIC @@ -56,5 +58,23 @@ config ARCH_VEXPRESS_CA9X4 bool "Versatile Express Cortex-A9x4 tile" + select ARM_ERRATA_643719 + +config ARCH_VEXPRESS_DCSCB + bool "Dual Cluster System Control Block (DCSCB) support" + depends on MCPM + select ARM_CCI + help + Support for the Dual Cluster System Configuration Block (DCSCB). + This is needed to provide CPU and cluster power management + on RTSM implementing big.LITTLE. + +config ARCH_VEXPRESS_TC2 + bool "TC2 cluster management" + depends on MCPM + select VEXPRESS_SPC + select ARM_CCI + help + Support for CPU and cluster power management on TC2. endmenu diff -Nur linux-3.10.30/arch/arm/mach-vexpress/Makefile linux-3.10.30-cubox-i/arch/arm/mach-vexpress/Makefile --- linux-3.10.30/arch/arm/mach-vexpress/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/Makefile 2014-03-08 20:32:56.000000000 +0100 @@ -6,5 +6,13 @@ obj-y := v2m.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o +obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o +CFLAGS_REMOVE_dcscb.o = -pg +obj-$(CONFIG_ARCH_VEXPRESS_TC2) += tc2_pm.o tc2_pm_setup.o +CFLAGS_REMOVE_tc2_pm.o = -pg +ifeq ($(CONFIG_ARCH_VEXPRESS_TC2),y) +obj-$(CONFIG_ARM_PSCI) += tc2_pm_psci.o +CFLAGS_REMOVE_tc2_pm_psci.o = -pg +endif obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff -Nur linux-3.10.30/arch/arm/mach-vexpress/core.h linux-3.10.30-cubox-i/arch/arm/mach-vexpress/core.h --- linux-3.10.30/arch/arm/mach-vexpress/core.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/core.h 2014-03-08 20:32:56.000000000 +0100 @@ -6,6 +6,8 @@ void vexpress_dt_smp_map_io(void); +bool vexpress_smp_init_ops(void); + extern struct smp_operations vexpress_smp_ops; extern void vexpress_cpu_die(unsigned int cpu); diff -Nur linux-3.10.30/arch/arm/mach-vexpress/dcscb.c linux-3.10.30-cubox-i/arch/arm/mach-vexpress/dcscb.c --- linux-3.10.30/arch/arm/mach-vexpress/dcscb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/dcscb.c 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,236 @@ +/* + * arch/arm/mach-vexpress/dcscb.c - Dual Cluster System Configuration Block + * + * Created by: Nicolas Pitre, May 2012 + * Copyright: (C) 2012-2013 Linaro Limited + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#define RST_HOLD0 0x0 +#define RST_HOLD1 0x4 +#define SYS_SWRESET 0x8 +#define RST_STAT0 0xc +#define RST_STAT1 0x10 +#define EAG_CFG_R 0x20 +#define EAG_CFG_W 0x24 +#define KFC_CFG_R 0x28 +#define KFC_CFG_W 0x2c +#define DCS_CFG_R 0x30 + +/* + * We can't use regular spinlocks. In the switcher case, it is possible + * for an outbound CPU to call power_down() while its inbound counterpart + * is already live using the same logical CPU number which trips lockdep + * debugging. + */ +static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +static void __iomem *dcscb_base; +static int dcscb_use_count[4][2]; +static int dcscb_allcpus_mask[2]; + +static int dcscb_power_up(unsigned int cpu, unsigned int cluster) +{ + unsigned int rst_hold, cpumask = (1 << cpu); + unsigned int all_mask = dcscb_allcpus_mask[cluster]; + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + if (cpu >= 4 || cluster >= 2) + return -EINVAL; + + /* + * Since this is called with IRQs enabled, and no arch_spin_lock_irq + * variant exists, we need to disable IRQs manually here. + */ + local_irq_disable(); + arch_spin_lock(&dcscb_lock); + + dcscb_use_count[cpu][cluster]++; + if (dcscb_use_count[cpu][cluster] == 1) { + rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); + if (rst_hold & (1 << 8)) { + /* remove cluster reset and add individual CPU's reset */ + rst_hold &= ~(1 << 8); + rst_hold |= all_mask; + } + rst_hold &= ~(cpumask | (cpumask << 4)); + writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); + } else if (dcscb_use_count[cpu][cluster] != 2) { + /* + * The only possible values are: + * 0 = CPU down + * 1 = CPU (still) up + * 2 = CPU requested to be up before it had a chance + * to actually make itself down. + * Any other value is a bug. + */ + BUG(); + } + + arch_spin_unlock(&dcscb_lock); + local_irq_enable(); + + return 0; +} + +static void dcscb_power_down(void) +{ + unsigned int mpidr, cpu, cluster, rst_hold, cpumask, all_mask; + bool last_man = false, skip_wfi = false; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + cpumask = (1 << cpu); + all_mask = dcscb_allcpus_mask[cluster]; + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cpu >= 4 || cluster >= 2); + + __mcpm_cpu_going_down(cpu, cluster); + + arch_spin_lock(&dcscb_lock); + BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); + dcscb_use_count[cpu][cluster]--; + if (dcscb_use_count[cpu][cluster] == 0) { + rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); + rst_hold |= cpumask; + if (((rst_hold | (rst_hold >> 4)) & all_mask) == all_mask) { + rst_hold |= (1 << 8); + last_man = true; + } + writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); + } else if (dcscb_use_count[cpu][cluster] == 1) { + /* + * A power_up request went ahead of us. + * Even if we do not want to shut this CPU down, + * the caller expects a certain state as if the WFI + * was aborted. So let's continue with cache cleaning. + */ + skip_wfi = true; + } else + BUG(); + + if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { + arch_spin_unlock(&dcscb_lock); + + /* Flush all cache levels for this cluster. */ + v7_exit_coherency_flush(all); + + /* + * This is a harmless no-op. On platforms with a real + * outer cache this might either be needed or not, + * depending on where the outer cache sits. + */ + outer_flush_all(); + + /* + * Disable cluster-level coherency by masking + * incoming snoops and DVM messages: + */ + cci_disable_port_by_cpu(mpidr); + + __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN); + } else { + arch_spin_unlock(&dcscb_lock); + + /* Disable and flush the local CPU cache. */ + v7_exit_coherency_flush(louis); + } + + __mcpm_cpu_down(cpu, cluster); + + /* Now we are prepared for power-down, do it: */ + dsb(); + if (!skip_wfi) + wfi(); + + /* Not dead at this point? Let our caller cope. */ +} + +static const struct mcpm_platform_ops dcscb_power_ops = { + .power_up = dcscb_power_up, + .power_down = dcscb_power_down, +}; + +static void __init dcscb_usage_count_init(void) +{ + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cpu >= 4 || cluster >= 2); + dcscb_use_count[cpu][cluster] = 1; +} + +extern void dcscb_power_up_setup(unsigned int affinity_level); + +static int __init dcscb_init(void) +{ + struct device_node *node; + unsigned int cfg; + int ret; + + ret = psci_probe(); + if (!ret) { + pr_debug("psci found. Aborting native init\n"); + return -ENODEV; + } + + if (!cci_probed()) + return -ENODEV; + + node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb"); + if (!node) + return -ENODEV; + dcscb_base = of_iomap(node, 0); + if (!dcscb_base) + return -EADDRNOTAVAIL; + cfg = readl_relaxed(dcscb_base + DCS_CFG_R); + dcscb_allcpus_mask[0] = (1 << (((cfg >> 16) >> (0 << 2)) & 0xf)) - 1; + dcscb_allcpus_mask[1] = (1 << (((cfg >> 16) >> (1 << 2)) & 0xf)) - 1; + dcscb_usage_count_init(); + + ret = mcpm_platform_register(&dcscb_power_ops); + if (!ret) + ret = mcpm_sync_init(dcscb_power_up_setup); + if (ret) { + iounmap(dcscb_base); + return ret; + } + + pr_info("VExpress DCSCB support installed\n"); + + /* + * Future entries into the kernel can now go + * through the cluster entry vectors. + */ + vexpress_flags_set(virt_to_phys(mcpm_entry_point)); + + return 0; +} + +early_initcall(dcscb_init); diff -Nur linux-3.10.30/arch/arm/mach-vexpress/dcscb_setup.S linux-3.10.30-cubox-i/arch/arm/mach-vexpress/dcscb_setup.S --- linux-3.10.30/arch/arm/mach-vexpress/dcscb_setup.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/dcscb_setup.S 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,38 @@ +/* + * arch/arm/include/asm/dcscb_setup.S + * + * Created by: Dave Martin, 2012-06-22 + * Copyright: (C) 2012-2013 Linaro Limited + * + * 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 + + +ENTRY(dcscb_power_up_setup) + + cmp r0, #0 @ check affinity level + beq 2f + +/* + * Enable cluster-level coherency, in preparation for turning on the MMU. + * The ACTLR SMP bit does not need to be set here, because cpu_resume() + * already restores that. + * + * A15/A7 may not require explicit L2 invalidation on reset, dependent + * on hardware integration decisions. + * For now, this code assumes that L2 is either already invalidated, + * or invalidation is not required. + */ + + b cci_enable_port_for_self + +2: @ Implementation-specific local CPU setup operations should go here, + @ if any. In this case, there is nothing to do. + + bx lr + +ENDPROC(dcscb_power_up_setup) diff -Nur linux-3.10.30/arch/arm/mach-vexpress/include/mach/tc2.h linux-3.10.30-cubox-i/arch/arm/mach-vexpress/include/mach/tc2.h --- linux-3.10.30/arch/arm/mach-vexpress/include/mach/tc2.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/include/mach/tc2.h 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,10 @@ +#ifndef __MACH_TC2_H +#define __MACH_TC2_H + +/* + * cpu and cluster limits + */ +#define TC2_MAX_CPUS 3 +#define TC2_MAX_CLUSTERS 2 + +#endif diff -Nur linux-3.10.30/arch/arm/mach-vexpress/platsmp.c linux-3.10.30-cubox-i/arch/arm/mach-vexpress/platsmp.c --- linux-3.10.30/arch/arm/mach-vexpress/platsmp.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/platsmp.c 2014-03-08 20:32:56.000000000 +0100 @@ -12,9 +12,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -203,3 +205,21 @@ .cpu_die = vexpress_cpu_die, #endif }; + +bool __init vexpress_smp_init_ops(void) +{ +#ifdef CONFIG_MCPM + /* + * The best way to detect a multi-cluster configuration at the moment + * is to look for the presence of a CCI in the system. + * Override the default vexpress_smp_ops if so. + */ + struct device_node *node; + node = of_find_compatible_node(NULL, NULL, "arm,cci-400"); + if (node && of_device_is_available(node)) { + mcpm_smp_set_ops(); + return true; + } +#endif + return false; +} diff -Nur linux-3.10.30/arch/arm/mach-vexpress/tc2_pm.c linux-3.10.30-cubox-i/arch/arm/mach-vexpress/tc2_pm.c --- linux-3.10.30/arch/arm/mach-vexpress/tc2_pm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/tc2_pm.c 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,277 @@ +/* + * arch/arm/mach-vexpress/tc2_pm.c - TC2 power management support + * + * Created by: Nicolas Pitre, October 2012 + * Copyright: (C) 2012 Linaro Limited + * + * Some portions of this file were originally written by Achin Gupta + * Copyright: (C) 2012 ARM Limited + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * We can't use regular spinlocks. In the switcher case, it is possible + * for an outbound CPU to call power_down() after its inbound counterpart + * is already live using the same logical CPU number which trips lockdep + * debugging. + */ +static arch_spinlock_t tc2_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +static int tc2_pm_use_count[TC2_MAX_CPUS][TC2_MAX_CLUSTERS]; + +static int tc2_pm_power_up(unsigned int cpu, unsigned int cluster) +{ + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + if (cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)) + return -EINVAL; + + /* + * Since this is called with IRQs enabled, and no arch_spin_lock_irq + * variant exists, we need to disable IRQs manually here. + */ + local_irq_disable(); + arch_spin_lock(&tc2_pm_lock); + + if (!tc2_pm_use_count[0][cluster] && + !tc2_pm_use_count[1][cluster] && + !tc2_pm_use_count[2][cluster]) + vexpress_spc_powerdown_enable(cluster, 0); + + tc2_pm_use_count[cpu][cluster]++; + if (tc2_pm_use_count[cpu][cluster] == 1) { + vexpress_spc_write_resume_reg(cluster, cpu, + virt_to_phys(mcpm_entry_point)); + vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 1); + } else if (tc2_pm_use_count[cpu][cluster] != 2) { + /* + * The only possible values are: + * 0 = CPU down + * 1 = CPU (still) up + * 2 = CPU requested to be up before it had a chance + * to actually make itself down. + * Any other value is a bug. + */ + BUG(); + } + + arch_spin_unlock(&tc2_pm_lock); + local_irq_enable(); + + return 0; +} + +static void tc2_pm_down(u64 residency) +{ + unsigned int mpidr, cpu, cluster; + bool last_man = false, skip_wfi = false; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)); + + __mcpm_cpu_going_down(cpu, cluster); + + arch_spin_lock(&tc2_pm_lock); + BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); + tc2_pm_use_count[cpu][cluster]--; + if (tc2_pm_use_count[cpu][cluster] == 0) { + vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 1); + if (!tc2_pm_use_count[0][cluster] && + !tc2_pm_use_count[1][cluster] && + !tc2_pm_use_count[2][cluster] && + (!residency || residency > 5000)) { + vexpress_spc_powerdown_enable(cluster, 1); + vexpress_spc_set_global_wakeup_intr(1); + last_man = true; + } + } else if (tc2_pm_use_count[cpu][cluster] == 1) { + /* + * A power_up request went ahead of us. + * Even if we do not want to shut this CPU down, + * the caller expects a certain state as if the WFI + * was aborted. So let's continue with cache cleaning. + */ + skip_wfi = true; + } else + BUG(); + + /* + * If the CPU is committed to power down, make sure + * the power controller will be in charge of waking it + * up upon IRQ, ie IRQ lines are cut from GIC CPU IF + * to the CPU by disabling the GIC CPU IF to prevent wfi + * from completing execution behind power controller back + */ + if (!skip_wfi) + gic_cpu_if_down(); + + if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { + arch_spin_unlock(&tc2_pm_lock); + + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) { + /* + * On the Cortex-A15 we need to disable + * L2 prefetching before flushing the cache. + */ + asm volatile( + "mcr p15, 1, %0, c15, c0, 3 \n\t" + "isb \n\t" + "dsb " + : : "r" (0x400) ); + } + + v7_exit_coherency_flush(all); + + cci_disable_port_by_cpu(mpidr); + + __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN); + } else { + /* + * If last man then undo any setup done previously. + */ + if (last_man) { + vexpress_spc_powerdown_enable(cluster, 0); + vexpress_spc_set_global_wakeup_intr(0); + } + + arch_spin_unlock(&tc2_pm_lock); + + v7_exit_coherency_flush(louis); + } + + __mcpm_cpu_down(cpu, cluster); + + /* Now we are prepared for power-down, do it: */ + if (!skip_wfi) + wfi(); + + /* Not dead at this point? Let our caller cope. */ +} + +static void tc2_pm_power_down(void) +{ + tc2_pm_down(0); +} + +static void tc2_pm_suspend(u64 residency) +{ + extern void tc2_resume(void); + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + vexpress_spc_write_resume_reg(cluster, cpu, + virt_to_phys(tc2_resume)); + + tc2_pm_down(residency); +} + +static void tc2_pm_powered_up(void) +{ + unsigned int mpidr, cpu, cluster; + unsigned long flags; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)); + + local_irq_save(flags); + arch_spin_lock(&tc2_pm_lock); + + if (!tc2_pm_use_count[0][cluster] && + !tc2_pm_use_count[1][cluster] && + !tc2_pm_use_count[2][cluster]) { + vexpress_spc_powerdown_enable(cluster, 0); + vexpress_spc_set_global_wakeup_intr(0); + } + + if (!tc2_pm_use_count[cpu][cluster]) + tc2_pm_use_count[cpu][cluster] = 1; + + vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 0); + vexpress_spc_write_resume_reg(cluster, cpu, 0); + + arch_spin_unlock(&tc2_pm_lock); + local_irq_restore(flags); +} + +static const struct mcpm_platform_ops tc2_pm_power_ops = { + .power_up = tc2_pm_power_up, + .power_down = tc2_pm_power_down, + .suspend = tc2_pm_suspend, + .powered_up = tc2_pm_powered_up, +}; + +static void __init tc2_pm_usage_count_init(void) +{ + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)); + + tc2_pm_use_count[cpu][cluster] = 1; +} + +extern void tc2_pm_power_up_setup(unsigned int affinity_level); + +static int __init tc2_pm_init(void) +{ + int ret; + + ret = psci_probe(); + if (!ret) { + pr_debug("psci found. Aborting native init\n"); + return -ENODEV; + } + + if (!vexpress_spc_check_loaded()) + return -ENODEV; + + tc2_pm_usage_count_init(); + + ret = mcpm_platform_register(&tc2_pm_power_ops); + if (!ret) + ret = mcpm_sync_init(tc2_pm_power_up_setup); + if (!ret) + pr_info("TC2 power management initialized\n"); + return ret; +} + +early_initcall(tc2_pm_init); diff -Nur linux-3.10.30/arch/arm/mach-vexpress/tc2_pm_psci.c linux-3.10.30-cubox-i/arch/arm/mach-vexpress/tc2_pm_psci.c --- linux-3.10.30/arch/arm/mach-vexpress/tc2_pm_psci.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/tc2_pm_psci.c 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,173 @@ +/* + * arch/arm/mach-vexpress/tc2_pm_psci.c - TC2 PSCI support + * + * Created by: Achin Gupta, December 2012 + * Copyright: (C) 2012 ARM Limited + * + * Some portions of this file were originally written by Nicolas Pitre + * Copyright: (C) 2012 Linaro Limited + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* + * Platform specific state id understood by the firmware and used to + * program the power controller + */ +#define PSCI_POWER_STATE_ID 0 + +static atomic_t tc2_pm_use_count[TC2_MAX_CPUS][TC2_MAX_CLUSTERS]; + +static int tc2_pm_psci_power_up(unsigned int cpu, unsigned int cluster) +{ + unsigned int mpidr = (cluster << 8) | cpu; + int ret = 0; + + BUG_ON(!psci_ops.cpu_on); + + switch (atomic_inc_return(&tc2_pm_use_count[cpu][cluster])) { + case 1: + /* + * This is a request to power up a cpu that linux thinks has + * been powered down. Retries are needed if the firmware has + * seen the power down request as yet. + */ + do + ret = psci_ops.cpu_on(mpidr, + virt_to_phys(mcpm_entry_point)); + while (ret == -EAGAIN); + + return ret; + case 2: + /* This power up request has overtaken a power down request */ + return ret; + default: + /* Any other value is a bug */ + BUG(); + } +} + +static void tc2_pm_psci_power_down(void) +{ + struct psci_power_state power_state; + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + BUG_ON(!psci_ops.cpu_off); + + switch (atomic_dec_return(&tc2_pm_use_count[cpu][cluster])) { + case 1: + /* + * Overtaken by a power up. Flush caches, exit coherency, + * return & fake a reset + */ + set_cr(get_cr() & ~CR_C); + + flush_cache_louis(); + + asm volatile ("clrex"); + set_auxcr(get_auxcr() & ~(1 << 6)); + + return; + case 0: + /* A normal request to possibly power down the cluster */ + power_state.id = PSCI_POWER_STATE_ID; + power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN; + power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1; + + psci_ops.cpu_off(power_state); + + /* On success this function never returns */ + default: + /* Any other value is a bug */ + BUG(); + } +} + +static void tc2_pm_psci_suspend(u64 unused) +{ + struct psci_power_state power_state; + + BUG_ON(!psci_ops.cpu_suspend); + + /* On TC2 always attempt to power down the cluster */ + power_state.id = PSCI_POWER_STATE_ID; + power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN; + power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1; + + psci_ops.cpu_suspend(power_state, virt_to_phys(mcpm_entry_point)); + + /* On success this function never returns */ + BUG(); +} + +static const struct mcpm_platform_ops tc2_pm_power_ops = { + .power_up = tc2_pm_psci_power_up, + .power_down = tc2_pm_psci_power_down, + .suspend = tc2_pm_psci_suspend, +}; + +static void __init tc2_pm_usage_count_init(void) +{ + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)); + + atomic_set(&tc2_pm_use_count[cpu][cluster], 1); +} + +static int __init tc2_pm_psci_init(void) +{ + int ret; + + ret = psci_probe(); + if (ret) { + pr_debug("psci not found. Aborting psci init\n"); + return -ENODEV; + } + + if (!vexpress_spc_check_loaded()) { + pr_debug("spc not found. Aborting psci init\n"); + return -ENODEV; + } + + tc2_pm_usage_count_init(); + + ret = mcpm_platform_register(&tc2_pm_power_ops); + if (!ret) + ret = mcpm_sync_init(NULL); + if (!ret) + pr_info("TC2 power management initialized\n"); + return ret; +} + +early_initcall(tc2_pm_psci_init); diff -Nur linux-3.10.30/arch/arm/mach-vexpress/tc2_pm_setup.S linux-3.10.30-cubox-i/arch/arm/mach-vexpress/tc2_pm_setup.S --- linux-3.10.30/arch/arm/mach-vexpress/tc2_pm_setup.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/tc2_pm_setup.S 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,68 @@ +/* + * arch/arm/include/asm/tc2_pm_setup.S + * + * Created by: Nicolas Pitre, October 2012 + ( (based on dcscb_setup.S by Dave Martin) + * Copyright: (C) 2012 Linaro Limited + * + * 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 +#include + + +#define SPC_PHYS_BASE 0x7FFF0000 +#define SPC_WAKE_INT_STAT 0xb2c + +#define SNOOP_CTL_A15 0x404 +#define SNOOP_CTL_A7 0x504 + +#define A15_SNOOP_MASK (0x3 << 7) +#define A7_SNOOP_MASK (0x1 << 13) + +#define A15_BX_ADDR0 0xB68 + + +ENTRY(tc2_resume) + mrc p15, 0, r0, c0, c0, 5 + ubfx r1, r0, #0, #4 @ r1 = cpu + ubfx r2, r0, #8, #4 @ r2 = cluster + add r1, r1, r2, lsl #2 @ r1 = index of CPU in WAKE_INT_STAT + ldr r3, =SPC_PHYS_BASE + SPC_WAKE_INT_STAT + ldr r3, [r3] + lsr r3, r1 + tst r3, #1 + wfieq @ if no pending IRQ reenters wfi + b mcpm_entry_point +ENDPROC(tc2_resume) + +/* + * Enable cluster-level coherency, in preparation for turning on the MMU. + * The ACTLR SMP bit does not need to be set here, because cpu_resume() + * already restores that. + */ + +ENTRY(tc2_pm_power_up_setup) + + cmp r0, #0 + beq 2f + + b cci_enable_port_for_self + +2: @ Clear the BX addr register + ldr r3, =SPC_PHYS_BASE + A15_BX_ADDR0 + mrc p15, 0, r0, c0, c0, 5 @ MPIDR + ubfx r1, r0, #8, #4 @ cluster + ubfx r0, r0, #0, #4 @ cpu + add r3, r3, r1, lsl #4 + mov r1, #0 + str r1, [r3, r0, lsl #2] + dsb + + bx lr + +ENDPROC(tc2_pm_power_up_setup) diff -Nur linux-3.10.30/arch/arm/mach-vexpress/v2m.c linux-3.10.30-cubox-i/arch/arm/mach-vexpress/v2m.c --- linux-3.10.30/arch/arm/mach-vexpress/v2m.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-vexpress/v2m.c 2014-03-08 20:32:56.000000000 +0100 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -373,6 +374,31 @@ .init_machine = v2m_init, MACHINE_END +static void __init v2m_dt_hdlcd_init(void) +{ + struct device_node *node; + int len, na, ns; + const __be32 *prop; + phys_addr_t fb_base, fb_size; + + node = of_find_compatible_node(NULL, NULL, "arm,hdlcd"); + if (!node) + return; + + na = of_n_addr_cells(node); + ns = of_n_size_cells(node); + + prop = of_get_property(node, "framebuffer", &len); + if (WARN_ON(!prop || len < (na + ns) * sizeof(*prop))) + return; + + fb_base = of_read_number(prop, na); + fb_size = of_read_number(prop + na, ns); + + if (WARN_ON(memblock_remove(fb_base, fb_size))) + return; +}; + static struct map_desc v2m_rs1_io_desc __initdata = { .virtual = V2M_PERIPH, .pfn = __phys_to_pfn(0x1c000000), @@ -423,6 +449,8 @@ pr_warning("vexpress: DT HBI (%x) is not matching " "hardware (%x)!\n", dt_hbi, hbi); } + + v2m_dt_hdlcd_init(); } static void __init v2m_dt_timer_init(void) @@ -456,6 +484,7 @@ DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express") .dt_compat = v2m_dt_match, .smp = smp_ops(vexpress_smp_ops), + .smp_init = smp_init_ops(vexpress_smp_init_ops), .map_io = v2m_dt_map_io, .init_early = v2m_dt_init_early, .init_irq = irqchip_init, diff -Nur linux-3.10.30/arch/arm/mach-virt/Makefile linux-3.10.30-cubox-i/arch/arm/mach-virt/Makefile --- linux-3.10.30/arch/arm/mach-virt/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-virt/Makefile 2014-03-08 20:32:56.000000000 +0100 @@ -3,4 +3,3 @@ # obj-y := virt.o -obj-$(CONFIG_SMP) += platsmp.o diff -Nur linux-3.10.30/arch/arm/mach-virt/platsmp.c linux-3.10.30-cubox-i/arch/arm/mach-virt/platsmp.c --- linux-3.10.30/arch/arm/mach-virt/platsmp.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-virt/platsmp.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,50 +0,0 @@ -/* - * Dummy Virtual Machine - does what it says on the tin. - * - * Copyright (C) 2012 ARM Ltd - * Author: Will Deacon - * - * 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 . - */ - -#include -#include -#include - -#include -#include - -extern void secondary_startup(void); - -static void __init virt_smp_init_cpus(void) -{ -} - -static void __init virt_smp_prepare_cpus(unsigned int max_cpus) -{ -} - -static int __cpuinit virt_boot_secondary(unsigned int cpu, - struct task_struct *idle) -{ - if (psci_ops.cpu_on) - return psci_ops.cpu_on(cpu_logical_map(cpu), - __pa(secondary_startup)); - return -ENODEV; -} - -struct smp_operations __initdata virt_smp_ops = { - .smp_init_cpus = virt_smp_init_cpus, - .smp_prepare_cpus = virt_smp_prepare_cpus, - .smp_boot_secondary = virt_boot_secondary, -}; diff -Nur linux-3.10.30/arch/arm/mach-virt/virt.c linux-3.10.30-cubox-i/arch/arm/mach-virt/virt.c --- linux-3.10.30/arch/arm/mach-virt/virt.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mach-virt/virt.c 2014-03-08 20:32:56.000000000 +0100 @@ -36,11 +36,8 @@ NULL }; -extern struct smp_operations virt_smp_ops; - DT_MACHINE_START(VIRT, "Dummy Virtual Machine") .init_irq = irqchip_init, .init_machine = virt_init, - .smp = smp_ops(virt_smp_ops), .dt_compat = virt_dt_match, MACHINE_END diff -Nur linux-3.10.30/arch/arm/mm/cache-v7.S linux-3.10.30-cubox-i/arch/arm/mm/cache-v7.S --- linux-3.10.30/arch/arm/mm/cache-v7.S 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mm/cache-v7.S 2014-03-08 20:32:56.000000000 +0100 @@ -146,18 +146,18 @@ ldr r7, =0x7fff ands r7, r7, r1, lsr #13 @ extract max number of the index size loop1: - mov r9, r4 @ create working copy of max way size + mov r9, r7 @ create working copy of max index loop2: - ARM( orr r11, r10, r9, lsl r5 ) @ factor way and cache number into r11 - THUMB( lsl r6, r9, r5 ) + ARM( orr r11, r10, r4, lsl r5 ) @ factor way and cache number into r11 + THUMB( lsl r6, r4, r5 ) THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11 - ARM( orr r11, r11, r7, lsl r2 ) @ factor index number into r11 - THUMB( lsl r6, r7, r2 ) + ARM( orr r11, r11, r9, lsl r2 ) @ factor index number into r11 + THUMB( lsl r6, r9, r2 ) THUMB( orr r11, r11, r6 ) @ factor index number into r11 mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way - subs r9, r9, #1 @ decrement the way + subs r9, r9, #1 @ decrement the index bge loop2 - subs r7, r7, #1 @ decrement the index + subs r4, r4, #1 @ decrement the way bge loop1 skip: add r10, r10, #2 @ increment cache number diff -Nur linux-3.10.30/arch/arm/mm/fault.c linux-3.10.30-cubox-i/arch/arm/mm/fault.c --- linux-3.10.30/arch/arm/mm/fault.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/mm/fault.c 2014-03-08 20:32:56.000000000 +0100 @@ -446,8 +446,16 @@ if (pud_none(*pud_k)) goto bad_area; - if (!pud_present(*pud)) + if (!pud_present(*pud)) { set_pud(pud, *pud_k); + /* + * There is a small window during free_pgtables() where the + * user *pud entry is 0 but the TLB has not been invalidated + * and we get a level 2 (pmd) translation fault caused by the + * intermediate TLB caching of the old level 1 (pud) entry. + */ + flush_tlb_kernel_page(addr); + } pmd = pmd_offset(pud, addr); pmd_k = pmd_offset(pud_k, addr); @@ -470,8 +478,9 @@ #endif if (pmd_none(pmd_k[index])) goto bad_area; + if (!pmd_present(pmd[index])) + copy_pmd(pmd, pmd_k); - copy_pmd(pmd, pmd_k); return 0; bad_area: diff -Nur linux-3.10.30/arch/arm/plat-versatile/headsmp.S linux-3.10.30-cubox-i/arch/arm/plat-versatile/headsmp.S --- linux-3.10.30/arch/arm/plat-versatile/headsmp.S 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm/plat-versatile/headsmp.S 2014-03-08 20:32:56.000000000 +0100 @@ -11,8 +11,6 @@ #include #include - __INIT - /* * Realview/Versatile Express specific entry point for secondary CPUs. * This provides a "holding pen" into which all secondary cores are held diff -Nur linux-3.10.30/arch/arm64/Kconfig linux-3.10.30-cubox-i/arch/arm64/Kconfig --- linux-3.10.30/arch/arm64/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/Kconfig 2014-03-08 20:32:56.000000000 +0100 @@ -144,12 +144,139 @@ If you don't know what to do here, say N. +config ARM_CPU_TOPOLOGY + bool "Support CPU topology definition" + depends on SMP + default y + help + Support CPU topology definition, based on configuration + provided by the firmware. + +config SCHED_MC + bool "Multi-core scheduler support" + depends on ARM_CPU_TOPOLOGY + help + Multi-core scheduler support improves the CPU scheduler's decision + making when dealing with multi-core CPU chips at a cost of slightly + increased overhead in some places. If unsure say N here. + +config SCHED_SMT + bool "SMT scheduler support" + depends on ARM_CPU_TOPOLOGY + help + Improves the CPU scheduler's decision making when dealing with + MultiThreading at a cost of slightly increased overhead in some + places. If unsure say N here. + +config DISABLE_CPU_SCHED_DOMAIN_BALANCE + bool "(EXPERIMENTAL) Disable CPU level scheduler load-balancing" + help + Disables scheduler load-balancing at CPU sched domain level. + +config SCHED_HMP + bool "(EXPERIMENTAL) Heterogenous multiprocessor scheduling" + depends on DISABLE_CPU_SCHED_DOMAIN_BALANCE && SCHED_MC && FAIR_GROUP_SCHED && !SCHED_AUTOGROUP + help + Experimental scheduler optimizations for heterogeneous platforms. + Attempts to introspectively select task affinity to optimize power + and performance. Basic support for multiple (>2) cpu types is in place, + but it has only been tested with two types of cpus. + There is currently no support for migration of task groups, hence + !SCHED_AUTOGROUP. Furthermore, normal load-balancing must be disabled + between cpus of different type (DISABLE_CPU_SCHED_DOMAIN_BALANCE). + +config SCHED_HMP_PRIO_FILTER + bool "(EXPERIMENTAL) Filter HMP migrations by task priority" + depends on SCHED_HMP + help + Enables task priority based HMP migration filter. Any task with + a NICE value above the threshold will always be on low-power cpus + with less compute capacity. + +config SCHED_HMP_PRIO_FILTER_VAL + int "NICE priority threshold" + default 5 + depends on SCHED_HMP_PRIO_FILTER + +config HMP_FAST_CPU_MASK + string "HMP scheduler fast CPU mask" + depends on SCHED_HMP + help + Leave empty to use device tree information. + Specify the cpuids of the fast CPUs in the system as a list string, + e.g. cpuid 0+1 should be specified as 0-1. + +config HMP_SLOW_CPU_MASK + string "HMP scheduler slow CPU mask" + depends on SCHED_HMP + help + Leave empty to use device tree information. + Specify the cpuids of the slow CPUs in the system as a list string, + e.g. cpuid 0+1 should be specified as 0-1. + +config HMP_VARIABLE_SCALE + bool "Allows changing the load tracking scale through sysfs" + depends on SCHED_HMP + help + When turned on, this option exports the thresholds and load average + period value for the load tracking patches through sysfs. + The values can be modified to change the rate of load accumulation + and the thresholds used for HMP migration. + The load_avg_period_ms is the time in ms to reach a load average of + 0.5 for an idle task of 0 load average ratio that start a busy loop. + The up_threshold and down_threshold is the value to go to a faster + CPU or to go back to a slower cpu. + The {up,down}_threshold are devided by 1024 before being compared + to the load average. + For examples, with load_avg_period_ms = 128 and up_threshold = 512, + a running task with a load of 0 will be migrated to a bigger CPU after + 128ms, because after 128ms its load_avg_ratio is 0.5 and the real + up_threshold is 0.5. + This patch has the same behavior as changing the Y of the load + average computation to + (1002/1024)^(LOAD_AVG_PERIOD/load_avg_period_ms) + but it remove intermadiate overflows in computation. + +config HMP_FREQUENCY_INVARIANT_SCALE + bool "(EXPERIMENTAL) Frequency-Invariant Tracked Load for HMP" + depends on HMP_VARIABLE_SCALE && CPU_FREQ + help + Scales the current load contribution in line with the frequency + of the CPU that the task was executed on. + In this version, we use a simple linear scale derived from the + maximum frequency reported by CPUFreq. + Restricting tracked load to be scaled by the CPU's frequency + represents the consumption of possible compute capacity + (rather than consumption of actual instantaneous capacity as + normal) and allows the HMP migration's simple threshold + migration strategy to interact more predictably with CPUFreq's + asynchronous compute capacity changes. + +config SCHED_HMP_LITTLE_PACKING + bool "Small task packing for HMP" + depends on SCHED_HMP + default n + help + Allows the HMP Scheduler to pack small tasks into CPUs in the + smallest HMP domain. + Controlled by two sysfs files in sys/kernel/hmp. + packing_enable: 1 to enable, 0 to disable packing. Default 1. + packing_limit: runqueue load ratio where a RQ is considered + to be full. Default is NICE_0_LOAD * 9/8. + config NR_CPUS int "Maximum number of CPUs (2-32)" range 2 32 depends on SMP default "4" +config HOTPLUG_CPU + bool "Support for hot-pluggable CPUs" + depends on SMP + help + Say Y here to experiment with turning CPUs off and on. CPUs + can be controlled through /sys/devices/system/cpu. + source kernel/Kconfig.preempt config HZ @@ -229,6 +356,14 @@ endmenu +menu "Power management options" + +source "kernel/power/Kconfig" + +source "drivers/cpufreq/Kconfig" + +endmenu + source "net/Kconfig" source "drivers/Kconfig" diff -Nur linux-3.10.30/arch/arm64/boot/dts/Makefile linux-3.10.30-cubox-i/arch/arm64/boot/dts/Makefile --- linux-3.10.30/arch/arm64/boot/dts/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/boot/dts/Makefile 2014-03-08 20:32:56.000000000 +0100 @@ -1,4 +1,5 @@ -dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb foundation-v8.dtb +dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb foundation-v8.dtb \ + fvp-base-gicv2-psci.dtb targets += dtbs targets += $(dtb-y) diff -Nur linux-3.10.30/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts linux-3.10.30-cubox-i/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts --- linux-3.10.30/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/boot/dts/fvp-base-gicv2-psci.dts 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2013, ARM Limited. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/dts-v1/; + +/memreserve/ 0x80000000 0x00010000; + +/ { +}; + +/ { + model = "FVP Base"; + compatible = "arm,vfp-base", "arm,vexpress"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + chosen { }; + + aliases { + serial0 = &v2m_serial0; + serial1 = &v2m_serial1; + serial2 = &v2m_serial2; + serial3 = &v2m_serial3; + }; + + psci { + compatible = "arm,psci"; + method = "smc"; + cpu_suspend = <0xc4000001>; + cpu_off = <0x84000002>; + cpu_on = <0xc4000003>; + }; + + cpus { + #address-cells = <2>; + #size-cells = <0>; + + big0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a57", "arm,armv8"; + reg = <0x0 0x0>; + enable-method = "psci"; + clock-frequency = <1000000>; + }; + big1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a57", "arm,armv8"; + reg = <0x0 0x1>; + enable-method = "psci"; + clock-frequency = <1000000>; + }; + big2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a57", "arm,armv8"; + reg = <0x0 0x2>; + enable-method = "psci"; + clock-frequency = <1000000>; + }; + big3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a57", "arm,armv8"; + reg = <0x0 0x3>; + enable-method = "psci"; + clock-frequency = <1000000>; + }; + little0: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a53", "arm,armv8"; + reg = <0x0 0x100>; + enable-method = "psci"; + clock-frequency = <1000000>; + }; + little1: cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a53", "arm,armv8"; + reg = <0x0 0x101>; + enable-method = "psci"; + clock-frequency = <1000000>; + }; + little2: cpu@102 { + device_type = "cpu"; + compatible = "arm,cortex-a53", "arm,armv8"; + reg = <0x0 0x102>; + enable-method = "psci"; + clock-frequency = <1000000>; + }; + little3: cpu@103 { + device_type = "cpu"; + compatible = "arm,cortex-a53", "arm,armv8"; + reg = <0x0 0x103>; + enable-method = "psci"; + clock-frequency = <1000000>; + }; + + cpu-map { + cluster0 { + core0 { + cpu = <&big0>; + }; + core1 { + cpu = <&big1>; + }; + core2 { + cpu = <&big2>; + }; + core3 { + cpu = <&big3>; + }; + }; + cluster1 { + core0 { + cpu = <&little0>; + }; + core1 { + cpu = <&little1>; + }; + core2 { + cpu = <&little2>; + }; + core3 { + cpu = <&little3>; + }; + }; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x00000000 0x80000000 0 0x80000000>, + <0x00000008 0x80000000 0 0x80000000>; + }; + + gic: interrupt-controller@2f000000 { + compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x0 0x2f000000 0 0x10000>, + <0x0 0x2c000000 0 0x2000>, + <0x0 0x2c010000 0 0x2000>, + <0x0 0x2c02F000 0 0x2000>; + interrupts = <1 9 0xf04>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = <1 13 0xff01>, + <1 14 0xff01>, + <1 11 0xff01>, + <1 10 0xff01>; + clock-frequency = <100000000>; + }; + + timer@2a810000 { + compatible = "arm,armv7-timer-mem"; + reg = <0x0 0x2a810000 0x0 0x10000>; + clock-frequency = <100000000>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + frame@2a820000 { + frame-number = <0>; + interrupts = <0 25 4>; + reg = <0x0 0x2a820000 0x0 0x10000>; + }; + }; + + pmu { + compatible = "arm,armv8-pmuv3"; + interrupts = <0 60 4>, + <0 61 4>, + <0 62 4>, + <0 63 4>; + }; + + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0 0x08000000 0x04000000>, + <1 0 0 0x14000000 0x04000000>, + <2 0 0 0x18000000 0x04000000>, + <3 0 0 0x1c000000 0x04000000>, + <4 0 0 0x0c000000 0x04000000>, + <5 0 0 0x10000000 0x04000000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <0 0 0 &gic 0 0 4>, + <0 0 1 &gic 0 1 4>, + <0 0 2 &gic 0 2 4>, + <0 0 3 &gic 0 3 4>, + <0 0 4 &gic 0 4 4>, + <0 0 5 &gic 0 5 4>, + <0 0 6 &gic 0 6 4>, + <0 0 7 &gic 0 7 4>, + <0 0 8 &gic 0 8 4>, + <0 0 9 &gic 0 9 4>, + <0 0 10 &gic 0 10 4>, + <0 0 11 &gic 0 11 4>, + <0 0 12 &gic 0 12 4>, + <0 0 13 &gic 0 13 4>, + <0 0 14 &gic 0 14 4>, + <0 0 15 &gic 0 15 4>, + <0 0 16 &gic 0 16 4>, + <0 0 17 &gic 0 17 4>, + <0 0 18 &gic 0 18 4>, + <0 0 19 &gic 0 19 4>, + <0 0 20 &gic 0 20 4>, + <0 0 21 &gic 0 21 4>, + <0 0 22 &gic 0 22 4>, + <0 0 23 &gic 0 23 4>, + <0 0 24 &gic 0 24 4>, + <0 0 25 &gic 0 25 4>, + <0 0 26 &gic 0 26 4>, + <0 0 27 &gic 0 27 4>, + <0 0 28 &gic 0 28 4>, + <0 0 29 &gic 0 29 4>, + <0 0 30 &gic 0 30 4>, + <0 0 31 &gic 0 31 4>, + <0 0 32 &gic 0 32 4>, + <0 0 33 &gic 0 33 4>, + <0 0 34 &gic 0 34 4>, + <0 0 35 &gic 0 35 4>, + <0 0 36 &gic 0 36 4>, + <0 0 37 &gic 0 37 4>, + <0 0 38 &gic 0 38 4>, + <0 0 39 &gic 0 39 4>, + <0 0 40 &gic 0 40 4>, + <0 0 41 &gic 0 41 4>, + <0 0 42 &gic 0 42 4>; + + /include/ "rtsm_ve-motherboard.dtsi" + }; + + panels { + panel@0 { + compatible = "panel"; + mode = "XVGA"; + refresh = <60>; + xres = <1024>; + yres = <768>; + pixclock = <15748>; + left_margin = <152>; + right_margin = <48>; + upper_margin = <23>; + lower_margin = <3>; + hsync_len = <104>; + vsync_len = <4>; + sync = <0>; + vmode = "FB_VMODE_NONINTERLACED"; + tim2 = "TIM2_BCD", "TIM2_IPC"; + cntl = "CNTL_LCDTFT", "CNTL_BGR", "CNTL_LCDVCOMP(1)"; + caps = "CLCD_CAP_5551", "CLCD_CAP_565", "CLCD_CAP_888"; + bpp = <16>; + }; + }; +}; diff -Nur linux-3.10.30/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi linux-3.10.30-cubox-i/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi --- linux-3.10.30/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi 2014-03-08 20:32:56.000000000 +0100 @@ -182,6 +182,15 @@ interrupts = <14>; clocks = <&v2m_oscclk1>, <&v2m_clk24mhz>; clock-names = "clcdclk", "apb_pclk"; + mode = "XVGA"; + use_dma = <0>; + framebuffer = <0x18000000 0x00180000>; + }; + + virtio_block@0130000 { + compatible = "virtio,mmio"; + reg = <0x130000 0x200>; + interrupts = <42>; }; }; diff -Nur linux-3.10.30/arch/arm64/include/asm/cmpxchg.h linux-3.10.30-cubox-i/arch/arm64/include/asm/cmpxchg.h --- linux-3.10.30/arch/arm64/include/asm/cmpxchg.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/include/asm/cmpxchg.h 2014-03-08 20:32:56.000000000 +0100 @@ -158,17 +158,23 @@ return ret; } -#define cmpxchg(ptr,o,n) \ - ((__typeof__(*(ptr)))__cmpxchg_mb((ptr), \ - (unsigned long)(o), \ - (unsigned long)(n), \ - sizeof(*(ptr)))) +#define cmpxchg(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) __ret; \ + __ret = (__typeof__(*(ptr))) \ + __cmpxchg_mb((ptr), (unsigned long)(o), (unsigned long)(n), \ + sizeof(*(ptr))); \ + __ret; \ +}) -#define cmpxchg_local(ptr,o,n) \ - ((__typeof__(*(ptr)))__cmpxchg((ptr), \ - (unsigned long)(o), \ - (unsigned long)(n), \ - sizeof(*(ptr)))) +#define cmpxchg_local(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) __ret; \ + __ret = (__typeof__(*(ptr))) \ + __cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr))); \ + __ret; \ +}) #define cmpxchg64(ptr,o,n) cmpxchg((ptr),(o),(n)) #define cmpxchg64_local(ptr,o,n) cmpxchg_local((ptr),(o),(n)) diff -Nur linux-3.10.30/arch/arm64/include/asm/cpu_ops.h linux-3.10.30-cubox-i/arch/arm64/include/asm/cpu_ops.h --- linux-3.10.30/arch/arm64/include/asm/cpu_ops.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/include/asm/cpu_ops.h 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013 ARM Ltd. + * + * 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 . + */ +#ifndef __ASM_CPU_OPS_H +#define __ASM_CPU_OPS_H + +#include +#include + +struct device_node; + +/** + * struct cpu_operations - Callback operations for hotplugging CPUs. + * + * @name: Name of the property as appears in a devicetree cpu node's + * enable-method property. + * @cpu_init: Reads any data necessary for a specific enable-method from the + * devicetree, for a given cpu node and proposed logical id. + * @cpu_prepare: Early one-time preparation step for a cpu. If there is a + * mechanism for doing so, tests whether it is possible to boot + * the given CPU. + * @cpu_boot: Boots a cpu into the kernel. + * @cpu_postboot: Optionally, perform any post-boot cleanup or necesary + * synchronisation. Called from the cpu being booted. + * @cpu_disable: Prepares a cpu to die. May fail for some mechanism-specific + * reason, which will cause the hot unplug to be aborted. Called + * from the cpu to be killed. + * @cpu_die: Makes a cpu leave the kernel. Must not fail. Called from the + * cpu being killed. + */ +struct cpu_operations { + const char *name; + int (*cpu_init)(struct device_node *, unsigned int); + int (*cpu_prepare)(unsigned int); + int (*cpu_boot)(unsigned int); + void (*cpu_postboot)(void); +#ifdef CONFIG_HOTPLUG_CPU + int (*cpu_disable)(unsigned int cpu); + void (*cpu_die)(unsigned int cpu); +#endif +}; + +extern const struct cpu_operations *cpu_ops[NR_CPUS]; +extern int __init cpu_read_ops(struct device_node *dn, int cpu); +extern void __init cpu_read_bootcpu_ops(void); + +#endif /* ifndef __ASM_CPU_OPS_H */ diff -Nur linux-3.10.30/arch/arm64/include/asm/irq.h linux-3.10.30-cubox-i/arch/arm64/include/asm/irq.h --- linux-3.10.30/arch/arm64/include/asm/irq.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/include/asm/irq.h 2014-03-08 20:32:56.000000000 +0100 @@ -4,6 +4,7 @@ #include extern void (*handle_arch_irq)(struct pt_regs *); +extern void migrate_irqs(void); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); #endif diff -Nur linux-3.10.30/arch/arm64/include/asm/pgtable-3level-types.h linux-3.10.30-cubox-i/arch/arm64/include/asm/pgtable-3level-types.h --- linux-3.10.30/arch/arm64/include/asm/pgtable-3level-types.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/include/asm/pgtable-3level-types.h 2014-03-08 20:32:56.000000000 +0100 @@ -16,6 +16,8 @@ #ifndef __ASM_PGTABLE_3LEVEL_TYPES_H #define __ASM_PGTABLE_3LEVEL_TYPES_H +#include + typedef u64 pteval_t; typedef u64 pmdval_t; typedef u64 pgdval_t; diff -Nur linux-3.10.30/arch/arm64/include/asm/psci.h linux-3.10.30-cubox-i/arch/arm64/include/asm/psci.h --- linux-3.10.30/arch/arm64/include/asm/psci.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/include/asm/psci.h 2014-03-08 20:32:56.000000000 +0100 @@ -14,25 +14,6 @@ #ifndef __ASM_PSCI_H #define __ASM_PSCI_H -#define PSCI_POWER_STATE_TYPE_STANDBY 0 -#define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 - -struct psci_power_state { - u16 id; - u8 type; - u8 affinity_level; -}; - -struct psci_operations { - int (*cpu_suspend)(struct psci_power_state state, - unsigned long entry_point); - int (*cpu_off)(struct psci_power_state state); - int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); - int (*migrate)(unsigned long cpuid); -}; - -extern struct psci_operations psci_ops; - int psci_init(void); #endif /* __ASM_PSCI_H */ diff -Nur linux-3.10.30/arch/arm64/include/asm/smp.h linux-3.10.30-cubox-i/arch/arm64/include/asm/smp.h --- linux-3.10.30/arch/arm64/include/asm/smp.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/include/asm/smp.h 2014-03-08 20:32:56.000000000 +0100 @@ -60,21 +60,14 @@ void *stack; }; extern struct secondary_data secondary_data; -extern void secondary_holding_pen(void); -extern volatile unsigned long secondary_holding_pen_release; +extern void secondary_entry(void); extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); -struct device_node; +extern int __cpu_disable(void); -struct smp_enable_ops { - const char *name; - int (*init_cpu)(struct device_node *, int); - int (*prepare_cpu)(int); -}; - -extern const struct smp_enable_ops smp_spin_table_ops; -extern const struct smp_enable_ops smp_psci_ops; +extern void __cpu_die(unsigned int cpu); +extern void cpu_die(void); #endif /* ifndef __ASM_SMP_H */ diff -Nur linux-3.10.30/arch/arm64/include/asm/topology.h linux-3.10.30-cubox-i/arch/arm64/include/asm/topology.h --- linux-3.10.30/arch/arm64/include/asm/topology.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/include/asm/topology.h 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,73 @@ +#ifndef _ASM_ARM_TOPOLOGY_H +#define _ASM_ARM_TOPOLOGY_H + +#ifdef CONFIG_ARM_CPU_TOPOLOGY + +#include + +struct cputopo_arm { + int thread_id; + int core_id; + int socket_id; + cpumask_t thread_sibling; + cpumask_t core_sibling; +}; + +extern struct cputopo_arm cpu_topology[NR_CPUS]; + +#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id) +#define topology_core_id(cpu) (cpu_topology[cpu].core_id) +#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling) +#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling) + +#define mc_capable() (cpu_topology[0].socket_id != -1) +#define smt_capable() (cpu_topology[0].thread_id != -1) + +void init_cpu_topology(void); +void store_cpu_topology(unsigned int cpuid); +const struct cpumask *cpu_coregroup_mask(int cpu); +int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask); + +#ifdef CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE +/* Common values for CPUs */ +#ifndef SD_CPU_INIT +#define SD_CPU_INIT (struct sched_domain) { \ + .min_interval = 1, \ + .max_interval = 4, \ + .busy_factor = 64, \ + .imbalance_pct = 125, \ + .cache_nice_tries = 1, \ + .busy_idx = 2, \ + .idle_idx = 1, \ + .newidle_idx = 0, \ + .wake_idx = 0, \ + .forkexec_idx = 0, \ + \ + .flags = 0*SD_LOAD_BALANCE \ + | 1*SD_BALANCE_NEWIDLE \ + | 1*SD_BALANCE_EXEC \ + | 1*SD_BALANCE_FORK \ + | 0*SD_BALANCE_WAKE \ + | 1*SD_WAKE_AFFINE \ + | 0*SD_SHARE_CPUPOWER \ + | 0*SD_SHARE_PKG_RESOURCES \ + | 0*SD_SERIALIZE \ + , \ + .last_balance = jiffies, \ + .balance_interval = 1, \ +} +#endif +#endif /* CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE */ + +#else + +static inline void init_cpu_topology(void) { } +static inline void store_cpu_topology(unsigned int cpuid) { } +static inline int cluster_to_logical_mask(unsigned int socket_id, + cpumask_t *cluster_mask) { return -EINVAL; } + +#endif + +#include + +#endif /* _ASM_ARM_TOPOLOGY_H */ diff -Nur linux-3.10.30/arch/arm64/kernel/Makefile linux-3.10.30-cubox-i/arch/arm64/kernel/Makefile --- linux-3.10.30/arch/arm64/kernel/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/Makefile 2014-03-08 20:32:56.000000000 +0100 @@ -9,15 +9,16 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ - hyp-stub.o psci.o + hyp-stub.o psci.o cpu_ops.o arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o -arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o smp_psci.o +arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +arm64-obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m) diff -Nur linux-3.10.30/arch/arm64/kernel/cpu_ops.c linux-3.10.30-cubox-i/arch/arm64/kernel/cpu_ops.c --- linux-3.10.30/arch/arm64/kernel/cpu_ops.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/cpu_ops.c 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,99 @@ +/* + * CPU kernel entry/exit control + * + * Copyright (C) 2013 ARM Ltd. + * + * 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 . + */ + +#include +#include +#include +#include +#include + +extern const struct cpu_operations smp_spin_table_ops; +extern const struct cpu_operations cpu_psci_ops; + +const struct cpu_operations *cpu_ops[NR_CPUS]; + +static const struct cpu_operations *supported_cpu_ops[] __initconst = { +#ifdef CONFIG_SMP + &smp_spin_table_ops, + &cpu_psci_ops, +#endif + NULL, +}; + +static const struct cpu_operations * __init cpu_get_ops(const char *name) +{ + const struct cpu_operations **ops = supported_cpu_ops; + + while (*ops) { + if (!strcmp(name, (*ops)->name)) + return *ops; + + ops++; + } + + return NULL; +} + +/* + * Read a cpu's enable method from the device tree and record it in cpu_ops. + */ +int __init cpu_read_ops(struct device_node *dn, int cpu) +{ + const char *enable_method = of_get_property(dn, "enable-method", NULL); + if (!enable_method) { + /* + * The boot CPU may not have an enable method (e.g. when + * spin-table is used for secondaries). Don't warn spuriously. + */ + if (cpu != 0) + pr_err("%s: missing enable-method property\n", + dn->full_name); + return -ENOENT; + } + + cpu_ops[cpu] = cpu_get_ops(enable_method); + if (!cpu_ops[cpu]) { + pr_warn("%s: unsupported enable-method property: %s\n", + dn->full_name, enable_method); + return -EOPNOTSUPP; + } + + return 0; +} + +void __init cpu_read_bootcpu_ops(void) +{ + struct device_node *dn = NULL; + u64 mpidr = cpu_logical_map(0); + + while ((dn = of_find_node_by_type(dn, "cpu"))) { + u64 hwid; + const __be32 *prop; + + prop = of_get_property(dn, "reg", NULL); + if (!prop) + continue; + + hwid = of_read_number(prop, of_n_addr_cells(dn)); + if (hwid == mpidr) { + cpu_read_ops(dn, 0); + of_node_put(dn); + return; + } + } +} diff -Nur linux-3.10.30/arch/arm64/kernel/cputable.c linux-3.10.30-cubox-i/arch/arm64/kernel/cputable.c --- linux-3.10.30/arch/arm64/kernel/cputable.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/cputable.c 2014-03-08 20:32:56.000000000 +0100 @@ -22,7 +22,7 @@ extern unsigned long __cpu_setup(void); -struct cpu_info __initdata cpu_table[] = { +struct cpu_info cpu_table[] = { { .cpu_id_val = 0x000f0000, .cpu_id_mask = 0x000f0000, diff -Nur linux-3.10.30/arch/arm64/kernel/head.S linux-3.10.30-cubox-i/arch/arm64/kernel/head.S --- linux-3.10.30/arch/arm64/kernel/head.S 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/head.S 2014-03-08 20:32:56.000000000 +0100 @@ -217,7 +217,6 @@ .quad PAGE_OFFSET #ifdef CONFIG_SMP - .pushsection .smp.pen.text, "ax" .align 3 1: .quad . .quad secondary_holding_pen_release @@ -242,7 +241,16 @@ wfe b pen ENDPROC(secondary_holding_pen) - .popsection + + /* + * Secondary entry point that jumps straight into the kernel. Only to + * be used where CPUs are brought online dynamically by the kernel. + */ +ENTRY(secondary_entry) + bl __calc_phys_offset // x2=phys offset + bl el2_setup // Drop to EL1 + b secondary_startup +ENDPROC(secondary_entry) ENTRY(secondary_startup) /* diff -Nur linux-3.10.30/arch/arm64/kernel/irq.c linux-3.10.30-cubox-i/arch/arm64/kernel/irq.c --- linux-3.10.30/arch/arm64/kernel/irq.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/irq.c 2014-03-08 20:32:56.000000000 +0100 @@ -81,3 +81,64 @@ if (!handle_arch_irq) panic("No interrupt controller found."); } + +#ifdef CONFIG_HOTPLUG_CPU +static bool migrate_one_irq(struct irq_desc *desc) +{ + struct irq_data *d = irq_desc_get_irq_data(desc); + const struct cpumask *affinity = d->affinity; + struct irq_chip *c; + bool ret = false; + + /* + * If this is a per-CPU interrupt, or the affinity does not + * include this CPU, then we have nothing to do. + */ + if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity)) + return false; + + if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) { + affinity = cpu_online_mask; + ret = true; + } + + c = irq_data_get_irq_chip(d); + if (!c->irq_set_affinity) + pr_debug("IRQ%u: unable to set affinity\n", d->irq); + else if (c->irq_set_affinity(d, affinity, true) == IRQ_SET_MASK_OK && ret) + cpumask_copy(d->affinity, affinity); + + return ret; +} + +/* + * The current CPU has been marked offline. Migrate IRQs off this CPU. + * If the affinity settings do not allow other CPUs, force them onto any + * available CPU. + * + * Note: we must iterate over all IRQs, whether they have an attached + * action structure or not, as we need to get chained interrupts too. + */ +void migrate_irqs(void) +{ + unsigned int i; + struct irq_desc *desc; + unsigned long flags; + + local_irq_save(flags); + + for_each_irq_desc(i, desc) { + bool affinity_broken; + + raw_spin_lock(&desc->lock); + affinity_broken = migrate_one_irq(desc); + raw_spin_unlock(&desc->lock); + + if (affinity_broken) + pr_warn_ratelimited("IRQ%u no longer affine to CPU%u\n", + i, smp_processor_id()); + } + + local_irq_restore(flags); +} +#endif /* CONFIG_HOTPLUG_CPU */ diff -Nur linux-3.10.30/arch/arm64/kernel/process.c linux-3.10.30-cubox-i/arch/arm64/kernel/process.c --- linux-3.10.30/arch/arm64/kernel/process.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/process.c 2014-03-08 20:32:56.000000000 +0100 @@ -102,6 +102,13 @@ local_irq_enable(); } +#ifdef CONFIG_HOTPLUG_CPU +void arch_cpu_idle_dead(void) +{ + cpu_die(); +} +#endif + void machine_shutdown(void) { #ifdef CONFIG_SMP diff -Nur linux-3.10.30/arch/arm64/kernel/psci.c linux-3.10.30-cubox-i/arch/arm64/kernel/psci.c --- linux-3.10.30/arch/arm64/kernel/psci.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/psci.c 2014-03-08 20:32:56.000000000 +0100 @@ -17,12 +17,32 @@ #include #include +#include #include +#include #include #include +#include -struct psci_operations psci_ops; +#define PSCI_POWER_STATE_TYPE_STANDBY 0 +#define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 + +struct psci_power_state { + u16 id; + u8 type; + u8 affinity_level; +}; + +struct psci_operations { + int (*cpu_suspend)(struct psci_power_state state, + unsigned long entry_point); + int (*cpu_off)(struct psci_power_state state); + int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); + int (*migrate)(unsigned long cpuid); +}; + +static struct psci_operations psci_ops; static int (*invoke_psci_fn)(u64, u64, u64, u64); @@ -209,3 +229,68 @@ of_node_put(np); return err; } + +#ifdef CONFIG_SMP + +static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu) +{ + return 0; +} + +static int __init cpu_psci_cpu_prepare(unsigned int cpu) +{ + if (!psci_ops.cpu_on) { + pr_err("no cpu_on method, not booting CPU%d\n", cpu); + return -ENODEV; + } + + return 0; +} + +static int cpu_psci_cpu_boot(unsigned int cpu) +{ + int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_entry)); + if (err) + pr_err("psci: failed to boot CPU%d (%d)\n", cpu, err); + + return err; +} + +#ifdef CONFIG_HOTPLUG_CPU +static int cpu_psci_cpu_disable(unsigned int cpu) +{ + /* Fail early if we don't have CPU_OFF support */ + if (!psci_ops.cpu_off) + return -EOPNOTSUPP; + return 0; +} + +static void cpu_psci_cpu_die(unsigned int cpu) +{ + int ret; + /* + * There are no known implementations of PSCI actually using the + * power state field, pass a sensible default for now. + */ + struct psci_power_state state = { + .type = PSCI_POWER_STATE_TYPE_POWER_DOWN, + }; + + ret = psci_ops.cpu_off(state); + + pr_crit("psci: unable to power off CPU%u (%d)\n", cpu, ret); +} +#endif + +const struct cpu_operations cpu_psci_ops = { + .name = "psci", + .cpu_init = cpu_psci_cpu_init, + .cpu_prepare = cpu_psci_cpu_prepare, + .cpu_boot = cpu_psci_cpu_boot, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_disable = cpu_psci_cpu_disable, + .cpu_die = cpu_psci_cpu_die, +#endif +}; + +#endif diff -Nur linux-3.10.30/arch/arm64/kernel/setup.c linux-3.10.30-cubox-i/arch/arm64/kernel/setup.c --- linux-3.10.30/arch/arm64/kernel/setup.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/setup.c 2014-03-08 20:32:56.000000000 +0100 @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,11 @@ printk("%s", buf); } +bool arch_match_cpu_phys_id(int cpu, u64 phys_id) +{ + return phys_id == cpu_logical_map(cpu); +} + static void __init setup_processor(void) { struct cpu_info *cpu_info; @@ -269,6 +275,7 @@ psci_init(); cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; + cpu_read_bootcpu_ops(); #ifdef CONFIG_SMP smp_init_cpus(); #endif diff -Nur linux-3.10.30/arch/arm64/kernel/smp.c linux-3.10.30-cubox-i/arch/arm64/kernel/smp.c --- linux-3.10.30/arch/arm64/kernel/smp.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/smp.c 2014-03-08 20:32:56.000000000 +0100 @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -48,13 +49,15 @@ #include #include +#define CREATE_TRACE_POINTS +#include + /* * as from 2.5, kernels no longer have an init_tasks structure * so we need some other way of telling a new secondary core * where to place its SVC stack */ struct secondary_data secondary_data; -volatile unsigned long secondary_holding_pen_release = INVALID_HWID; enum ipi_msg_type { IPI_RESCHEDULE, @@ -63,61 +66,16 @@ IPI_CPU_STOP, }; -static DEFINE_RAW_SPINLOCK(boot_lock); - -/* - * Write secondary_holding_pen_release in a way that is guaranteed to be - * visible to all observers, irrespective of whether they're taking part - * in coherency or not. This is necessary for the hotplug code to work - * reliably. - */ -static void __cpuinit write_pen_release(u64 val) -{ - void *start = (void *)&secondary_holding_pen_release; - unsigned long size = sizeof(secondary_holding_pen_release); - - secondary_holding_pen_release = val; - __flush_dcache_area(start, size); -} - /* * Boot a secondary CPU, and assign it the specified idle task. * This also gives us the initial stack to use for this CPU. */ static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) { - unsigned long timeout; + if (cpu_ops[cpu]->cpu_boot) + return cpu_ops[cpu]->cpu_boot(cpu); - /* - * Set synchronisation state between this boot processor - * and the secondary one - */ - raw_spin_lock(&boot_lock); - - /* - * Update the pen release flag. - */ - write_pen_release(cpu_logical_map(cpu)); - - /* - * Send an event, causing the secondaries to read pen_release. - */ - sev(); - - timeout = jiffies + (1 * HZ); - while (time_before(jiffies, timeout)) { - if (secondary_holding_pen_release == INVALID_HWID) - break; - udelay(10); - } - - /* - * Now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ - raw_spin_unlock(&boot_lock); - - return secondary_holding_pen_release != INVALID_HWID ? -ENOSYS : 0; + return -EOPNOTSUPP; } static DECLARE_COMPLETION(cpu_running); @@ -158,6 +116,11 @@ return ret; } +static void __cpuinit smp_store_cpu_info(unsigned int cpuid) +{ + store_cpu_topology(cpuid); +} + /* * This is the secondary CPU boot entry. We're using this CPUs * idle thread stack, but a set of temporary page tables. @@ -187,17 +150,15 @@ preempt_disable(); trace_hardirqs_off(); - /* - * Let the primary processor know we're out of the - * pen, then head off into the C entry point - */ - write_pen_release(INVALID_HWID); + if (cpu_ops[cpu]->cpu_postboot) + cpu_ops[cpu]->cpu_postboot(); + + smp_store_cpu_info(cpu); /* - * Synchronise with the boot thread. + * Enable GIC and timers. */ - raw_spin_lock(&boot_lock); - raw_spin_unlock(&boot_lock); + notify_cpu_starting(cpu); /* * OK, now it's safe to let the boot CPU continue. Wait for @@ -207,11 +168,6 @@ set_cpu_online(cpu, true); complete(&cpu_running); - /* - * Enable GIC and timers. - */ - notify_cpu_starting(cpu); - local_irq_enable(); local_fiq_enable(); @@ -221,43 +177,117 @@ cpu_startup_entry(CPUHP_ONLINE); } -void __init smp_cpus_done(unsigned int max_cpus) +#ifdef CONFIG_HOTPLUG_CPU +static int op_cpu_disable(unsigned int cpu) { - unsigned long bogosum = loops_per_jiffy * num_online_cpus(); + /* + * If we don't have a cpu_die method, abort before we reach the point + * of no return. CPU0 may not have an cpu_ops, so test for it. + */ + if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_die) + return -EOPNOTSUPP; - pr_info("SMP: Total of %d processors activated (%lu.%02lu BogoMIPS).\n", - num_online_cpus(), bogosum / (500000/HZ), - (bogosum / (5000/HZ)) % 100); + /* + * We may need to abort a hot unplug for some other mechanism-specific + * reason. + */ + if (cpu_ops[cpu]->cpu_disable) + return cpu_ops[cpu]->cpu_disable(cpu); + + return 0; } -void __init smp_prepare_boot_cpu(void) +/* + * __cpu_disable runs on the processor to be shutdown. + */ +int __cpu_disable(void) { -} + unsigned int cpu = smp_processor_id(); + int ret; -static void (*smp_cross_call)(const struct cpumask *, unsigned int); + ret = op_cpu_disable(cpu); + if (ret) + return ret; -static const struct smp_enable_ops *enable_ops[] __initconst = { - &smp_spin_table_ops, - &smp_psci_ops, - NULL, -}; + /* + * Take this CPU offline. Once we clear this, we can't return, + * and we must not schedule until we're ready to give up the cpu. + */ + set_cpu_online(cpu, false); -static const struct smp_enable_ops *smp_enable_ops[NR_CPUS]; + /* + * OK - migrate IRQs away from this CPU + */ + migrate_irqs(); -static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name) -{ - const struct smp_enable_ops **ops = enable_ops; + /* + * Remove this CPU from the vm mask set of all processes. + */ + clear_tasks_mm_cpumask(cpu); + + return 0; +} - while (*ops) { - if (!strcmp(name, (*ops)->name)) - return *ops; +static DECLARE_COMPLETION(cpu_died); - ops++; +/* + * called on the thread which is asking for a CPU to be shutdown - + * waits until shutdown has completed, or it is timed out. + */ +void __cpu_die(unsigned int cpu) +{ + if (!wait_for_completion_timeout(&cpu_died, msecs_to_jiffies(5000))) { + pr_crit("CPU%u: cpu didn't die\n", cpu); + return; } + pr_notice("CPU%u: shutdown\n", cpu); +} + +/* + * Called from the idle thread for the CPU which has been shutdown. + * + * Note that we disable IRQs here, but do not re-enable them + * before returning to the caller. This is also the behaviour + * of the other hotplug-cpu capable cores, so presumably coming + * out of idle fixes this. + */ +void cpu_die(void) +{ + unsigned int cpu = smp_processor_id(); - return NULL; + idle_task_exit(); + + local_irq_disable(); + + /* Tell __cpu_die() that this CPU is now safe to dispose of */ + complete(&cpu_died); + + /* + * Actually shutdown the CPU. This must never fail. The specific hotplug + * mechanism must perform all required cache maintenance to ensure that + * no dirty lines are lost in the process of shutting down the CPU. + */ + cpu_ops[cpu]->cpu_die(cpu); + + BUG(); +} +#endif + +void __init smp_cpus_done(unsigned int max_cpus) +{ + unsigned long bogosum = loops_per_jiffy * num_online_cpus(); + + pr_info("SMP: Total of %d processors activated (%lu.%02lu BogoMIPS).\n", + num_online_cpus(), bogosum / (500000/HZ), + (bogosum / (5000/HZ)) % 100); +} + +void __init smp_prepare_boot_cpu(void) +{ } +static void (*smp_cross_call)(const struct cpumask *, unsigned int); + /* * Enumerate the possible CPU set from the device tree and build the * cpu logical map array containing MPIDR values related to logical @@ -265,9 +295,8 @@ */ void __init smp_init_cpus(void) { - const char *enable_method; struct device_node *dn = NULL; - int i, cpu = 1; + unsigned int i, cpu = 1; bool bootcpu_valid = false; while ((dn = of_find_node_by_type(dn, "cpu"))) { @@ -336,25 +365,10 @@ if (cpu >= NR_CPUS) goto next; - /* - * We currently support only the "spin-table" enable-method. - */ - enable_method = of_get_property(dn, "enable-method", NULL); - if (!enable_method) { - pr_err("%s: missing enable-method property\n", - dn->full_name); + if (cpu_read_ops(dn, cpu) != 0) goto next; - } - - smp_enable_ops[cpu] = smp_get_enable_ops(enable_method); - if (!smp_enable_ops[cpu]) { - pr_err("%s: invalid enable-method property: %s\n", - dn->full_name, enable_method); - goto next; - } - - if (smp_enable_ops[cpu]->init_cpu(dn, cpu)) + if (cpu_ops[cpu]->cpu_init(dn, cpu)) goto next; pr_debug("cpu logical map 0x%llx\n", hwid); @@ -384,8 +398,13 @@ void __init smp_prepare_cpus(unsigned int max_cpus) { - int cpu, err; - unsigned int ncores = num_possible_cpus(); + int err; + unsigned int cpu, ncores = num_possible_cpus(); + + init_cpu_topology(); + + smp_store_cpu_info(smp_processor_id()); + /* * are we trying to boot more cores than exist? @@ -412,10 +431,10 @@ if (cpu == smp_processor_id()) continue; - if (!smp_enable_ops[cpu]) + if (!cpu_ops[cpu]) continue; - err = smp_enable_ops[cpu]->prepare_cpu(cpu); + err = cpu_ops[cpu]->cpu_prepare(cpu); if (err) continue; diff -Nur linux-3.10.30/arch/arm64/kernel/smp_psci.c linux-3.10.30-cubox-i/arch/arm64/kernel/smp_psci.c --- linux-3.10.30/arch/arm64/kernel/smp_psci.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/smp_psci.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,53 +0,0 @@ -/* - * PSCI SMP initialisation - * - * Copyright (C) 2013 ARM Ltd. - * - * 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 . - */ - -#include -#include -#include - -#include -#include - -static int __init smp_psci_init_cpu(struct device_node *dn, int cpu) -{ - return 0; -} - -static int __init smp_psci_prepare_cpu(int cpu) -{ - int err; - - if (!psci_ops.cpu_on) { - pr_err("psci: no cpu_on method, not booting CPU%d\n", cpu); - return -ENODEV; - } - - err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_holding_pen)); - if (err) { - pr_err("psci: failed to boot CPU%d (%d)\n", cpu, err); - return err; - } - - return 0; -} - -const struct smp_enable_ops smp_psci_ops __initconst = { - .name = "psci", - .init_cpu = smp_psci_init_cpu, - .prepare_cpu = smp_psci_prepare_cpu, -}; diff -Nur linux-3.10.30/arch/arm64/kernel/smp_spin_table.c linux-3.10.30-cubox-i/arch/arm64/kernel/smp_spin_table.c --- linux-3.10.30/arch/arm64/kernel/smp_spin_table.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/smp_spin_table.c 2014-03-08 20:32:56.000000000 +0100 @@ -16,15 +16,39 @@ * along with this program. If not, see . */ +#include #include #include #include #include +#include +#include +#include + +extern void secondary_holding_pen(void); +volatile unsigned long secondary_holding_pen_release = INVALID_HWID; static phys_addr_t cpu_release_addr[NR_CPUS]; +static DEFINE_RAW_SPINLOCK(boot_lock); -static int __init smp_spin_table_init_cpu(struct device_node *dn, int cpu) +/* + * Write secondary_holding_pen_release in a way that is guaranteed to be + * visible to all observers, irrespective of whether they're taking part + * in coherency or not. This is necessary for the hotplug code to work + * reliably. + */ +static void write_pen_release(u64 val) +{ + void *start = (void *)&secondary_holding_pen_release; + unsigned long size = sizeof(secondary_holding_pen_release); + + secondary_holding_pen_release = val; + __flush_dcache_area(start, size); +} + + +static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu) { /* * Determine the address from which the CPU is polling. @@ -40,7 +64,7 @@ return 0; } -static int __init smp_spin_table_prepare_cpu(int cpu) +static int smp_spin_table_cpu_prepare(unsigned int cpu) { void **release_addr; @@ -59,8 +83,60 @@ return 0; } -const struct smp_enable_ops smp_spin_table_ops __initconst = { +static int smp_spin_table_cpu_boot(unsigned int cpu) +{ + unsigned long timeout; + + /* + * Set synchronisation state between this boot processor + * and the secondary one + */ + raw_spin_lock(&boot_lock); + + /* + * Update the pen release flag. + */ + write_pen_release(cpu_logical_map(cpu)); + + /* + * Send an event, causing the secondaries to read pen_release. + */ + sev(); + + timeout = jiffies + (1 * HZ); + while (time_before(jiffies, timeout)) { + if (secondary_holding_pen_release == INVALID_HWID) + break; + udelay(10); + } + + /* + * Now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ + raw_spin_unlock(&boot_lock); + + return secondary_holding_pen_release != INVALID_HWID ? -ENOSYS : 0; +} + +void smp_spin_table_cpu_postboot(void) +{ + /* + * Let the primary processor know we're out of the pen. + */ + write_pen_release(INVALID_HWID); + + /* + * Synchronise with the boot thread. + */ + raw_spin_lock(&boot_lock); + raw_spin_unlock(&boot_lock); +} + +const struct cpu_operations smp_spin_table_ops = { .name = "spin-table", - .init_cpu = smp_spin_table_init_cpu, - .prepare_cpu = smp_spin_table_prepare_cpu, + .cpu_init = smp_spin_table_cpu_init, + .cpu_prepare = smp_spin_table_cpu_prepare, + .cpu_boot = smp_spin_table_cpu_boot, + .cpu_postboot = smp_spin_table_cpu_postboot, }; diff -Nur linux-3.10.30/arch/arm64/kernel/topology.c linux-3.10.30-cubox-i/arch/arm64/kernel/topology.c --- linux-3.10.30/arch/arm64/kernel/topology.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/topology.c 2014-03-08 20:32:56.000000000 +0100 @@ -0,0 +1,537 @@ +/* + * arch/arm64/kernel/topology.c + * + * Copyright (C) 2011,2013 Linaro Limited. + * Written by: Vincent Guittot + * + * based on arch/sh/kernel/topology.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * cpu power scale management + */ + +/* + * cpu power table + * This per cpu data structure describes the relative capacity of each core. + * On a heteregenous system, cores don't have the same computation capacity + * and we reflect that difference in the cpu_power field so the scheduler can + * take this difference into account during load balance. A per cpu structure + * is preferred because each CPU updates its own cpu_power field during the + * load balance except for idle cores. One idle core is selected to run the + * rebalance_domains for all idle cores and the cpu_power can be updated + * during this sequence. + */ +static DEFINE_PER_CPU(unsigned long, cpu_scale); + +unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu) +{ + return per_cpu(cpu_scale, cpu); +} + +static void set_power_scale(unsigned int cpu, unsigned long power) +{ + per_cpu(cpu_scale, cpu) = power; +} + +#ifdef CONFIG_OF +struct cpu_efficiency { + const char *compatible; + unsigned long efficiency; +}; + +/* + * Table of relative efficiency of each processors + * The efficiency value must fit in 20bit and the final + * cpu_scale value must be in the range + * 0 < cpu_scale < 3*SCHED_POWER_SCALE/2 + * in order to return at most 1 when DIV_ROUND_CLOSEST + * is used to compute the capacity of a CPU. + * Processors that are not defined in the table, + * use the default SCHED_POWER_SCALE value for cpu_scale. + */ +static const struct cpu_efficiency table_efficiency[] = { + { "arm,cortex-a57", 3891 }, + { "arm,cortex-a53", 2048 }, + { NULL, }, +}; + +static unsigned long *__cpu_capacity; +#define cpu_capacity(cpu) __cpu_capacity[cpu] + +static unsigned long middle_capacity = 1; +static int cluster_id; + +static int __init get_cpu_for_node(struct device_node *node) +{ + struct device_node *cpu_node; + int cpu; + + cpu_node = of_parse_phandle(node, "cpu", 0); + if (!cpu_node) { + pr_crit("%s: Unable to parse CPU phandle\n", node->full_name); + return -1; + } + + for_each_possible_cpu(cpu) { + if (of_get_cpu_node(cpu, NULL) == cpu_node) + return cpu; + } + + pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name); + return -1; +} + +static void __init parse_core(struct device_node *core, int core_id) +{ + char name[10]; + bool leaf = true; + int i, cpu; + struct device_node *t; + + i = 0; + do { + snprintf(name, sizeof(name), "thread%d", i); + t = of_get_child_by_name(core, name); + if (t) { + leaf = false; + cpu = get_cpu_for_node(t); + if (cpu) { + pr_info("CPU%d: socket %d core %d thread %d\n", + cpu, cluster_id, core_id, i); + cpu_topology[cpu].socket_id = cluster_id; + cpu_topology[cpu].core_id = core_id; + cpu_topology[cpu].thread_id = i; + } else { + pr_err("%s: Can't get CPU for thread\n", + t->full_name); + } + } + i++; + } while (t); + + cpu = get_cpu_for_node(core); + if (cpu >= 0) { + if (!leaf) { + pr_err("%s: Core has both threads and CPU\n", + core->full_name); + return; + } + + pr_info("CPU%d: socket %d core %d\n", + cpu, cluster_id, core_id); + cpu_topology[cpu].socket_id = cluster_id; + cpu_topology[cpu].core_id = core_id; + } else if (leaf) { + pr_err("%s: Can't get CPU for leaf core\n", core->full_name); + } +} + +static void __init parse_cluster(struct device_node *cluster) +{ + char name[10]; + bool leaf = true; + bool has_cores = false; + struct device_node *c; + int core_id = 0; + int i; + + /* + * First check for child clusters; we currently ignore any + * information about the nesting of clusters and present the + * scheduler with a flat list of them. + */ + i = 0; + do { + snprintf(name, sizeof(name), "cluster%d", i); + c = of_get_child_by_name(cluster, name); + if (c) { + parse_cluster(c); + leaf = false; + } + i++; + } while (c); + + /* Now check for cores */ + i = 0; + do { + snprintf(name, sizeof(name), "core%d", i); + c = of_get_child_by_name(cluster, name); + if (c) { + has_cores = true; + + if (leaf) + parse_core(c, core_id++); + else + pr_err("%s: Non-leaf cluster with core %s\n", + cluster->full_name, name); + } + i++; + } while (c); + + if (leaf && !has_cores) + pr_warn("%s: empty cluster\n", cluster->full_name); + + if (leaf) + cluster_id++; +} + +/* + * Iterate all CPUs' descriptor in DT and compute the efficiency + * (as per table_efficiency). Also calculate a middle efficiency + * as close as possible to (max{eff_i} - min{eff_i}) / 2 + * This is later used to scale the cpu_power field such that an + * 'average' CPU is of middle power. Also see the comments near + * table_efficiency[] and update_cpu_power(). + */ +static void __init parse_dt_topology(void) +{ + const struct cpu_efficiency *cpu_eff; + struct device_node *cn = NULL; + unsigned long min_capacity = (unsigned long)(-1); + unsigned long max_capacity = 0; + unsigned long capacity = 0; + int alloc_size, cpu; + + alloc_size = nr_cpu_ids * sizeof(*__cpu_capacity); + __cpu_capacity = kzalloc(alloc_size, GFP_NOWAIT); + + cn = of_find_node_by_path("/cpus"); + if (!cn) { + pr_err("No CPU information found in DT\n"); + return; + } + + /* + * If topology is provided as a cpu-map it is essentially a + * root cluster. + */ + cn = of_find_node_by_name(cn, "cpu-map"); + if (!cn) + return; + parse_cluster(cn); + + for_each_possible_cpu(cpu) { + const u32 *rate; + int len; + + /* Too early to use cpu->of_node */ + cn = of_get_cpu_node(cpu, NULL); + if (!cn) { + pr_err("Missing device node for CPU %d\n", cpu); + continue; + } + + /* check if the cpu is marked as "disabled", if so ignore */ + if (!of_device_is_available(cn)) + continue; + + for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++) + if (of_device_is_compatible(cn, cpu_eff->compatible)) + break; + + if (cpu_eff->compatible == NULL) { + pr_warn("%s: Unknown CPU type\n", cn->full_name); + continue; + } + + rate = of_get_property(cn, "clock-frequency", &len); + if (!rate || len != 4) { + pr_err("%s: Missing clock-frequency property\n", + cn->full_name); + continue; + } + + capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency; + + /* Save min capacity of the system */ + if (capacity < min_capacity) + min_capacity = capacity; + + /* Save max capacity of the system */ + if (capacity > max_capacity) + max_capacity = capacity; + + cpu_capacity(cpu) = capacity; + } + + /* If min and max capacities are equal we bypass the update of the + * cpu_scale because all CPUs have the same capacity. Otherwise, we + * compute a middle_capacity factor that will ensure that the capacity + * of an 'average' CPU of the system will be as close as possible to + * SCHED_POWER_SCALE, which is the default value, but with the + * constraint explained near table_efficiency[]. + */ + if (min_capacity == max_capacity) + return; + else if (4 * max_capacity < (3 * (max_capacity + min_capacity))) + middle_capacity = (min_capacity + max_capacity) + >> (SCHED_POWER_SHIFT+1); + else + middle_capacity = ((max_capacity / 3) + >> (SCHED_POWER_SHIFT-1)) + 1; + +} + +/* + * Look for a customed capacity of a CPU in the cpu_topo_data table during the + * boot. The update of all CPUs is in O(n^2) for heteregeneous system but the + * function returns directly for SMP system. + */ +static void update_cpu_power(unsigned int cpu) +{ + if (!cpu_capacity(cpu)) + return; + + set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity); + + pr_info("CPU%u: update cpu_power %lu\n", + cpu, arch_scale_freq_power(NULL, cpu)); +} + +#else +static inline void parse_dt_topology(void) {} +static inline void update_cpu_power(unsigned int cpuid) {} +#endif + +/* + * cpu topology table + */ +struct cputopo_arm cpu_topology[NR_CPUS]; +EXPORT_SYMBOL_GPL(cpu_topology); + +const struct cpumask *cpu_coregroup_mask(int cpu) +{ + return &cpu_topology[cpu].core_sibling; +} + +static void update_siblings_masks(unsigned int cpuid) +{ + struct cputopo_arm *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; + int cpu; + + /* update core and thread sibling masks */ + for_each_possible_cpu(cpu) { + cpu_topo = &cpu_topology[cpu]; + + if (cpuid_topo->socket_id != cpu_topo->socket_id) + continue; + + cpumask_set_cpu(cpuid, &cpu_topo->core_sibling); + if (cpu != cpuid) + cpumask_set_cpu(cpu, &cpuid_topo->core_sibling); + + if (cpuid_topo->core_id != cpu_topo->core_id) + continue; + + cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling); + if (cpu != cpuid) + cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling); + } + smp_wmb(); +} + +void store_cpu_topology(unsigned int cpuid) +{ + struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid]; + + /* DT should have been parsed by the time we get here */ + if (cpuid_topo->core_id == -1) + pr_info("CPU%u: No topology information configured\n", cpuid); + else + update_siblings_masks(cpuid); + + update_cpu_power(cpuid); +} + +#ifdef CONFIG_SCHED_HMP + +/* + * Retrieve logical cpu index corresponding to a given MPIDR[23:0] + * - mpidr: MPIDR[23:0] to be used for the look-up + * + * Returns the cpu logical index or -EINVAL on look-up error + */ +static inline int get_logical_index(u32 mpidr) +{ + int cpu; + for (cpu = 0; cpu < nr_cpu_ids; cpu++) + if (cpu_logical_map(cpu) == mpidr) + return cpu; + return -EINVAL; +} + +static const char * const little_cores[] = { + "arm,cortex-a53", + NULL, +}; + +static bool is_little_cpu(struct device_node *cn) +{ + const char * const *lc; + for (lc = little_cores; *lc; lc++) + if (of_device_is_compatible(cn, *lc)) + return true; + return false; +} + +void __init arch_get_fast_and_slow_cpus(struct cpumask *fast, + struct cpumask *slow) +{ + struct device_node *cn = NULL; + int cpu; + + cpumask_clear(fast); + cpumask_clear(slow); + + /* + * Use the config options if they are given. This helps testing + * HMP scheduling on systems without a big.LITTLE architecture. + */ + if (strlen(CONFIG_HMP_FAST_CPU_MASK) && strlen(CONFIG_HMP_SLOW_CPU_MASK)) { + if (cpulist_parse(CONFIG_HMP_FAST_CPU_MASK, fast)) + WARN(1, "Failed to parse HMP fast cpu mask!\n"); + if (cpulist_parse(CONFIG_HMP_SLOW_CPU_MASK, slow)) + WARN(1, "Failed to parse HMP slow cpu mask!\n"); + return; + } + + /* + * Else, parse device tree for little cores. + */ + while ((cn = of_find_node_by_type(cn, "cpu"))) { + + const u32 *mpidr; + int len; + + mpidr = of_get_property(cn, "reg", &len); + if (!mpidr || len != 8) { + pr_err("%s missing reg property\n", cn->full_name); + continue; + } + + cpu = get_logical_index(be32_to_cpup(mpidr+1)); + if (cpu == -EINVAL) { + pr_err("couldn't get logical index for mpidr %x\n", + be32_to_cpup(mpidr+1)); + break; + } + + if (is_little_cpu(cn)) + cpumask_set_cpu(cpu, slow); + else + cpumask_set_cpu(cpu, fast); + } + + if (!cpumask_empty(fast) && !cpumask_empty(slow)) + return; + + /* + * We didn't find both big and little cores so let's call all cores + * fast as this will keep the system running, with all cores being + * treated equal. + */ + cpumask_setall(fast); + cpumask_clear(slow); +} + +struct cpumask hmp_slow_cpu_mask; + +void __init arch_get_hmp_domains(struct list_head *hmp_domains_list) +{ + struct cpumask hmp_fast_cpu_mask; + struct hmp_domain *domain; + + arch_get_fast_and_slow_cpus(&hmp_fast_cpu_mask, &hmp_slow_cpu_mask); + + /* + * Initialize hmp_domains + * Must be ordered with respect to compute capacity. + * Fastest domain at head of list. + */ + if(!cpumask_empty(&hmp_slow_cpu_mask)) { + domain = (struct hmp_domain *) + kmalloc(sizeof(struct hmp_domain), GFP_KERNEL); + cpumask_copy(&domain->possible_cpus, &hmp_slow_cpu_mask); + cpumask_and(&domain->cpus, cpu_online_mask, &domain->possible_cpus); + list_add(&domain->hmp_domains, hmp_domains_list); + } + domain = (struct hmp_domain *) + kmalloc(sizeof(struct hmp_domain), GFP_KERNEL); + cpumask_copy(&domain->possible_cpus, &hmp_fast_cpu_mask); + cpumask_and(&domain->cpus, cpu_online_mask, &domain->possible_cpus); + list_add(&domain->hmp_domains, hmp_domains_list); +} +#endif /* CONFIG_SCHED_HMP */ + +/* + * cluster_to_logical_mask - return cpu logical mask of CPUs in a cluster + * @socket_id: cluster HW identifier + * @cluster_mask: the cpumask location to be initialized, modified by the + * function only if return value == 0 + * + * Return: + * + * 0 on success + * -EINVAL if cluster_mask is NULL or there is no record matching socket_id + */ +int cluster_to_logical_mask(unsigned int socket_id, cpumask_t *cluster_mask) +{ + int cpu; + + if (!cluster_mask) + return -EINVAL; + + for_each_online_cpu(cpu) { + if (socket_id == topology_physical_package_id(cpu)) { + cpumask_copy(cluster_mask, topology_core_cpumask(cpu)); + return 0; + } + } + + return -EINVAL; +} + +/* + * init_cpu_topology is called at boot when only one cpu is running + * which prevent simultaneous write access to cpu_topology array + */ +void __init init_cpu_topology(void) +{ + unsigned int cpu; + + /* init core mask and power*/ + for_each_possible_cpu(cpu) { + struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]); + + cpu_topo->thread_id = -1; + cpu_topo->core_id = -1; + cpu_topo->socket_id = -1; + cpumask_clear(&cpu_topo->core_sibling); + cpumask_clear(&cpu_topo->thread_sibling); + + set_power_scale(cpu, SCHED_POWER_SCALE); + } + smp_wmb(); + + parse_dt_topology(); +} diff -Nur linux-3.10.30/arch/arm64/kernel/vmlinux.lds.S linux-3.10.30-cubox-i/arch/arm64/kernel/vmlinux.lds.S --- linux-3.10.30/arch/arm64/kernel/vmlinux.lds.S 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/arm64/kernel/vmlinux.lds.S 2014-03-08 20:32:56.000000000 +0100 @@ -41,7 +41,6 @@ } .text : { /* Real text segment */ _stext = .; /* Text and read-only data */ - *(.smp.pen.text) __exception_text_start = .; *(.exception.text) __exception_text_end = .; diff -Nur linux-3.10.30/arch/mips/include/asm/pci.h linux-3.10.30-cubox-i/arch/mips/include/asm/pci.h --- linux-3.10.30/arch/mips/include/asm/pci.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/mips/include/asm/pci.h 2014-03-08 20:33:02.000000000 +0100 @@ -137,11 +137,6 @@ return channel ? 15 : 14; } -#ifdef CONFIG_CPU_CAVIUM_OCTEON -/* MSI arch hook for OCTEON */ -#define arch_setup_msi_irqs arch_setup_msi_irqs -#endif - extern char * (*pcibios_plat_setup)(char *str); #ifdef CONFIG_OF diff -Nur linux-3.10.30/arch/powerpc/include/asm/pci.h linux-3.10.30-cubox-i/arch/powerpc/include/asm/pci.h --- linux-3.10.30/arch/powerpc/include/asm/pci.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/powerpc/include/asm/pci.h 2014-03-08 20:33:09.000000000 +0100 @@ -113,11 +113,6 @@ /* Decide whether to display the domain number in /proc */ extern int pci_proc_domain(struct pci_bus *bus); -/* MSI arch hooks */ -#define arch_setup_msi_irqs arch_setup_msi_irqs -#define arch_teardown_msi_irqs arch_teardown_msi_irqs -#define arch_msi_check_device arch_msi_check_device - struct vm_area_struct; /* Map a range of PCI memory or I/O space for a device into user space */ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, diff -Nur linux-3.10.30/arch/powerpc/include/asm/prom.h linux-3.10.30-cubox-i/arch/powerpc/include/asm/prom.h --- linux-3.10.30/arch/powerpc/include/asm/prom.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/powerpc/include/asm/prom.h 2014-03-08 20:33:09.000000000 +0100 @@ -43,9 +43,6 @@ extern void kdump_move_device_tree(void); -/* CPU OF node matching */ -struct device_node *of_get_cpu_node(int cpu, unsigned int *thread); - /* cache lookup */ struct device_node *of_find_next_cache_node(struct device_node *np); diff -Nur linux-3.10.30/arch/powerpc/kernel/prom.c linux-3.10.30-cubox-i/arch/powerpc/kernel/prom.c --- linux-3.10.30/arch/powerpc/kernel/prom.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/powerpc/kernel/prom.c 2014-03-08 20:33:10.000000000 +0100 @@ -827,49 +827,10 @@ __initcall(prom_reconfig_setup); #endif -/* Find the device node for a given logical cpu number, also returns the cpu - * local thread number (index in ibm,interrupt-server#s) if relevant and - * asked for (non NULL) - */ -struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) +bool arch_match_cpu_phys_id(int cpu, u64 phys_id) { - int hardid; - struct device_node *np; - - hardid = get_hard_smp_processor_id(cpu); - - for_each_node_by_type(np, "cpu") { - const u32 *intserv; - unsigned int plen, t; - - /* Check for ibm,ppc-interrupt-server#s. If it doesn't exist - * fallback to "reg" property and assume no threads - */ - intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", - &plen); - if (intserv == NULL) { - const u32 *reg = of_get_property(np, "reg", NULL); - if (reg == NULL) - continue; - if (*reg == hardid) { - if (thread) - *thread = 0; - return np; - } - } else { - plen /= sizeof(u32); - for (t = 0; t < plen; t++) { - if (hardid == intserv[t]) { - if (thread) - *thread = t; - return np; - } - } - } - } - return NULL; + return (int)phys_id == get_hard_smp_processor_id(cpu); } -EXPORT_SYMBOL(of_get_cpu_node); #if defined(CONFIG_DEBUG_FS) && defined(DEBUG) static struct debugfs_blob_wrapper flat_dt_blob; diff -Nur linux-3.10.30/arch/s390/include/asm/pci.h linux-3.10.30-cubox-i/arch/s390/include/asm/pci.h --- linux-3.10.30/arch/s390/include/asm/pci.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/s390/include/asm/pci.h 2014-03-08 20:33:12.000000000 +0100 @@ -21,10 +21,6 @@ int pci_domain_nr(struct pci_bus *); int pci_proc_domain(struct pci_bus *); -/* MSI arch hooks */ -#define arch_setup_msi_irqs arch_setup_msi_irqs -#define arch_teardown_msi_irqs arch_teardown_msi_irqs - #define ZPCI_BUS_NR 0 /* default bus number */ #define ZPCI_DEVFN 0 /* default device number */ diff -Nur linux-3.10.30/arch/x86/include/asm/pci.h linux-3.10.30-cubox-i/arch/x86/include/asm/pci.h --- linux-3.10.30/arch/x86/include/asm/pci.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/x86/include/asm/pci.h 2014-03-08 20:33:20.000000000 +0100 @@ -100,29 +100,6 @@ extern void pci_iommu_alloc(void); #ifdef CONFIG_PCI_MSI -/* MSI arch specific hooks */ -static inline int x86_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) -{ - return x86_msi.setup_msi_irqs(dev, nvec, type); -} - -static inline void x86_teardown_msi_irqs(struct pci_dev *dev) -{ - x86_msi.teardown_msi_irqs(dev); -} - -static inline void x86_teardown_msi_irq(unsigned int irq) -{ - x86_msi.teardown_msi_irq(irq); -} -static inline void x86_restore_msi_irqs(struct pci_dev *dev, int irq) -{ - x86_msi.restore_msi_irqs(dev, irq); -} -#define arch_setup_msi_irqs x86_setup_msi_irqs -#define arch_teardown_msi_irqs x86_teardown_msi_irqs -#define arch_teardown_msi_irq x86_teardown_msi_irq -#define arch_restore_msi_irqs x86_restore_msi_irqs /* implemented in arch/x86/kernel/apic/io_apic. */ struct msi_desc; int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); @@ -130,16 +107,9 @@ void native_restore_msi_irqs(struct pci_dev *dev, int irq); int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, unsigned int irq_base, unsigned int irq_offset); -/* default to the implementation in drivers/lib/msi.c */ -#define HAVE_DEFAULT_MSI_TEARDOWN_IRQS -#define HAVE_DEFAULT_MSI_RESTORE_IRQS -void default_teardown_msi_irqs(struct pci_dev *dev); -void default_restore_msi_irqs(struct pci_dev *dev, int irq); #else #define native_setup_msi_irqs NULL #define native_teardown_msi_irq NULL -#define default_teardown_msi_irqs NULL -#define default_restore_msi_irqs NULL #endif #define PCI_DMA_BUS_IS_PHYS (dma_ops->is_phys) diff -Nur linux-3.10.30/arch/x86/kernel/x86_init.c linux-3.10.30-cubox-i/arch/x86/kernel/x86_init.c --- linux-3.10.30/arch/x86/kernel/x86_init.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/arch/x86/kernel/x86_init.c 2014-03-08 20:33:22.000000000 +0100 @@ -107,6 +107,8 @@ }; EXPORT_SYMBOL_GPL(x86_platform); + +#if defined(CONFIG_PCI_MSI) struct x86_msi_ops x86_msi = { .setup_msi_irqs = native_setup_msi_irqs, .compose_msi_msg = native_compose_msi_msg, @@ -116,6 +118,28 @@ .setup_hpet_msi = default_setup_hpet_msi, }; +/* MSI arch specific hooks */ +int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + return x86_msi.setup_msi_irqs(dev, nvec, type); +} + +void arch_teardown_msi_irqs(struct pci_dev *dev) +{ + x86_msi.teardown_msi_irqs(dev); +} + +void arch_teardown_msi_irq(unsigned int irq) +{ + x86_msi.teardown_msi_irq(irq); +} + +void arch_restore_msi_irqs(struct pci_dev *dev, int irq) +{ + x86_msi.restore_msi_irqs(dev, irq); +} +#endif + struct x86_io_apic_ops x86_io_apic_ops = { .init = native_io_apic_init_mappings, .read = native_io_apic_read, diff -Nur linux-3.10.30/block/blk-core.c linux-3.10.30-cubox-i/block/blk-core.c --- linux-3.10.30/block/blk-core.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/block/blk-core.c 2014-03-08 20:33:23.000000000 +0100 @@ -3191,7 +3191,8 @@ /* used for unplugging and affects IO latency/throughput - HIGHPRI */ kblockd_workqueue = alloc_workqueue("kblockd", - WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + WQ_MEM_RECLAIM | WQ_HIGHPRI | + WQ_POWER_EFFICIENT, 0); if (!kblockd_workqueue) panic("Failed to create kblockd\n"); diff -Nur linux-3.10.30/block/blk-ioc.c linux-3.10.30-cubox-i/block/blk-ioc.c --- linux-3.10.30/block/blk-ioc.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/block/blk-ioc.c 2014-03-08 20:33:23.000000000 +0100 @@ -144,7 +144,8 @@ if (atomic_long_dec_and_test(&ioc->refcount)) { spin_lock_irqsave(&ioc->lock, flags); if (!hlist_empty(&ioc->icq_list)) - schedule_work(&ioc->release_work); + queue_work(system_power_efficient_wq, + &ioc->release_work); else free_ioc = true; spin_unlock_irqrestore(&ioc->lock, flags); diff -Nur linux-3.10.30/block/genhd.c linux-3.10.30-cubox-i/block/genhd.c --- linux-3.10.30/block/genhd.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/block/genhd.c 2014-03-08 20:33:23.000000000 +0100 @@ -1489,9 +1489,11 @@ intv = disk_events_poll_jiffies(disk); set_timer_slack(&ev->dwork.timer, intv / 4); if (check_now) - queue_delayed_work(system_freezable_wq, &ev->dwork, 0); + queue_delayed_work(system_freezable_power_efficient_wq, + &ev->dwork, 0); else if (intv) - queue_delayed_work(system_freezable_wq, &ev->dwork, intv); + queue_delayed_work(system_freezable_power_efficient_wq, + &ev->dwork, intv); out_unlock: spin_unlock_irqrestore(&ev->lock, flags); } @@ -1534,7 +1536,8 @@ spin_lock_irq(&ev->lock); ev->clearing |= mask; if (!ev->block) - mod_delayed_work(system_freezable_wq, &ev->dwork, 0); + mod_delayed_work(system_freezable_power_efficient_wq, + &ev->dwork, 0); spin_unlock_irq(&ev->lock); } @@ -1627,7 +1630,8 @@ intv = disk_events_poll_jiffies(disk); if (!ev->block && intv) - queue_delayed_work(system_freezable_wq, &ev->dwork, intv); + queue_delayed_work(system_freezable_power_efficient_wq, + &ev->dwork, intv); spin_unlock_irq(&ev->lock); diff -Nur linux-3.10.30/drivers/Kconfig linux-3.10.30-cubox-i/drivers/Kconfig --- linux-3.10.30/drivers/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/Kconfig 2014-03-08 20:33:25.000000000 +0100 @@ -98,6 +98,8 @@ source "drivers/memstick/Kconfig" +source "drivers/mxc/Kconfig" + source "drivers/leds/Kconfig" source "drivers/accessibility/Kconfig" @@ -166,4 +168,6 @@ source "drivers/reset/Kconfig" +source "drivers/gator/Kconfig" + endmenu diff -Nur linux-3.10.30/drivers/Makefile linux-3.10.30-cubox-i/drivers/Makefile --- linux-3.10.30/drivers/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/Makefile 2014-03-08 20:33:25.000000000 +0100 @@ -109,6 +109,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += mmc/ +obj-$(CONFIG_ARCH_MXC) += mxc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-y += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ @@ -152,3 +153,5 @@ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_IPACK_BUS) += ipack/ obj-$(CONFIG_NTB) += ntb/ + +obj-$(CONFIG_GATOR) += gator/ diff -Nur linux-3.10.30/drivers/ata/Kconfig linux-3.10.30-cubox-i/drivers/ata/Kconfig --- linux-3.10.30/drivers/ata/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/ata/Kconfig 2014-03-08 20:33:26.000000000 +0100 @@ -97,6 +97,15 @@ If unsure, say N. +config AHCI_IMX + tristate "Freescale i.MX AHCI SATA support" + depends on SATA_AHCI_PLATFORM + help + This option enables support for the Freescale i.MX SoC's + onboard AHCI SATA. + + If unsure, say N. + config SATA_FSL tristate "Freescale 3.0Gbps SATA support" depends on FSL_SOC diff -Nur linux-3.10.30/drivers/ata/Makefile linux-3.10.30-cubox-i/drivers/ata/Makefile --- linux-3.10.30/drivers/ata/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/ata/Makefile 2014-03-08 20:33:26.000000000 +0100 @@ -10,6 +10,7 @@ obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +obj-$(CONFIG_AHCI_IMX) += ahci_imx.o # SFF w/ custom DMA obj-$(CONFIG_PDC_ADMA) += pdc_adma.o diff -Nur linux-3.10.30/drivers/ata/ahci.h linux-3.10.30-cubox-i/drivers/ata/ahci.h --- linux-3.10.30/drivers/ata/ahci.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/ata/ahci.h 2014-03-08 20:33:26.000000000 +0100 @@ -337,6 +337,7 @@ .sdev_attrs = ahci_sdev_attrs extern struct ata_port_operations ahci_ops; +extern struct ata_port_operations ahci_platform_ops; extern struct ata_port_operations ahci_pmp_retry_srst_ops; unsigned int ahci_dev_classify(struct ata_port *ap); @@ -366,6 +367,7 @@ irqreturn_t ahci_thread_fn(int irq, void *dev_instance); void ahci_print_info(struct ata_host *host, const char *scc_s); int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis); +void ahci_error_handler(struct ata_port *ap); static inline void __iomem *__ahci_port_base(struct ata_host *host, unsigned int port_no) diff -Nur linux-3.10.30/drivers/ata/ahci_imx.c linux-3.10.30-cubox-i/drivers/ata/ahci_imx.c --- linux-3.10.30/drivers/ata/ahci_imx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/ata/ahci_imx.c 2014-03-08 20:33:26.000000000 +0100 @@ -0,0 +1,351 @@ +/* + * copyright (c) 2013 Freescale Semiconductor, Inc. + * Freescale IMX AHCI SATA platform driver + * + * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ahci.h" + +enum { + PORT_PHY_CTL = 0x178, /* Port0 PHY Control */ + PORT_PHY_CTL_PDDQ_LOC = 0x100000, /* PORT_PHY_CTL bits */ + HOST_TIMER1MS = 0xe0, /* Timer 1-ms */ +}; + +struct imx_ahci_priv { + struct platform_device *ahci_pdev; + struct clk *sata_ref_clk; + struct clk *ahb_clk; + struct regmap *gpr; + bool no_device; + bool first_time; +}; + +static int ahci_imx_hotplug; +module_param_named(hotplug, ahci_imx_hotplug, int, 0644); +MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)"); + +static void ahci_imx_error_handler(struct ata_port *ap) +{ + u32 reg_val; + struct ata_device *dev; + struct ata_host *host = dev_get_drvdata(ap->dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + struct imx_ahci_priv *imxpriv = dev_get_drvdata(ap->dev->parent); + + ahci_error_handler(ap); + + if (!(imxpriv->first_time) || ahci_imx_hotplug) + return; + + imxpriv->first_time = false; + + ata_for_each_dev(dev, &ap->link, ENABLED) + return; + /* + * Disable link to save power. An imx ahci port can't be recovered + * without full reset once the pddq mode is enabled making it + * impossible to use as part of libata LPM. + */ + reg_val = readl(mmio + PORT_PHY_CTL); + writel(reg_val | PORT_PHY_CTL_PDDQ_LOC, mmio + PORT_PHY_CTL); + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + !IMX6Q_GPR13_SATA_MPLL_CLK_EN); + clk_disable_unprepare(imxpriv->sata_ref_clk); + release_bus_freq(BUS_FREQ_HIGH); + imxpriv->no_device = true; + + dev_info(ap->dev, "no device found, disabling link.\n"); + dev_info(ap->dev, "pass " MODULE_PARAM_PREFIX + ".hotplug=1 to enable hotplug\n"); +} + +static struct ata_port_operations ahci_imx_ops = { + .inherits = &ahci_platform_ops, + .error_handler = ahci_imx_error_handler, +}; + +static const struct ata_port_info ahci_imx_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_imx_ops, +}; + +static int imx6q_sata_init(struct device *dev, void __iomem *mmio) +{ + int ret = 0; + unsigned int reg_val; + struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); + + imxpriv->gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imxpriv->gpr)) { + dev_err(dev, "failed to find fsl,imx6q-iomux-gpr regmap\n"); + return PTR_ERR(imxpriv->gpr); + } + + ret = clk_prepare_enable(imxpriv->sata_ref_clk); + if (ret < 0) { + dev_err(dev, "prepare-enable sata_ref clock err:%d\n", ret); + return ret; + } + request_bus_freq(BUS_FREQ_HIGH); + + /* + * set PHY Paremeters, two steps to configure the GPR13, + * one write for rest of parameters, mask of first write + * is 0x07fffffd, and the other one write for setting + * the mpll_clk_en. + */ + regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK + | IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK + | IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK + | IMX6Q_GPR13_SATA_SPD_MODE_MASK + | IMX6Q_GPR13_SATA_MPLL_SS_EN + | IMX6Q_GPR13_SATA_TX_ATTEN_MASK + | IMX6Q_GPR13_SATA_TX_BOOST_MASK + | IMX6Q_GPR13_SATA_TX_LVL_MASK + | IMX6Q_GPR13_SATA_TX_EDGE_RATE + , IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB + | IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M + | IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F + | IMX6Q_GPR13_SATA_SPD_MODE_3P0G + | /* IMX6Q_GPR13_SATA_MPLL_SS_EN */ 0 + | IMX6Q_GPR13_SATA_TX_ATTEN_9_16 + | /* IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB + | IMX6Q_GPR13_SATA_TX_LVL_1_025_V */ + IMX6Q_GPR13_SATA_TX_LVL_1_104_V); + regmap_update_bits(imxpriv->gpr, 0x34, IMX6Q_GPR13_SATA_MPLL_CLK_EN, + IMX6Q_GPR13_SATA_MPLL_CLK_EN); + usleep_range(100, 200); + + /* + * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, + * and IP vendor specific register HOST_TIMER1MS. + * Configure CAP_SSS (support stagered spin up). + * Implement the port0. + * Get the ahb clock rate, and configure the TIMER1MS register. + */ + reg_val = readl(mmio + HOST_CAP); + if (!(reg_val & HOST_CAP_SSS)) { + reg_val |= HOST_CAP_SSS; + writel(reg_val, mmio + HOST_CAP); + } + reg_val = readl(mmio + HOST_PORTS_IMPL); + if (!(reg_val & 0x1)) { + reg_val |= 0x1; + writel(reg_val, mmio + HOST_PORTS_IMPL); + } + + reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; + writel(reg_val, mmio + HOST_TIMER1MS); + + return 0; +} + +static void imx6q_sata_exit(struct device *dev) +{ + struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); + + if (!imxpriv->no_device) { + regmap_update_bits(imxpriv->gpr, 0x34, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + !IMX6Q_GPR13_SATA_MPLL_CLK_EN); + clk_disable_unprepare(imxpriv->sata_ref_clk); + release_bus_freq(BUS_FREQ_HIGH); + } +} + +static int imx_ahci_suspend(struct device *dev) +{ + struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); + + /* + * If no_device is set, The CLKs had been gated off in the + * initialization so don't do it again here. + */ + if (!imxpriv->no_device) { + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + !IMX6Q_GPR13_SATA_MPLL_CLK_EN); + clk_disable_unprepare(imxpriv->sata_ref_clk); + release_bus_freq(BUS_FREQ_HIGH); + } + + return 0; +} + +static int imx_ahci_resume(struct device *dev) +{ + struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); + int ret; + + if (!imxpriv->no_device) { + ret = clk_prepare_enable(imxpriv->sata_ref_clk); + if (ret < 0) { + dev_err(dev, "pre-enable sata_ref clock err:%d\n", ret); + return ret; + } + request_bus_freq(BUS_FREQ_HIGH); + + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_MPLL_CLK_EN, + IMX6Q_GPR13_SATA_MPLL_CLK_EN); + usleep_range(1000, 2000); + } + + return 0; +} + +static struct ahci_platform_data imx6q_sata_pdata = { + .init = imx6q_sata_init, + .exit = imx6q_sata_exit, + .ata_port_info = &ahci_imx_port_info, + .suspend = imx_ahci_suspend, + .resume = imx_ahci_resume, +}; + +static const struct of_device_id imx_ahci_of_match[] = { + { .compatible = "fsl,imx6q-ahci", .data = &imx6q_sata_pdata}, + {}, +}; +MODULE_DEVICE_TABLE(of, imx_ahci_of_match); + +static int imx_ahci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *mem, *irq, res[2]; + const struct of_device_id *of_id; + const struct ahci_platform_data *pdata = NULL; + struct imx_ahci_priv *imxpriv; + struct device *ahci_dev; + struct platform_device *ahci_pdev; + int ret; + + /* Prevent our child ahci device coming back to us */ + if (!strcmp(dev_name(&pdev->dev), "ahci")) + return -ENODEV; + + imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL); + if (!imxpriv) { + dev_err(dev, "can't alloc ahci_host_priv\n"); + return -ENOMEM; + } + + ahci_pdev = platform_device_alloc("ahci", -1); + if (!ahci_pdev) + return -ENODEV; + + ahci_dev = &ahci_pdev->dev; + ahci_dev->parent = dev; + + imxpriv->no_device = false; + imxpriv->first_time = true; + imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(imxpriv->ahb_clk)) { + dev_err(dev, "can't get ahb clock.\n"); + ret = PTR_ERR(imxpriv->ahb_clk); + goto err_out; + } + + imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref"); + if (IS_ERR(imxpriv->sata_ref_clk)) { + dev_err(dev, "can't get sata_ref clock.\n"); + ret = PTR_ERR(imxpriv->sata_ref_clk); + goto err_out; + } + + imxpriv->ahci_pdev = ahci_pdev; + platform_set_drvdata(pdev, imxpriv); + + of_id = of_match_device(imx_ahci_of_match, dev); + if (of_id) { + pdata = of_id->data; + } else { + ret = -EINVAL; + goto err_out; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem || !irq) { + dev_err(dev, "no mmio/irq resource\n"); + ret = -ENOMEM; + goto err_out; + } + + res[0] = *mem; + res[1] = *irq; + + ahci_dev->coherent_dma_mask = DMA_BIT_MASK(32); + ahci_dev->dma_mask = &ahci_dev->coherent_dma_mask; + ahci_dev->of_node = dev->of_node; + + ret = platform_device_add_resources(ahci_pdev, res, 2); + if (ret) + goto err_out; + + ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata)); + if (ret) + goto err_out; + + ret = platform_device_add(ahci_pdev); + if (ret) { +err_out: + platform_device_put(ahci_pdev); + return ret; + } + + return 0; +} + +static int imx_ahci_remove(struct platform_device *pdev) +{ + struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev); + struct platform_device *ahci_pdev = imxpriv->ahci_pdev; + + platform_device_unregister(ahci_pdev); + return 0; +} + +static struct platform_driver imx_ahci_driver = { + .probe = imx_ahci_probe, + .remove = imx_ahci_remove, + .driver = { + .name = "ahci-imx", + .owner = THIS_MODULE, + .of_match_table = imx_ahci_of_match, + }, +}; +module_platform_driver(imx_ahci_driver); + +MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver"); +MODULE_AUTHOR("Richard Zhu "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ahci:imx"); diff -Nur linux-3.10.30/drivers/ata/ahci_platform.c linux-3.10.30-cubox-i/drivers/ata/ahci_platform.c --- linux-3.10.30/drivers/ata/ahci_platform.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/ata/ahci_platform.c 2014-03-08 20:33:26.000000000 +0100 @@ -49,10 +49,11 @@ }; MODULE_DEVICE_TABLE(platform, ahci_devtype); -static struct ata_port_operations ahci_platform_ops = { +struct ata_port_operations ahci_platform_ops = { .inherits = &ahci_ops, .host_stop = ahci_host_stop, }; +EXPORT_SYMBOL_GPL(ahci_platform_ops); static struct ata_port_operations ahci_platform_retry_srst_ops = { .inherits = &ahci_pmp_retry_srst_ops, diff -Nur linux-3.10.30/drivers/ata/libahci.c linux-3.10.30-cubox-i/drivers/ata/libahci.c --- linux-3.10.30/drivers/ata/libahci.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/ata/libahci.c 2014-03-08 20:33:26.000000000 +0100 @@ -89,7 +89,6 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline); static void ahci_postreset(struct ata_link *link, unsigned int *class); -static void ahci_error_handler(struct ata_port *ap); static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); static void ahci_dev_config(struct ata_device *dev); #ifdef CONFIG_PM @@ -1996,7 +1995,7 @@ writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); } -static void ahci_error_handler(struct ata_port *ap) +void ahci_error_handler(struct ata_port *ap) { if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ @@ -2009,6 +2008,7 @@ if (!ata_dev_enabled(ap->link.device)) ahci_stop_engine(ap); } +EXPORT_SYMBOL_GPL(ahci_error_handler); static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) { diff -Nur linux-3.10.30/drivers/base/Makefile linux-3.10.30-cubox-i/drivers/base/Makefile --- linux-3.10.30/drivers/base/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/base/Makefile 2014-03-08 20:33:27.000000000 +0100 @@ -1,6 +1,6 @@ # Makefile for the Linux device tree -obj-y := core.o bus.o dd.o syscore.o \ +obj-y := component.o core.o bus.o dd.o syscore.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o \ diff -Nur linux-3.10.30/drivers/base/base.h linux-3.10.30-cubox-i/drivers/base/base.h --- linux-3.10.30/drivers/base/base.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/base/base.h 2014-03-08 20:33:27.000000000 +0100 @@ -119,6 +119,11 @@ return drv->bus->match ? drv->bus->match(dev, drv) : 1; } +extern int device_add_groups(struct device *dev, + const struct attribute_group **groups); +extern void device_remove_groups(struct device *dev, + const struct attribute_group **groups); + extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); diff -Nur linux-3.10.30/drivers/base/bus.c linux-3.10.30-cubox-i/drivers/base/bus.c --- linux-3.10.30/drivers/base/bus.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/base/bus.c 2014-03-08 20:33:27.000000000 +0100 @@ -499,6 +499,9 @@ error = device_add_attrs(bus, dev); if (error) goto out_put; + error = device_add_groups(dev, bus->dev_groups); + if (error) + goto out_groups; error = sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev_name(dev)); if (error) @@ -513,6 +516,8 @@ out_subsys: sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev)); +out_groups: + device_remove_groups(dev, bus->dev_groups); out_id: device_remove_attrs(bus, dev); out_put: @@ -575,6 +580,7 @@ sysfs_remove_link(&dev->bus->p->devices_kset->kobj, dev_name(dev)); device_remove_attrs(dev->bus, dev); + device_remove_groups(dev, dev->bus->dev_groups); if (klist_node_attached(&dev->p->knode_bus)) klist_del(&dev->p->knode_bus); diff -Nur linux-3.10.30/drivers/base/component.c linux-3.10.30-cubox-i/drivers/base/component.c --- linux-3.10.30/drivers/base/component.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/base/component.c 2014-03-08 20:33:27.000000000 +0100 @@ -0,0 +1,382 @@ +/* + * Componentized device handling. + * + * 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 is work in progress. We gather up the component devices into a list, + * and bind them when instructed. At the moment, we're specific to the DRM + * subsystem, and only handles one master device, but this doesn't have to be + * the case. + */ +#include +#include +#include +#include +#include +#include +#include + +struct master { + struct list_head node; + struct list_head components; + bool bound; + + const struct component_master_ops *ops; + struct device *dev; +}; + +struct component { + struct list_head node; + struct list_head master_node; + struct master *master; + bool bound; + + const struct component_ops *ops; + struct device *dev; +}; + +static DEFINE_MUTEX(component_mutex); +static LIST_HEAD(component_list); +static LIST_HEAD(masters); + +static struct master *__master_find(struct device *dev, + const struct component_master_ops *ops) +{ + struct master *m; + + list_for_each_entry(m, &masters, node) + if (m->dev == dev && (!ops || m->ops == ops)) + return m; + + return NULL; +} + +/* Attach an unattached component to a master. */ +static void component_attach_master(struct master *master, struct component *c) +{ + c->master = master; + + list_add_tail(&c->master_node, &master->components); +} + +/* Detach a component from a master. */ +static void component_detach_master(struct master *master, struct component *c) +{ + list_del(&c->master_node); + + c->master = NULL; +} + +int component_master_add_child(struct master *master, + int (*compare)(struct device *, void *), void *compare_data) +{ + struct component *c; + int ret = -ENXIO; + + list_for_each_entry(c, &component_list, node) { + if (c->master) + continue; + + if (compare(c->dev, compare_data)) { + component_attach_master(master, c); + ret = 0; + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(component_master_add_child); + +/* Detach all attached components from this master */ +static void master_remove_components(struct master *master) +{ + while (!list_empty(&master->components)) { + struct component *c = list_first_entry(&master->components, + struct component, master_node); + + WARN_ON(c->master != master); + + component_detach_master(master, c); + } +} + +/* + * Try to bring up a master. If component is NULL, we're interested in + * this master, otherwise it's a component which must be present to try + * and bring up the master. + * + * Returns 1 for successful bringup, 0 if not ready, or -ve errno. + */ +static int try_to_bring_up_master(struct master *master, + struct component *component) +{ + int ret = 0; + + if (!master->bound) { + /* + * Search the list of components, looking for components that + * belong to this master, and attach them to the master. + */ + if (master->ops->add_components(master->dev, master)) { + /* Failed to find all components */ + master_remove_components(master); + ret = 0; + goto out; + } + + if (component && component->master != master) { + master_remove_components(master); + ret = 0; + goto out; + } + + /* Found all components */ + ret = master->ops->bind(master->dev); + if (ret < 0) { + master_remove_components(master); + goto out; + } + + master->bound = true; + ret = 1; + } +out: + + return ret; +} + +static int try_to_bring_up_masters(struct component *component) +{ + struct master *m; + int ret = 0; + + list_for_each_entry(m, &masters, node) { + ret = try_to_bring_up_master(m, component); + if (ret != 0) + break; + } + + return ret; +} + +static void take_down_master(struct master *master) +{ + if (master->bound) { + master->ops->unbind(master->dev); + master->bound = false; + } + + master_remove_components(master); +} + +int component_master_add(struct device *dev, + const struct component_master_ops *ops) +{ + struct master *master; + int ret; + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; + + master->dev = dev; + master->ops = ops; + INIT_LIST_HEAD(&master->components); + + /* Add to the list of available masters. */ + mutex_lock(&component_mutex); + list_add(&master->node, &masters); + + ret = try_to_bring_up_master(master, NULL); + + if (ret < 0) { + /* Delete off the list if we weren't successful */ + list_del(&master->node); + kfree(master); + } + mutex_unlock(&component_mutex); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(component_master_add); + +void component_master_del(struct device *dev, + const struct component_master_ops *ops) +{ + struct master *master; + + mutex_lock(&component_mutex); + master = __master_find(dev, ops); + if (master) { + take_down_master(master); + + list_del(&master->node); + kfree(master); + } + mutex_unlock(&component_mutex); +} +EXPORT_SYMBOL_GPL(component_master_del); + +static void component_unbind(struct component *component, + struct master *master, void *data) +{ + WARN_ON(!component->bound); + + component->ops->unbind(component->dev, master->dev, data); + component->bound = false; + + /* Release all resources claimed in the binding of this component */ + devres_release_group(component->dev, component); +} + +void component_unbind_all(struct device *master_dev, void *data) +{ + struct master *master; + struct component *c; + + WARN_ON(!mutex_is_locked(&component_mutex)); + + master = __master_find(master_dev, NULL); + if (!master) + return; + + list_for_each_entry_reverse(c, &master->components, master_node) + component_unbind(c, master, data); +} +EXPORT_SYMBOL_GPL(component_unbind_all); + +static int component_bind(struct component *component, struct master *master, + void *data) +{ + int ret; + + /* + * Each component initialises inside its own devres group. + * This allows us to roll-back a failed component without + * affecting anything else. + */ + if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) + return -ENOMEM; + + /* + * Also open a group for the device itself: this allows us + * to release the resources claimed against the sub-device + * at the appropriate moment. + */ + if (!devres_open_group(component->dev, component, GFP_KERNEL)) { + devres_release_group(master->dev, NULL); + return -ENOMEM; + } + + dev_dbg(master->dev, "binding %s (ops %ps)\n", + dev_name(component->dev), component->ops); + + ret = component->ops->bind(component->dev, master->dev, data); + if (!ret) { + component->bound = true; + + /* + * Close the component device's group so that resources + * allocated in the binding are encapsulated for removal + * at unbind. Remove the group on the DRM device as we + * can clean those resources up independently. + */ + devres_close_group(component->dev, NULL); + devres_remove_group(master->dev, NULL); + + dev_info(master->dev, "bound %s (ops %ps)\n", + dev_name(component->dev), component->ops); + } else { + devres_release_group(component->dev, NULL); + devres_release_group(master->dev, NULL); + + dev_err(master->dev, "failed to bind %s (ops %ps): %d\n", + dev_name(component->dev), component->ops, ret); + } + + return ret; +} + +int component_bind_all(struct device *master_dev, void *data) +{ + struct master *master; + struct component *c; + int ret = 0; + + WARN_ON(!mutex_is_locked(&component_mutex)); + + master = __master_find(master_dev, NULL); + if (!master) + return -EINVAL; + + list_for_each_entry(c, &master->components, master_node) { + ret = component_bind(c, master, data); + if (ret) + break; + } + + if (ret != 0) { + list_for_each_entry_continue_reverse(c, &master->components, + master_node) + component_unbind(c, master, data); + } + + return ret; +} +EXPORT_SYMBOL_GPL(component_bind_all); + +int component_add(struct device *dev, const struct component_ops *ops) +{ + struct component *component; + int ret; + + component = kzalloc(sizeof(*component), GFP_KERNEL); + if (!component) + return -ENOMEM; + + component->ops = ops; + component->dev = dev; + + dev_dbg(dev, "adding component (ops %ps)\n", ops); + + mutex_lock(&component_mutex); + list_add_tail(&component->node, &component_list); + + ret = try_to_bring_up_masters(component); + if (ret < 0) { + list_del(&component->node); + + kfree(component); + } + mutex_unlock(&component_mutex); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(component_add); + +void component_del(struct device *dev, const struct component_ops *ops) +{ + struct component *c, *component = NULL; + + mutex_lock(&component_mutex); + list_for_each_entry(c, &component_list, node) + if (c->dev == dev && c->ops == ops) { + list_del(&c->node); + component = c; + break; + } + + if (component && component->master) + take_down_master(component->master); + + mutex_unlock(&component_mutex); + + WARN_ON(!component); + kfree(component); +} +EXPORT_SYMBOL_GPL(component_del); + +MODULE_LICENSE("GPL v2"); diff -Nur linux-3.10.30/drivers/base/core.c linux-3.10.30-cubox-i/drivers/base/core.c --- linux-3.10.30/drivers/base/core.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/base/core.c 2014-03-08 20:33:27.000000000 +0100 @@ -461,8 +461,7 @@ device_remove_bin_file(dev, &attrs[i]); } -static int device_add_groups(struct device *dev, - const struct attribute_group **groups) +int device_add_groups(struct device *dev, const struct attribute_group **groups) { int error = 0; int i; @@ -481,8 +480,8 @@ return error; } -static void device_remove_groups(struct device *dev, - const struct attribute_group **groups) +void device_remove_groups(struct device *dev, + const struct attribute_group **groups) { int i; diff -Nur linux-3.10.30/drivers/base/pinctrl.c linux-3.10.30-cubox-i/drivers/base/pinctrl.c --- linux-3.10.30/drivers/base/pinctrl.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/base/pinctrl.c 2014-03-08 20:33:27.000000000 +0100 @@ -48,6 +48,25 @@ goto cleanup_get; } +#ifdef CONFIG_PM + /* + * If power management is enabled, we also look for the optional + * sleep and idle pin states, with semantics as defined in + * + */ + dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, + PINCTRL_STATE_SLEEP); + if (IS_ERR(dev->pins->sleep_state)) + /* Not supplying this state is perfectly legal */ + dev_dbg(dev, "no sleep pinctrl state\n"); + + dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p, + PINCTRL_STATE_IDLE); + if (IS_ERR(dev->pins->idle_state)) + /* Not supplying this state is perfectly legal */ + dev_dbg(dev, "no idle pinctrl state\n"); +#endif + return 0; /* diff -Nur linux-3.10.30/drivers/base/regmap/internal.h linux-3.10.30-cubox-i/drivers/base/regmap/internal.h --- linux-3.10.30/drivers/base/regmap/internal.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/base/regmap/internal.h 2014-03-08 20:33:27.000000000 +0100 @@ -52,6 +52,7 @@ struct regmap { struct mutex mutex; spinlock_t spinlock; + unsigned long spinlock_flags; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ diff -Nur linux-3.10.30/drivers/base/regmap/regmap.c linux-3.10.30-cubox-i/drivers/base/regmap/regmap.c --- linux-3.10.30/drivers/base/regmap/regmap.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/base/regmap/regmap.c 2014-03-08 20:33:27.000000000 +0100 @@ -302,13 +302,16 @@ static void regmap_lock_spinlock(void *__map) { struct regmap *map = __map; - spin_lock(&map->spinlock); + unsigned long flags; + + spin_lock_irqsave(&map->spinlock, flags); + map->spinlock_flags = flags; } static void regmap_unlock_spinlock(void *__map) { struct regmap *map = __map; - spin_unlock(&map->spinlock); + spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); } static void dev_get_regmap_release(struct device *dev, void *res) diff -Nur linux-3.10.30/drivers/block/Kconfig linux-3.10.30-cubox-i/drivers/block/Kconfig --- linux-3.10.30/drivers/block/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/Kconfig 2014-03-08 20:33:27.000000000 +0100 @@ -541,4 +541,6 @@ To compile this driver as a module, choose M here: the module will be called rsxx. +source "drivers/block/enhanceio/Kconfig" + endif # BLK_DEV diff -Nur linux-3.10.30/drivers/block/Makefile linux-3.10.30-cubox-i/drivers/block/Makefile --- linux-3.10.30/drivers/block/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/Makefile 2014-03-08 20:33:27.000000000 +0100 @@ -42,5 +42,7 @@ obj-$(CONFIG_BLK_DEV_RSXX) += rsxx/ +obj-$(CONFIG_ENHANCEIO) += enhanceio/ + nvme-y := nvme-core.o nvme-scsi.o swim_mod-y := swim.o swim_asm.o diff -Nur linux-3.10.30/drivers/block/enhanceio/Kconfig linux-3.10.30-cubox-i/drivers/block/enhanceio/Kconfig --- linux-3.10.30/drivers/block/enhanceio/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/Kconfig 2014-03-08 20:33:27.000000000 +0100 @@ -0,0 +1,21 @@ +# +# EnhanceIO caching solution by STEC INC. +# + +config ENHANCEIO + tristate "Enable EnhanceIO" + depends on PROC_FS + default m + ---help--- + Based on Facebook's open source Flashcache project developed by + Mohan Srinivasan and hosted at "http://github.com", EnhanceIO is + a collection of (currently three) loadable kernel modules for + using SSDs as cache devices for traditional rotating hard disk + + The caching engine is a loadable kernel module ("enhanceio.ko") + implemented as a device mapper target. The cache replacement + policies are implemented as loadable kernel modules + ("enhanceio_fifo.ko", "enhanceio_lru.ko") that register with + the caching engine module. + + If unsure, say N. diff -Nur linux-3.10.30/drivers/block/enhanceio/Makefile linux-3.10.30-cubox-i/drivers/block/enhanceio/Makefile --- linux-3.10.30/drivers/block/enhanceio/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/Makefile 2014-03-08 20:33:27.000000000 +0100 @@ -0,0 +1,66 @@ +# test +# Makefile for EnhanceIO block device caching. +# + +COMMIT_REV ?= $(shell echo) +KERNEL_SOURCE_VERSION ?= $(shell uname -r) +KERNEL_TREE ?= /lib/modules/$(KERNEL_SOURCE_VERSION)/build +EXTRA_CFLAGS += -I$(KERNEL_TREE)/drivers/md -I./ -DCOMMIT_REV="\"$(COMMIT_REV)\"" +EXTRA_CFLAGS += -I$(KERNEL_TREE)/include/ -I$(KERNEL_TREE)/include/linux +# Check for RHEL/CentOS +RHEL5_VER ?= $(shell if [ -e /etc/redhat-release ]; then grep 5.[0-9] /etc/redhat-release; else false; fi) +RHEL5_SETUP := +ifneq "$(RHEL5_VER)" "" + RHEL5_SETUP := rhel5-setup + RHEL5_SPEC := /usr/src/redhat/SPECS/kernel.spec + RHEL5_TREE := /usr/src/redhat/BUILD/kernel-2.6.18/linux-$(shell uname -r).$(shell uname -i) + RHEL5_SRC := /usr/src/kernels/$(shell uname -r)-$(shell uname -i) + KERNEL_TREE := $(RHEL5_TREE) +endif +# Check for OpenVZ (/proc/vz) +OPENVZ_VER ?= $(shell if [ -e /proc/vz ]; then grep 5.[0-9] /etc/redhat-release; else false; fi) +ifneq "$(OPENVZ_VER)" "" + RHEL5_SPEC := /usr/src/redhat/SPECS/kernel-ovz.spec + RHEL5_TREE := /usr/src/redhat/BUILD/ovzkernel-2.6.18/linux-$(shell uname -r).$(shell uname -i) + KERNEL_TREE := $(RHEL5_TREE) +endif +obj-m += enhanceio.o enhanceio_lru.o enhanceio_fifo.o enhanceio_rand.o +enhanceio-y += \ + eio_conf.o \ + eio_ioctl.o \ + eio_main.o \ + eio_mem.o \ + eio_policy.o \ + eio_procfs.o \ + eio_setlru.o \ + eio_subr.o \ + eio_ttc.o +enhanceio_fifo-y += eio_fifo.o +enhanceio_rand-y += eio_rand.o +enhanceio_lru-y += eio_lru.o +.PHONY: all +all: modules +.PHONY: modules +modules: $(RHEL5_SETUP) + make -C $(KERNEL_TREE) M=$(PWD) modules V=0 +.PHONY: modules_install +modules_install: modules + install -o root -g root -m 0755 -d $(DESTDIR)/lib/modules/$(KERNEL_SOURCE_VERSION)/extra/enhanceio/ + install -o root -g root -m 0755 enhanceio.ko $(DESTDIR)/lib/modules/$(KERNEL_SOURCE_VERSION)/extra/enhanceio/ + install -o root -g root -m 0755 enhanceio_rand.ko $(DESTDIR)/lib/modules/$(KERNEL_SOURCE_VERSION)/extra/enhanceio/ + install -o root -g root -m 0755 enhanceio_fifo.ko $(DESTDIR)/lib/modules/$(KERNEL_SOURCE_VERSION)/extra/enhanceio/ + install -o root -g root -m 0755 enhanceio_lru.ko $(DESTDIR)/lib/modules/$(KERNEL_SOURCE_VERSION)/extra/enhanceio/ + depmod -a +.PHONY: install +install: modules_install +.PHONY: clean +clean: + make -C $(KERNEL_TREE) M=$(PWD) clean +.PHONY: rhel5-setup +rhel5-setup: $(RHEL5_TREE) + make -C $(RHEL5_TREE) oldconfig ; \ + make -C $(RHEL5_TREE) prepare modules_prepare + ln -s -f $(RHEL5_SRC)/Module.symvers $(RHEL5_TREE)/Module.symvers +$(RHEL5_TREE): + rpmbuild -bp --target=`uname -m` $(RHEL5_SPEC) 2>&1 | tee `dirname $(RHEL5_SPEC)`/prep.log + diff -Nur linux-3.10.30/drivers/block/enhanceio/eio.h linux-3.10.30-cubox-i/drivers/block/enhanceio/eio.h --- linux-3.10.30/drivers/block/enhanceio/eio.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio.h 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,1150 @@ +/* + * eio.h + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Saied Kazemi + * Added EnhanceIO-specific code. + * Siddharth Choudhuri + * Common data structures and definitions between Windows and Linux. + * Amit Kale + * Restructured much of the io code to split bio within map function instead + * of letting dm do it. + * Amit Kale + * Harish Pujari + * Designed and implemented the writeback caching mode + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* required for eio_subr.c */ +#include +#include +#include /* for sysinfo (mem) variables */ +#include +#include /* required for SSD failure handling */ +/* resolve conflict with scsi/scsi_device.h */ +#ifdef QUEUED +#undef QUEUED +#endif + +#if defined(__KERNEL__) && !defined(CONFIG_PROC_FS) +#error "EnhanceIO requires CONFIG_PROC_FS" +#endif /* __KERNEL__ && !CONFIG_PROC_FS */ + +#ifndef EIO_INC_H +#define EIO_INC_H + +/* Bit offsets for wait_on_bit_lock() */ +#define EIO_UPDATE_LIST 0 +#define EIO_HANDLE_REBOOT 1 + +/* + * It is to carry out 64bit division + * on 32bit architecture. + */ +static inline uint64_t +EIO_CALCULATE_PERCENTAGE(uint64_t x64, uint64_t y64) +{ + uint64_t result; + uint32_t h_y32 = y64 >> 32; + + result = x64 * 100; + + while (h_y32) { + h_y32 >>= 1; + y64 >>= 1; + result >>= 1; + } + do_div(result, y64); + return result; +} + + +static inline uint64_t +EIO_DIV(uint64_t dividend_64, uint32_t divisor_32) +{ + uint64_t result; + + result = dividend_64; + do_div(result, divisor_32); + return result; +} + + +static inline uint32_t +EIO_REM(uint64_t dividend_64, uint32_t divisor_32) +{ + uint64_t temp; + + temp = dividend_64; + return do_div(temp, divisor_32); +} + +static inline sector_t +eio_to_sector(uint64_t size_in_bytes) +{ + return size_in_bytes >> 9; +} + +struct eio_control_s { + unsigned long synch_flags; +}; + +int eio_wait_schedule(void *unused); + +struct eio_event { + struct task_struct *process; /* handle of the sleeping process */ +}; + +typedef long int index_t; + +extern int eio_reboot_notified; +extern mempool_t *_io_pool; +extern struct eio_control_s *eio_control; +extern struct work_struct _kcached_wq; +extern int eio_force_warm_boot; +extern atomic_t nr_cache_jobs; +extern mempool_t *_job_pool; + +/* + * This file has three sections as follows: + * + * Section 1: User space only + * Section 2: User space and kernel + * Section 3: Kernel only + * + * Each section may contain its own subsections. + */ + +/* + * Begin Section 1: User space only. + */ + +/* + * End Section 1: User space only. + */ + +/* + * Begin Section 2: User space and kernel. + */ + +/* States of a cache block */ +#define INVALID 0x0001 +#define VALID 0x0002 /* Valid */ +#define DISKREADINPROG 0x0004 /* Read from disk in progress */ +#define DISKWRITEINPROG 0x0008 /* Write to disk in progress */ +#define CACHEREADINPROG 0x0010 /* Read from cache in progress */ +#define CACHEWRITEINPROG 0x0020 /* Write to cache in progress */ +#define DIRTY 0x0040 /* Dirty, needs writeback to disk */ +#define QUEUED 0x0080 /* Other requests are queued for this block */ + +#define BLOCK_IO_INPROG (DISKREADINPROG | DISKWRITEINPROG | \ + CACHEREADINPROG | CACHEWRITEINPROG) +#define DIRTY_INPROG (VALID | DIRTY | CACHEWRITEINPROG) /* block being dirtied */ +#define CLEAN_INPROG (VALID | DIRTY | DISKWRITEINPROG) /* ongoing clean */ +#define ALREADY_DIRTY (VALID | DIRTY) /* block which is dirty to begin with for an I/O */ + +/* + * This is a special state used only in the following scenario as + * part of device (SSD) failure handling: + * + * ------| dev fail |------| dev resume |------------ + * ...-<--- Tf --><- Td -><---- Tr ---><-- Tn ---... + * |---- Normal ----|-- Degraded -------|-- Normal ---| + * + * Tf: Time during device failure. + * Td: Time after failure when the cache is in degraded mode. + * Tr: Time when the SSD comes back online. + * + * When a failed SSD is added back again, it should be treated + * as a cold SSD. + * + * If Td is very small, then there can be IOs that were initiated + * before or during Tf, and did not finish until the end of Tr. From + * the IO's viewpoint, the SSD was there when the IO was initiated + * and it was there when the IO was finished. These IOs need special + * handling as described below. + * + * To add the SSD as a cold cache device, we initialize all blocks + * to INVALID, execept for the ones that had IOs in progress before + * or during Tf. We mark such blocks as both VALID and INVALID. + * These blocks will be marked INVALID when finished. + */ +#define NO_SSD_IO_INPROG (VALID | INVALID) + +/* + * On Flash (cache metadata) Structures + */ +#define CACHE_MD_STATE_DIRTY 0x55daddee +#define CACHE_MD_STATE_CLEAN 0xacceded1 +#define CACHE_MD_STATE_FASTCLEAN 0xcafebabf +#define CACHE_MD_STATE_UNSTABLE 0xdeaddeee + +/* Do we have a read cache or a read-write cache */ +#define CACHE_MODE_WB 1 +#define CACHE_MODE_RO 2 +#define CACHE_MODE_WT 3 +#define CACHE_MODE_FIRST CACHE_MODE_WB +#define CACHE_MODE_LAST CACHE_MODE_WT +#define CACHE_MODE_DEFAULT CACHE_MODE_WT + +#define DEV_PATHLEN 128 +#define EIO_SUPERBLOCK_SIZE 4096 + +#define EIO_CLEAN_ABORT 0x00000000 +#define EIO_CLEAN_START 0x00000001 +#define EIO_CLEAN_KEEP 0x00000002 + +/* EIO magic number */ +#define EIO_MAGIC 0xE10CAC6E +#define EIO_BAD_MAGIC 0xBADCAC6E + +/* EIO version */ +#define EIO_SB_VERSION 3 /* kernel superblock version */ +#define EIO_SB_MAGIC_VERSION 3 /* version in which magic number was introduced */ + +union eio_superblock { + struct superblock_fields { + __le64 size; /* Cache size */ + __le32 block_size; /* Cache block size */ + __le32 assoc; /* Cache associativity */ + __le32 cache_sb_state; /* Clean shutdown ? */ + char cache_devname[DEV_PATHLEN]; + __le64 cache_devsize; + char disk_devname[DEV_PATHLEN]; + __le64 disk_devsize; + __le32 cache_version; + char cache_name[DEV_PATHLEN]; + __le32 mode; + __le32 repl_policy; + __le32 cache_flags; + __le32 magic; + __le32 cold_boot; /* cache to be started as cold after boot */ + char ssd_uuid[DEV_PATHLEN]; + __le64 cache_md_start_sect; /* cache metadata start (8K aligned) */ + __le64 cache_data_start_sect; /* cache data start (8K aligned) */ + __le32 dirty_high_threshold; + __le32 dirty_low_threshold; + __le32 dirty_set_high_threshold; + __le32 dirty_set_low_threshold; + __le32 time_based_clean_interval; + __le32 autoclean_threshold; + } sbf; + u_int8_t padding[EIO_SUPERBLOCK_SIZE]; +}; + +/* + * For EnhanceIO, we move the superblock from sector 0 to 128 + * and give it a full 4K. Also, in addition to the single + * "red-zone" buffer that separates metadata sectors from the + * data sectors, we allocate extra sectors so that we can + * align the data sectors on a 4K boundary. + * + * 64K 4K variable variable 8K variable variable + * +--------+--+--------+---------+---+--------+---------+ + * | unused |SB| align1 |metadata | Z | align2 | data... | + * +--------+--+--------+---------+---+--------+---------+ + * <------------- dmc->md_sectors ------------> + */ +#define EIO_UNUSED_SECTORS 128 +#define EIO_SUPERBLOCK_SECTORS 8 +#define EIO_REDZONE_SECTORS 16 +#define EIO_START 0 + +#define EIO_ALIGN1_SECTORS(index) ((index % 16) ? (24 - (index % 16)) : 8) +#define EIO_ALIGN2_SECTORS(index) ((index % 16) ? (16 - (index % 16)) : 0) +#define EIO_SUPERBLOCK_START (EIO_START + EIO_UNUSED_SECTORS) +#define EIO_METADATA_START(hd_start_sect) (EIO_SUPERBLOCK_START + \ + EIO_SUPERBLOCK_SECTORS + \ + EIO_ALIGN1_SECTORS(hd_start_sect)) + +#define EIO_EXTRA_SECTORS(start_sect, md_sects) (EIO_METADATA_START(start_sect) + \ + EIO_REDZONE_SECTORS + \ + EIO_ALIGN2_SECTORS(md_sects)) + +/* + * We do metadata updates only when a block trasitions from DIRTY -> CLEAN + * or from CLEAN -> DIRTY. Consequently, on an unclean shutdown, we only + * pick up blocks that are marked (DIRTY | CLEAN), we clean these and stick + * them in the cache. + * On a clean shutdown, we will sync the state for every block, and we will + * load every block back into cache on a restart. + */ +struct flash_cacheblock { + __le64 dbn; /* Sector number of the cached block */ + __le64 cache_state; +}; + +/* blksize in terms of no. of sectors */ +#define BLKSIZE_2K 4 +#define BLKSIZE_4K 8 +#define BLKSIZE_8K 16 + +/* + * Give me number of pages to allocated for the + * iosize x specified in terms of bytes. + */ +#define IO_PAGE_COUNT(x) (((x) + (PAGE_SIZE - 1)) / PAGE_SIZE) + +/* + * Macro that calculates number of biovecs to be + * allocated depending on the iosize and cache + * block size. + */ +#define IO_BVEC_COUNT(x, blksize) ({ \ + int count = IO_PAGE_COUNT(x); \ + switch ((blksize)) { \ + case BLKSIZE_2K: \ + count = count * 2; \ + break; \ + case BLKSIZE_4K: \ + case BLKSIZE_8K: \ + break; \ + } \ + count; \ + }) + +#define MD_MAX_NR_PAGES 16 +#define MD_BLOCKS_PER_PAGE ((PAGE_SIZE) / sizeof(struct flash_cacheblock)) +#define INDEX_TO_MD_PAGE(INDEX) ((INDEX) / MD_BLOCKS_PER_PAGE) +#define INDEX_TO_MD_PAGE_OFFSET(INDEX) ((INDEX) % MD_BLOCKS_PER_PAGE) + +#define MD_BLOCKS_PER_SECTOR (512 / (sizeof(struct flash_cacheblock))) +#define INDEX_TO_MD_SECTOR(INDEX) (EIO_DIV((INDEX), MD_BLOCKS_PER_SECTOR)) +#define INDEX_TO_MD_SECTOR_OFFSET(INDEX) (EIO_REM((INDEX), MD_BLOCKS_PER_SECTOR)) +#define MD_BLOCKS_PER_CBLOCK(dmc) (MD_BLOCKS_PER_SECTOR * (dmc)->block_size) + +#define METADATA_IO_BLOCKSIZE (256 * 1024) +#define METADATA_IO_BLOCKSIZE_SECT (METADATA_IO_BLOCKSIZE / 512) +#define SECTORS_PER_PAGE ((PAGE_SIZE) / 512) + +/* + * Cache persistence. + */ +#define CACHE_RELOAD 1 +#define CACHE_CREATE 2 +#define CACHE_FORCECREATE 3 + +/* + * Cache replacement policy. + */ +#define CACHE_REPL_FIFO 1 +#define CACHE_REPL_LRU 2 +#define CACHE_REPL_RANDOM 3 +#define CACHE_REPL_FIRST CACHE_REPL_FIFO +#define CACHE_REPL_LAST CACHE_REPL_RANDOM +#define CACHE_REPL_DEFAULT CACHE_REPL_FIFO + +struct eio_policy_and_name { + u8 p; + char *n; +}; + + +static const struct eio_policy_and_name eio_policy_names[] = { + { CACHE_REPL_FIFO, "fifo" }, + { CACHE_REPL_LRU, "lru" }, + { CACHE_REPL_RANDOM, "rand" }, +}; + + +/* + * Default cache parameters. + */ +#define DEFAULT_CACHE_ASSOC 512 +#define DEFAULT_CACHE_BLKSIZE 8 /* 4 KB */ + +/* + * Valid commands that can be written to "control". + * NOTE: Update CACHE_CONTROL_FLAG_MAX value whenever a new control flag is added + */ +#define CACHE_CONTROL_FLAG_MAX 7 +#define CACHE_VERBOSE_OFF 0 +#define CACHE_VERBOSE_ON 1 +#define CACHE_WRITEBACK_ON 2 /* register write back variables */ +#define CACHE_WRITEBACK_OFF 3 +#define CACHE_INVALIDATE_ON 4 /* register invalidate variables */ +#define CACHE_INVALIDATE_OFF 5 +#define CACHE_FAST_REMOVE_ON 6 /* do not write MD when destroying cache */ +#define CACHE_FAST_REMOVE_OFF 7 + +/* + * Bit definitions in "cache_flags". These are exported in Linux as + * hex in the "flags" output line of /proc/enhanceio//config. + */ + +#define CACHE_FLAGS_VERBOSE (1 << 0) +#define CACHE_FLAGS_INVALIDATE (1 << 1) +#define CACHE_FLAGS_FAST_REMOVE (1 << 2) +#define CACHE_FLAGS_DEGRADED (1 << 3) +#define CACHE_FLAGS_SSD_ADD_INPROG (1 << 4) +#define CACHE_FLAGS_MD8 (1 << 5) /* using 8-byte metadata (instead of 4-byte md) */ +#define CACHE_FLAGS_FAILED (1 << 6) +#define CACHE_FLAGS_STALE (1 << 7) +#define CACHE_FLAGS_SHUTDOWN_INPROG (1 << 8) +#define CACHE_FLAGS_MOD_INPROG (1 << 9) /* cache modification such as edit/delete in progress */ +#define CACHE_FLAGS_DELETED (1 << 10) +#define CACHE_FLAGS_INCORE_ONLY (CACHE_FLAGS_DEGRADED | \ + CACHE_FLAGS_SSD_ADD_INPROG | \ + CACHE_FLAGS_FAILED | \ + CACHE_FLAGS_SHUTDOWN_INPROG | \ + CACHE_FLAGS_MOD_INPROG | \ + CACHE_FLAGS_STALE | \ + CACHE_FLAGS_DELETED) /* need a proper definition */ + +/* flags that govern cold/warm enable after reboot */ +#define BOOT_FLAG_COLD_ENABLE (1 << 0) /* enable the cache as cold */ +#define BOOT_FLAG_FORCE_WARM (1 << 1) /* override the cold enable flag */ + +enum dev_notifier { + NOTIFY_INITIALIZER, + NOTIFY_SSD_ADD, + NOTIFY_SSD_REMOVED, + NOTIFY_SRC_REMOVED +}; + +/* + * End Section 2: User space and kernel. + */ + +/* + * Begin Section 3: Kernel only. + */ +#if defined(__KERNEL__) + +/* + * Subsection 3.1: Definitions. + */ + +#define EIO_SB_VERSION 3 /* kernel superblock version */ + +/* kcached/pending job states */ +#define READCACHE 1 +#define WRITECACHE 2 +#define READDISK 3 +#define WRITEDISK 4 +#define READFILL 5 /* Read Cache Miss Fill */ +#define INVALIDATE 6 + +/* Cache persistence */ +#define CACHE_RELOAD 1 +#define CACHE_CREATE 2 +#define CACHE_FORCECREATE 3 + +/* Sysctl defined */ +#define MAX_CLEAN_IOS_SET 2 +#define MAX_CLEAN_IOS_TOTAL 4 + +/* + * TBD + * Rethink on max, min, default values + */ +#define DIRTY_HIGH_THRESH_DEF 30 +#define DIRTY_LOW_THRESH_DEF 10 +#define DIRTY_SET_HIGH_THRESH_DEF 100 +#define DIRTY_SET_LOW_THRESH_DEF 30 + +#define CLEAN_FACTOR(sectors) ((sectors) >> 25) /* in 16 GB multiples */ +#define TIME_BASED_CLEAN_INTERVAL_DEF(dmc) (uint32_t)(CLEAN_FACTOR((dmc)->cache_size) ? \ + CLEAN_FACTOR((dmc)->cache_size) : 1) +#define TIME_BASED_CLEAN_INTERVAL_MAX 720 /* in minutes */ + +#define AUTOCLEAN_THRESH_DEF 128 /* Number of I/Os which puts a hold on time based cleaning */ +#define AUTOCLEAN_THRESH_MAX 1024 /* Number of I/Os which puts a hold on time based cleaning */ + +/* Inject a 5s delay between cleaning blocks and metadata */ +#define CLEAN_REMOVE_DELAY 5000 + +/* + * Subsection 2: Data structures. + */ + +typedef void (*eio_notify_fn)(int error, void *context); + +/* + * 4-byte metadata support. + */ + +#define EIO_MAX_SECTOR (((u_int64_t)1) << 40) +#ifdef __BIG_ENDIAN +struct md4 { + u_int8_t cache_state; + char dbn_bytes[3]; +}; +#else /* Little Endian */ +struct md4 { + char dbn_bytes[3]; + u_int8_t cache_state; +}; +#endif + +struct cacheblock { + union { + u_int32_t u_i_md4; + struct md4 u_s_md4; + } md4_u; +}; + +#define EIO_MD4_DBN_BITS (32 - 8) /* 8 bits for state */ +#define EIO_MD4_DBN_MASK ((1 << EIO_MD4_DBN_BITS) - 1) +#define EIO_MD4_INVALID (INVALID << EIO_MD4_DBN_BITS) + +/* + * 8-byte metadata support. + */ +#ifdef __BIG_ENDIAN +struct md8 { + u_int8_t cache_state; + char dbn_bytes[7]; +}; +#else /* little endian*/ +struct md8 { + char dbn_bytes[7]; + u_int8_t cache_state; +}; +#endif +struct cacheblock_md8 { + union { + u_int64_t u_i_md8; + struct md8 u_s_md8; + } md8_u; +}; + +#define EIO_MD8_DBN_BITS (64 - 8) /* 8 bits for state */ +#define EIO_MD8_DBN_MASK ((((u_int64_t)1) << EIO_MD8_DBN_BITS) - 1) +#define EIO_MD8_INVALID (((u_int64_t)INVALID) << EIO_MD8_DBN_BITS) +#define EIO_MD8(dmc) CACHE_MD8_IS_SET(dmc) + +/* Structure used for metadata update on-disk and in-core for writeback cache */ +struct mdupdate_request { + struct list_head list; /* to build mdrequest chain */ + struct work_struct work; /* work structure */ + struct cache_c *dmc; /* cache pointer */ + index_t set; /* set index */ + unsigned md_size; /* metadata size */ + unsigned mdbvec_count; /* count of bvecs allocated. */ + struct bio_vec *mdblk_bvecs; /* bvecs for updating md_blocks */ + atomic_t holdcount; /* I/O hold count */ + struct eio_bio *pending_mdlist; /* ebios pending for md update */ + struct eio_bio *inprog_mdlist; /* ebios processed for md update */ + int error; /* error during md update */ + struct mdupdate_request *next; /* next mdreq in the mdreq list .TBD. Deprecate */ +}; + +#define SETFLAG_CLEAN_INPROG 0x00000001 /* clean in progress on a set */ +#define SETFLAG_CLEAN_WHOLE 0x00000002 /* clean the set fully */ + +/* Structure used for doing operations and storing cache set level info */ +struct cache_set { + struct list_head list; + u_int32_t nr_dirty; /* number of dirty blocks */ + spinlock_t cs_lock; /* spin lock to protect struct fields */ + struct rw_semaphore rw_lock; /* reader-writer lock used for clean */ + unsigned int flags; /* misc cache set specific flags */ + struct mdupdate_request *mdreq; /* metadata update request pointer */ +}; + +struct eio_errors { + int disk_read_errors; + int disk_write_errors; + int ssd_read_errors; + int ssd_write_errors; + int memory_alloc_errors; + int no_cache_dev; + int no_source_dev; +}; + +/* + * Stats. Note that everything should be "atomic64_t" as + * code relies on it. + */ +#define SECTOR_STATS(statval, io_size) \ + atomic64_add(eio_to_sector(io_size), &statval); + +struct eio_stats { + atomic64_t reads; /* Number of reads */ + atomic64_t writes; /* Number of writes */ + atomic64_t read_hits; /* Number of cache hits */ + atomic64_t write_hits; /* Number of write hits (includes dirty write hits) */ + atomic64_t dirty_write_hits; /* Number of "dirty" write hits */ + atomic64_t cached_blocks; /* Number of cached blocks */ + atomic64_t rd_replace; /* Number of read cache replacements. TBD modify def doc */ + atomic64_t wr_replace; /* Number of write cache replacements. TBD modify def doc */ + atomic64_t noroom; /* No room in set */ + atomic64_t cleanings; /* blocks cleaned TBD modify def doc */ + atomic64_t md_write_dirty; /* Metadata sector writes dirtying block */ + atomic64_t md_write_clean; /* Metadata sector writes cleaning block */ + atomic64_t md_ssd_writes; /* How many md ssd writes did we do ? */ + atomic64_t uncached_reads; + atomic64_t uncached_writes; + atomic64_t uncached_map_size; + atomic64_t uncached_map_uncacheable; + atomic64_t disk_reads; + atomic64_t disk_writes; + atomic64_t ssd_reads; + atomic64_t ssd_writes; + atomic64_t ssd_readfills; + atomic64_t ssd_readfill_unplugs; + atomic64_t readdisk; + atomic64_t writedisk; + atomic64_t readcache; + atomic64_t readfill; + atomic64_t writecache; + atomic64_t wrtime_ms; /* total write time in ms */ + atomic64_t rdtime_ms; /* total read time in ms */ + atomic64_t readcount; /* total reads received so far */ + atomic64_t writecount; /* total writes received so far */ +}; + +#define PENDING_JOB_HASH_SIZE 32 +#define PENDING_JOB_HASH(index) ((index) % PENDING_JOB_HASH_SIZE) +#define SIZE_HIST (128 + 1) +#define EIO_COPY_PAGES 1024 /* Number of pages for I/O */ +#define MIN_JOBS 1024 +#define MIN_EIO_IO 4096 +#define MIN_DMC_BIO_PAIR 8192 + +/* Structure representing a sequence of sets(first to last set index) */ +struct set_seq { + index_t first_set; + index_t last_set; + struct set_seq *next; +}; + +/* EIO system control variables(tunables) */ +/* + * Adding synchonization is not worth the benefits. + */ +struct eio_sysctl { + uint32_t error_inject; + int32_t fast_remove; + int32_t zerostats; + int32_t do_clean; + uint32_t dirty_high_threshold; + uint32_t dirty_low_threshold; + uint32_t dirty_set_high_threshold; + uint32_t dirty_set_low_threshold; + uint32_t time_based_clean_interval; /* time after which dirty sets should clean */ + int32_t autoclean_threshold; + int32_t mem_limit_pct; + int32_t control; + u_int64_t invalidate; +}; + +/* forward declaration */ +struct lru_ls; + +/* Replacement for 'struct dm_dev' */ +struct eio_bdev { + struct block_device *bdev; + fmode_t mode; + char name[16]; +}; + +/* Replacement for 'struct dm_io_region */ +struct eio_io_region { + struct block_device *bdev; + sector_t sector; + sector_t count; /* If zero the region is ignored */ +}; + +/* + * Cache context + */ +struct cache_c { + struct list_head cachelist; + make_request_fn *origmfn; + char dev_info; /* partition or whole device */ + + sector_t dev_start_sect; + sector_t dev_end_sect; + int cache_rdonly; /* protected by ttc_write lock */ + struct eio_bdev *disk_dev; /* Source device */ + struct eio_bdev *cache_dev; /* Cache device */ + struct cacheblock *cache; /* Hash table for cache blocks */ + struct cache_set *cache_sets; + struct cache_c *next_cache; + struct kcached_job *readfill_queue; + struct work_struct readfill_wq; + + struct list_head cleanq; /* queue of sets to awaiting clean */ + struct eio_event clean_event; /* event to wait for, when cleanq is empty */ + spinlock_t clean_sl; /* spinlock to protect cleanq etc */ + void *clean_thread; /* OS specific thread object to handle cleanq */ + int clean_thread_running; /* to indicate that clean thread is running */ + atomic64_t clean_pendings; /* Number of sets pending to be cleaned */ + struct bio_vec *clean_dbvecs; /* Data bvecs for clean set */ + struct page **clean_mdpages; /* Metadata pages for clean set */ + int dbvec_count; + int mdpage_count; + int clean_excess_dirty; /* Clean in progress to bring cache dirty blocks in limits */ + atomic_t clean_index; /* set being cleaned, in case of force clean */ + + u_int64_t md_start_sect; /* Sector no. at which Metadata starts */ + u_int64_t md_sectors; /* Numbers of metadata sectors, including header */ + u_int64_t disk_size; /* Source size */ + u_int64_t size; /* Cache size */ + u_int32_t assoc; /* Cache associativity */ + u_int32_t block_size; /* Cache block size */ + u_int32_t block_shift; /* Cache block size in bits */ + u_int32_t block_mask; /* Cache block mask */ + u_int32_t consecutive_shift; /* Consecutive blocks size in bits */ + u_int32_t persistence; /* Create | Force create | Reload */ + u_int32_t mode; /* CACHE_MODE_{WB, RO, WT} */ + u_int32_t cold_boot; /* Cache should be started as cold after boot */ + u_int32_t bio_nr_pages; /* number of hardware sectors supported by SSD in terms of PAGE_SIZE */ + + spinlock_t cache_spin_lock; + long unsigned int cache_spin_lock_flags; /* See comments above spin_lock_irqsave_FLAGS */ + atomic_t nr_jobs; /* Number of I/O jobs */ + + u_int32_t cache_flags; + u_int32_t sb_state; /* Superblock state */ + u_int32_t sb_version; /* Superblock version */ + + int readfill_in_prog; + struct eio_stats eio_stats; /* Run time stats */ + struct eio_errors eio_errors; /* Error stats */ + int max_clean_ios_set; /* Max cleaning IOs per set */ + int max_clean_ios_total; /* Total max cleaning IOs */ + int clean_inprog; + atomic64_t nr_dirty; + atomic64_t nr_ios; + atomic64_t size_hist[SIZE_HIST]; + + void *sysctl_handle_common; + void *sysctl_handle_writeback; + void *sysctl_handle_invalidate; + + struct eio_sysctl sysctl_pending; /* sysctl values pending to become active */ + struct eio_sysctl sysctl_active; /* sysctl currently active */ + + char cache_devname[DEV_PATHLEN]; + char disk_devname[DEV_PATHLEN]; + char cache_name[DEV_PATHLEN]; + char cache_gendisk_name[DEV_PATHLEN]; /* Used for SSD failure checks */ + char cache_srcdisk_name[DEV_PATHLEN]; /* Used for SRC failure checks */ + char ssd_uuid[DEV_PATHLEN]; + + struct cacheblock_md8 *cache_md8; + sector_t cache_size; /* Cache size passed to ctr(), used by dmsetup info */ + sector_t cache_dev_start_sect; /* starting sector of cache device */ + u_int64_t index_zero; /* index of cache block with starting sector 0 */ + u_int32_t num_sets; /* number of cache sets */ + u_int32_t num_sets_bits; /* number of bits to encode "num_sets" */ + u_int64_t num_sets_mask; /* mask value for bits in "num_sets" */ + + struct eio_policy *policy_ops; /* Cache block Replacement policy */ + u_int32_t req_policy; /* Policy requested by the user */ + u_int32_t random; /* Use for random replacement policy */ + void *sp_cache_blk; /* Per cache-block data structure */ + void *sp_cache_set; /* Per cache-set data structure */ + struct lru_ls *dirty_set_lru; /* lru for dirty sets : lru_list_t */ + spinlock_t dirty_set_lru_lock; /* spinlock for dirty set lru */ + struct delayed_work clean_aged_sets_work; /* work item for clean_aged_sets */ + int is_clean_aged_sets_sched; /* to know whether clean aged sets is scheduled */ + struct workqueue_struct *mdupdate_q; /* Workqueue to handle md updates */ + struct workqueue_struct *callback_q; /* Workqueue to handle io callbacks */ +}; + +#define EIO_CACHE_IOSIZE 0 + +#define EIO_ROUND_SECTOR(dmc, sector) (sector & (~(unsigned long)(dmc->block_size - 1))) +#define EIO_ROUND_SET_SECTOR(dmc, sector) (sector & (~(unsigned long)((dmc->block_size * dmc->assoc) - 1))) + +/* + * The bit definitions are exported to the user space and are in the very beginning of the file. + */ +#define CACHE_VERBOSE_IS_SET(dmc) (((dmc)->cache_flags & CACHE_FLAGS_VERBOSE) ? 1 : 0) +#define CACHE_INVALIDATE_IS_SET(dmc) (((dmc)->cache_flags & CACHE_FLAGS_INVALIDATE) ? 1 : 0) +#define CACHE_FAST_REMOVE_IS_SET(dmc) (((dmc)->cache_flags & CACHE_FLAGS_FAST_REMOVE) ? 1 : 0) +#define CACHE_DEGRADED_IS_SET(dmc) (((dmc)->cache_flags & CACHE_FLAGS_DEGRADED) ? 1 : 0) +#define CACHE_SSD_ADD_INPROG_IS_SET(dmc) (((dmc)->cache_flags & CACHE_FLAGS_SSD_ADD_INPROG) ? 1 : 0) +#define CACHE_MD8_IS_SET(dmc) (((dmc)->cache_flags & CACHE_FLAGS_MD8) ? 1 : 0) +#define CACHE_FAILED_IS_SET(dmc) (((dmc)->cache_flags & CACHE_FLAGS_FAILED) ? 1 : 0) +#define CACHE_STALE_IS_SET(dmc) (((dmc)->cache_flags & CACHE_FLAGS_STALE) ? 1 : 0) + +/* Device failure handling. */ +#define CACHE_SRC_IS_ABSENT(dmc) (((dmc)->eio_errors.no_source_dev == 1) ? 1 : 0) + +#define AUTOCLEAN_THRESHOLD_CROSSED(dmc) \ + ((atomic64_read(&(dmc)->nr_ios) > (int64_t)(dmc)->sysctl_active.autoclean_threshold) || \ + ((dmc)->sysctl_active.autoclean_threshold == 0)) + +#define DIRTY_CACHE_THRESHOLD_CROSSED(dmc) \ + ((atomic64_read(&(dmc)->nr_dirty) - atomic64_read(&(dmc)->clean_pendings)) >= \ + (int64_t)((dmc)->sysctl_active.dirty_high_threshold * EIO_DIV((dmc)->size, 100)) && \ + ((dmc)->sysctl_active.dirty_high_threshold > (dmc)->sysctl_active.dirty_low_threshold)) + +#define DIRTY_SET_THRESHOLD_CROSSED(dmc, set) \ + (((dmc)->cache_sets[(set)].nr_dirty >= (u_int32_t)((dmc)->sysctl_active.dirty_set_high_threshold * (dmc)->assoc) / 100) && \ + ((dmc)->sysctl_active.dirty_set_high_threshold > (dmc)->sysctl_active.dirty_set_low_threshold)) + +/* + * Do not reverse the order of disk and cache! Code + * relies on this ordering. (Eg: eio_dm_io_async_bvec()). + */ +struct job_io_regions { + struct eio_io_region disk; /* has to be the first member */ + struct eio_io_region cache; /* has to be the second member */ +}; + +#define EB_MAIN_IO 1 +#define EB_SUBORDINATE_IO 2 +#define EB_INVAL 4 +#define GET_BIO_FLAGS(ebio) ((ebio)->eb_bc->bc_bio->bi_rw) +#define VERIFY_BIO_FLAGS(ebio) EIO_ASSERT((ebio) && (ebio)->eb_bc && (ebio)->eb_bc->bc_bio) + +#define SET_BARRIER_FLAGS(rw_flags) (rw_flags |= (REQ_WRITE | REQ_FLUSH)) + +struct eio_bio { + int eb_iotype; + struct bio_container *eb_bc; + unsigned eb_cacheset; + sector_t eb_sector; /*sector number*/ + unsigned eb_size; /*size in bytes*/ + struct bio_vec *eb_bv; /*bvec pointer*/ + unsigned eb_nbvec; /*number of bio_vecs*/ + int eb_dir; /* io direction*/ + struct eio_bio *eb_next; /*used for splitting reads*/ + index_t eb_index; /*for read bios*/ + atomic_t eb_holdcount; /* ebio hold count, currently used only for dirty block I/O */ + struct bio_vec eb_rbv[0]; +}; + +enum eio_io_dir { + EIO_IO_INVALID_DIR = 0, + CACHED_WRITE, + CACHED_READ, + UNCACHED_WRITE, + UNCACHED_READ, + UNCACHED_READ_AND_READFILL +}; + +/* ASK + * Container for all eio_bio corresponding to a given bio + */ +struct bio_container { + spinlock_t bc_lock; /* lock protecting the bc fields */ + atomic_t bc_holdcount; /* number of ebios referencing bc */ + struct bio *bc_bio; /* bio for the bc */ + struct cache_c *bc_dmc; /* cache structure */ + struct eio_bio *bc_mdlist; /* ebios waiting for md update */ + int bc_mdwait; /* count of ebios that will do md update */ + struct mdupdate_request *mdreqs; /* mdrequest structures required for md update */ + struct set_seq *bc_setspan; /* sets spanned by the bc(used only for wb) */ + struct set_seq bc_singlesspan; /* used(by wb) if bc spans a single set sequence */ + enum eio_io_dir bc_dir; /* bc I/O direction */ + int bc_error; /* error encountered during processing bc */ + unsigned long bc_iotime; /* maintains i/o time in jiffies */ + struct bio_container *bc_next; /* next bc in the chain */ +}; + +/* structure used as callback context during synchronous I/O */ +struct sync_io_context { + struct rw_semaphore sio_lock; + unsigned long sio_error; +}; + +struct kcached_job { + struct list_head list; + struct work_struct work; + struct cache_c *dmc; + struct eio_bio *ebio; + struct job_io_regions job_io_regions; + index_t index; + int action; + int error; + struct flash_cacheblock *md_sector; + struct bio_vec md_io_bvec; + struct kcached_job *next; +}; + +struct ssd_rm_list { + struct cache_c *dmc; + int action; + dev_t devt; + enum dev_notifier note; + struct list_head list; +}; + +struct dbn_index_pair { + sector_t dbn; + index_t index; +}; + +/* + * Subsection 3: Function prototypes and definitions. + */ + +struct kcached_job *eio_alloc_cache_job(void); +void eio_free_cache_job(struct kcached_job *job); +struct kcached_job *pop(struct list_head *jobs); +void push(struct list_head *jobs, struct kcached_job *job); +void do_work(struct work_struct *unused); +void update_job_cacheregion(struct kcached_job *job, struct cache_c *dmc, + struct eio_bio *bio); +void push_io(struct kcached_job *job); +void push_md_io(struct kcached_job *job); +void push_md_complete(struct kcached_job *job); +void push_uncached_io_complete(struct kcached_job *job); +int eio_io_empty(void); +int eio_md_io_empty(void); +int eio_md_complete_empty(void); +void eio_md_write_done(struct kcached_job *job); +void eio_ssderror_diskread(struct kcached_job *job); +void eio_md_write(struct kcached_job *job); +void eio_md_write_kickoff(struct kcached_job *job); +void eio_do_readfill(struct work_struct *work); +void eio_comply_dirty_thresholds(struct cache_c *dmc, index_t set); +void eio_clean_all(struct cache_c *dmc); +void eio_clean_for_reboot(struct cache_c *dmc); +void eio_clean_aged_sets(struct work_struct *work); +void eio_comply_dirty_thresholds(struct cache_c *dmc, index_t set); +#ifndef SSDCACHE +void eio_reclaim_lru_movetail(struct cache_c *dmc, index_t index, + struct eio_policy *); +#endif /* !SSDCACHE */ +int eio_io_sync_vm(struct cache_c *dmc, struct eio_io_region *where, int rw, + struct bio_vec *bvec, int nbvec); +int eio_io_sync_pages(struct cache_c *dmc, struct eio_io_region *where, int rw, + struct page **pages, int num_bvecs); +void eio_update_sync_progress(struct cache_c *dmc); +void eio_plug_cache_device(struct cache_c *dmc); +void eio_unplug_cache_device(struct cache_c *dmc); +void eio_plug_disk_device(struct cache_c *dmc); +void eio_unplug_disk_device(struct cache_c *dmc); +int dm_io_async_bvec(unsigned int num_regions, struct eio_io_region *where, + int rw, struct bio_vec *bvec, eio_notify_fn fn, + void *context); +void eio_put_cache_device(struct cache_c *dmc); +void eio_suspend_caching(struct cache_c *dmc, enum dev_notifier note); +void eio_resume_caching(struct cache_c *dmc, char *dev); +int eio_ctr_ssd_add(struct cache_c *dmc, char *dev); + +/* procfs */ +void eio_module_procfs_init(void); +void eio_module_procfs_exit(void); +void eio_procfs_ctr(struct cache_c *dmc); +void eio_procfs_dtr(struct cache_c *dmc); + +int eio_sb_store(struct cache_c *dmc); + +int eio_md_destroy(struct dm_target *tip, char *namep, char *srcp, char *cachep, + int force); + +/* eio_conf.c */ +extern int eio_ctr(struct dm_target *ti, unsigned int argc, char **argv); +extern void eio_dtr(struct dm_target *ti); +extern int eio_md_destroy(struct dm_target *tip, char *namep, char *srcp, + char *cachep, int force); +extern int eio_ctr_ssd_add(struct cache_c *dmc, char *dev); + +/* thread related functions */ +void *eio_create_thread(int (*func)(void *), void *context, char *name); +void eio_thread_exit(long exit_code); +void eio_wait_thread_exit(void *thrdptr, int *notifier); + +/* eio_main.c */ +extern int eio_map(struct cache_c *, struct request_queue *, struct bio *); +extern void eio_md_write_done(struct kcached_job *job); +extern void eio_ssderror_diskread(struct kcached_job *job); +extern void eio_md_write(struct kcached_job *job); +extern void eio_md_write_kickoff(struct kcached_job *job); +extern void eio_do_readfill(struct work_struct *work); +extern void eio_check_dirty_thresholds(struct cache_c *dmc, index_t set); +extern void eio_clean_all(struct cache_c *dmc); +extern int eio_clean_thread_proc(void *context); +extern void eio_touch_set_lru(struct cache_c *dmc, index_t set); +extern void eio_inval_range(struct cache_c *dmc, sector_t iosector, + unsigned iosize); +extern int eio_invalidate_sanity_check(struct cache_c *dmc, u_int64_t iosector, + u_int64_t *iosize); +/* + * Invalidates all cached blocks without waiting for them to complete + * Should be called with incoming IO suspended + */ +extern int eio_invalidate_cache(struct cache_c *dmc); + +/* eio_mem.c */ +extern int eio_mem_init(struct cache_c *dmc); +extern u_int32_t eio_hash_block(struct cache_c *dmc, sector_t dbn); +extern unsigned int eio_shrink_dbn(struct cache_c *dmc, sector_t dbn); +extern sector_t eio_expand_dbn(struct cache_c *dmc, u_int64_t index); +extern void eio_invalidate_md(struct cache_c *dmc, u_int64_t index); +extern void eio_md4_dbn_set(struct cache_c *dmc, u_int64_t index, + u_int32_t dbn_24); +extern void eio_md8_dbn_set(struct cache_c *dmc, u_int64_t index, sector_t dbn); + +/* eio_procfs.c */ +extern void eio_module_procfs_init(void); +extern void eio_module_procfs_exit(void); +extern void eio_procfs_ctr(struct cache_c *dmc); +extern void eio_procfs_dtr(struct cache_c *dmc); +extern int eio_version_query(size_t buf_sz, char *bufp); + +/* eio_subr.c */ +extern void eio_free_cache_job(struct kcached_job *job); +extern void eio_do_work(struct work_struct *unused); +extern struct kcached_job *eio_new_job(struct cache_c *dmc, struct eio_bio *bio, + index_t index); +extern void eio_push_ssdread_failures(struct kcached_job *job); +extern void eio_push_md_io(struct kcached_job *job); +extern void eio_push_md_complete(struct kcached_job *job); +extern void eio_push_uncached_io_complete(struct kcached_job *job); +extern int eio_io_empty(void); +extern int eio_io_sync_vm(struct cache_c *dmc, struct eio_io_region *where, + int rw, struct bio_vec *bvec, int nbvec); +extern void eio_unplug_cache_device(struct cache_c *dmc); +extern void eio_put_cache_device(struct cache_c *dmc); +extern void eio_suspend_caching(struct cache_c *dmc, enum dev_notifier note); +extern void eio_resume_caching(struct cache_c *dmc, char *dev); + +static inline void +EIO_DBN_SET(struct cache_c *dmc, u_int64_t index, sector_t dbn) +{ + if (EIO_MD8(dmc)) + eio_md8_dbn_set(dmc, index, dbn); + else + eio_md4_dbn_set(dmc, index, eio_shrink_dbn(dmc, dbn)); + if (dbn == 0) + dmc->index_zero = index; +} + +static inline u_int64_t EIO_DBN_GET(struct cache_c *dmc, u_int64_t index) +{ + if (EIO_MD8(dmc)) + return dmc->cache_md8[index].md8_u.u_i_md8 & EIO_MD8_DBN_MASK; + + return eio_expand_dbn(dmc, index); +} + +static inline void +EIO_CACHE_STATE_SET(struct cache_c *dmc, u_int64_t index, u_int8_t cache_state) +{ + if (EIO_MD8(dmc)) + dmc->cache_md8[index].md8_u.u_s_md8.cache_state = cache_state; + else + dmc->cache[index].md4_u.u_s_md4.cache_state = cache_state; +} + +static inline u_int8_t +EIO_CACHE_STATE_GET(struct cache_c *dmc, u_int64_t index) +{ + u_int8_t cache_state; + + if (EIO_MD8(dmc)) + cache_state = dmc->cache_md8[index].md8_u.u_s_md8.cache_state; + else + cache_state = dmc->cache[index].md4_u.u_s_md4.cache_state; + return cache_state; +} + +static inline void +EIO_CACHE_STATE_OFF(struct cache_c *dmc, index_t index, u_int8_t bitmask) +{ + u_int8_t cache_state = EIO_CACHE_STATE_GET(dmc, index); + + cache_state &= ~bitmask; + EIO_CACHE_STATE_SET(dmc, index, cache_state); +} + +static inline void +EIO_CACHE_STATE_ON(struct cache_c *dmc, index_t index, u_int8_t bitmask) +{ + u_int8_t cache_state = EIO_CACHE_STATE_GET(dmc, index); + + cache_state |= bitmask; + EIO_CACHE_STATE_SET(dmc, index, cache_state); +} + +void eio_set_warm_boot(void); +#endif /* defined(__KERNEL__) */ + +#include "eio_ioctl.h" + +/* resolve conflict with scsi/scsi_device.h */ +#ifdef __KERNEL__ + +#ifdef EIO_ASSERT +#undef EIO_ASSERT +#endif +/*Always compiled in*/ +#define EIO_ASSERT(x) BUG_ON(unlikely(!(x))) + +extern sector_t eio_get_device_size(struct eio_bdev *); +extern sector_t eio_get_device_start_sect(struct eio_bdev *); +#endif /* __KERNEL__ */ + +#define EIO_INIT_EVENT(ev) ((ev)->process = NULL) + +/*Assumes that the macro gets called under the same spinlock as in wait event*/ +#define EIO_SET_EVENT_AND_UNLOCK(ev, sl, flags) \ + do { \ + struct task_struct *p = NULL; \ + if ((ev)->process) { \ + (p) = (ev)->process; \ + (ev)->process = NULL; \ + } \ + spin_unlock_irqrestore((sl), flags); \ + if (p) { \ + (void)wake_up_process(p); \ + } \ + } while (0) + +/*Assumes that the spin lock sl is taken while calling this macro*/ +#define EIO_WAIT_EVENT(ev, sl, flags) \ + do { \ + (ev)->process = current; \ + set_current_state(TASK_INTERRUPTIBLE); \ + spin_unlock_irqrestore((sl), flags); \ + (void)schedule_timeout(10 * HZ); \ + spin_lock_irqsave((sl), flags); \ + (ev)->process = NULL; \ + } while (0) + +#define EIO_CLEAR_EVENT(ev) ((ev)->process = NULL) + +#include "eio_setlru.h" +#include "eio_policy.h" +#define EIO_CACHE(dmc) (EIO_MD8(dmc) ? (void *)dmc->cache_md8 : (void *)dmc->cache) + +#endif /* !EIO_INC_H */ diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_conf.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_conf.c --- linux-3.10.30/drivers/block/enhanceio/eio_conf.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_conf.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,2630 @@ +/* + * eio_conf.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * Amit Kale + * Restructured much of the io code to split bio within map function instead + * of letting dm do it. + * Simplified queued logic for write through. + * Amit Kale + * Harish Pujari + * Designed and implemented the writeback caching mode + * + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "eio.h" +#include "eio_ttc.h" + +#define KMEM_CACHE_JOB "eio-kcached-jobs" +#define KMEM_EIO_IO "eio-io-context" +#define KMEM_DMC_BIO_PAIR "eio-dmc-bio-pair" +/* #define KMEM_CACHE_PENDING_JOB "eio-pending-jobs" */ + +static struct cache_c *cache_list_head; +struct work_struct _kcached_wq; + +static struct kmem_cache *_job_cache; +struct kmem_cache *_io_cache; /* cache of eio_context objects */ +mempool_t *_job_pool; +mempool_t *_io_pool; /* pool of eio_context object */ + +atomic_t nr_cache_jobs; + + +LIST_HEAD(ssd_rm_list); +int ssd_rm_list_not_empty; +spinlock_t ssd_rm_list_lock; + +struct eio_control_s *eio_control; + +int eio_force_warm_boot; +static int eio_notify_reboot(struct notifier_block *nb, unsigned long action, + void *x); +void eio_stop_async_tasks(struct cache_c *dmc); +static int eio_notify_ssd_rm(struct notifier_block *nb, unsigned long action, + void *x); + +/* + * The notifiers are registered in descending order of priority and + * executed in descending order or priority. We should be run before + * any notifiers of ssd's or other block devices. Typically, devices + * use a priority of 0. + * XXX - If in the future we happen to use a md device as the cache + * block device, we have a problem because md uses a priority of + * INT_MAX as well. But we want to run before the md's reboot notifier ! + */ +static struct notifier_block eio_reboot_notifier = { + .notifier_call = eio_notify_reboot, + .next = NULL, + .priority = INT_MAX, /* should be > ssd pri's and disk dev pri's */ +}; + +static struct notifier_block eio_ssd_rm_notifier = { + .notifier_call = eio_notify_ssd_rm, + .next = NULL, + .priority = 0, +}; + +int eio_wait_schedule(void *unused) +{ + + schedule(); + return 0; +} + +/* + * Check if the System RAM threshold > requested memory, don't care + * if threshold is set to 0. Return value is 0 for fail and 1 for success. + */ +static inline int eio_mem_available(struct cache_c *dmc, size_t size) +{ + struct sysinfo si; + + if (unlikely + (dmc->sysctl_active.mem_limit_pct <= 0 + || dmc->sysctl_active.mem_limit_pct >= 100)) + return 1; + + si_meminfo(&si); + return (((si.freeram << PAGE_SHIFT) * + dmc->sysctl_active.mem_limit_pct) / 100) > size; +} + +/* create a new thread and call the specified function */ +void *eio_create_thread(int (*func)(void *), void *context, char *name) +{ + return kthread_run(func, context, "%s", name); +} + +/* wait for the given thread to exit */ +void eio_wait_thread_exit(void *thrdptr, int *running) +{ + while (*running) + msleep(1); + + /*do_exit() would be called within the thread func itself*/ + + return; +} + +/* thread exit self */ +void eio_thread_exit(long exit_code) +{ + do_exit(exit_code); +} +const char *eio_policy_to_name(u8 p) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(eio_policy_names); i++) + if (eio_policy_names[i].p == p) + return eio_policy_names[i].n; + + return NULL; +} + + +inline int eio_policy_init(struct cache_c *dmc) +{ + + if (dmc->req_policy == 0) + dmc->req_policy = CACHE_REPL_DEFAULT; + dmc->policy_ops = eio_get_policy(dmc->req_policy); + if (dmc->policy_ops == NULL) { + pr_err("eio_policy_init: Failed to initialize %s(%d) policy", + eio_policy_to_name(dmc->req_policy), dmc->req_policy); + return -EINVAL; + } else { + /* Back pointer to reference dmc from policy_ops */ + dmc->policy_ops->sp_dmc = dmc; + pr_info("Setting replacement policy to %s (%d)", + eio_policy_to_name(dmc->policy_ops->sp_name), + dmc->policy_ops->sp_name); + return 0; + } +} + +static int eio_jobs_init(void) +{ + + _job_cache = _io_cache = NULL; + _job_pool = _io_pool = NULL; + + _job_cache = kmem_cache_create(KMEM_CACHE_JOB, + sizeof(struct kcached_job), + __alignof__(struct kcached_job), + 0, NULL); + if (!_job_cache) + return -ENOMEM; + + _job_pool = mempool_create(MIN_JOBS, mempool_alloc_slab, + mempool_free_slab, _job_cache); + if (!_job_pool) + goto out; + + _io_cache = kmem_cache_create(KMEM_EIO_IO, + sizeof(struct eio_context), + __alignof__(struct eio_context), 0, NULL); + if (!_io_cache) + goto out; + + _io_pool = mempool_create(MIN_EIO_IO, mempool_alloc_slab, + mempool_free_slab, _io_cache); + if (!_io_pool) + goto out; + + return 0; + +out: + if (_io_pool) + mempool_destroy(_io_pool); + if (_io_cache) + kmem_cache_destroy(_io_cache); + if (_job_pool) + mempool_destroy(_job_pool); + if (_job_cache) + kmem_cache_destroy(_job_cache); + + _job_pool = _io_pool = NULL; + _job_cache = _io_cache = NULL; + return -ENOMEM; +} + +static void eio_jobs_exit(void) +{ + + mempool_destroy(_io_pool); + mempool_destroy(_job_pool); + kmem_cache_destroy(_io_cache); + kmem_cache_destroy(_job_cache); + + _job_pool = _io_pool = NULL; + _job_cache = _io_cache = NULL; +} + +static int eio_kcached_init(struct cache_c *dmc) +{ + + /* init_waitqueue_head(&dmc->destroyq); */ + atomic_set(&dmc->nr_jobs, 0); + return 0; +} + +static void eio_kcached_client_destroy(struct cache_c *dmc) +{ + + /* Wait for all IOs + /wait_event(dmc->destroyq, !atomic_read(&dmc->nr_jobs));*/ +} + +/* Store the cache superblock on ssd */ +int eio_sb_store(struct cache_c *dmc) +{ + union eio_superblock *sb = NULL; + struct eio_io_region where; + int error; + + struct bio_vec *sb_pages; + int nr_pages; + int page_count, page_index; + + if ((unlikely(CACHE_FAILED_IS_SET(dmc)) || CACHE_DEGRADED_IS_SET(dmc)) + && (!CACHE_SSD_ADD_INPROG_IS_SET(dmc))) { + pr_err + ("sb_store: Cannot write superblock for cache \"%s\", in degraded/failed mode.\n", + dmc->cache_name); + return -ENODEV; + } + + page_count = 0; + nr_pages = EIO_SUPERBLOCK_SIZE / PAGE_SIZE; + EIO_ASSERT(nr_pages != 0); + + sb_pages = eio_alloc_pages(nr_pages, &page_count); + if (sb_pages == NULL) { + pr_err("sb_store: System memory too low.\n"); + return -ENOMEM; + } + + EIO_ASSERT(page_count == nr_pages); + + nr_pages = page_count; + page_index = 0; + sb = (union eio_superblock *)kmap(sb_pages[page_index].bv_page); + + sb->sbf.cache_sb_state = cpu_to_le32(dmc->sb_state); + sb->sbf.block_size = cpu_to_le32(dmc->block_size); + sb->sbf.size = cpu_to_le32(dmc->size); + sb->sbf.assoc = cpu_to_le32(dmc->assoc); + sb->sbf.cache_md_start_sect = cpu_to_le64(dmc->md_start_sect); + sb->sbf.cache_data_start_sect = cpu_to_le64(dmc->md_sectors); + strncpy(sb->sbf.disk_devname, dmc->disk_devname, DEV_PATHLEN); + strncpy(sb->sbf.cache_devname, dmc->cache_devname, DEV_PATHLEN); + strncpy(sb->sbf.ssd_uuid, dmc->ssd_uuid, DEV_PATHLEN - 1); + sb->sbf.cache_devsize = cpu_to_le64(eio_to_sector(eio_get_device_size(dmc->cache_dev))); + sb->sbf.disk_devsize = cpu_to_le64(eio_to_sector(eio_get_device_size(dmc->disk_dev))); + sb->sbf.cache_version = cpu_to_le32(dmc->sb_version); + strncpy(sb->sbf.cache_name, dmc->cache_name, DEV_PATHLEN); + sb->sbf.cache_name[DEV_PATHLEN - 1] = '\0'; + sb->sbf.mode = cpu_to_le32(dmc->mode); + spin_lock_irqsave(&dmc->cache_spin_lock, dmc->cache_spin_lock_flags); + sb->sbf.repl_policy = cpu_to_le32(dmc->req_policy); + sb->sbf.cache_flags = cpu_to_le32(dmc->cache_flags & ~CACHE_FLAGS_INCORE_ONLY); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + if (dmc->sb_version) + sb->sbf.magic = cpu_to_le32(EIO_MAGIC); + else + sb->sbf.magic = cpu_to_le32(EIO_BAD_MAGIC); + + sb->sbf.cold_boot = cpu_to_le32(dmc->cold_boot); + if (le32_to_cpu(sb->sbf.cold_boot) && eio_force_warm_boot) + sb->sbf.cold_boot = cpu_to_le32(le32_to_cpu(sb->sbf.cold_boot) | + BOOT_FLAG_FORCE_WARM); + + sb->sbf.dirty_high_threshold = cpu_to_le32(dmc->sysctl_active.dirty_high_threshold); + sb->sbf.dirty_low_threshold = cpu_to_le32(dmc->sysctl_active.dirty_low_threshold); + sb->sbf.dirty_set_high_threshold = + cpu_to_le32(dmc->sysctl_active.dirty_set_high_threshold); + sb->sbf.dirty_set_low_threshold = + cpu_to_le32(dmc->sysctl_active.dirty_set_low_threshold); + sb->sbf.time_based_clean_interval = + cpu_to_le32(dmc->sysctl_active.time_based_clean_interval); + sb->sbf.autoclean_threshold = cpu_to_le32(dmc->sysctl_active.autoclean_threshold); + + /* write out to ssd */ + where.bdev = dmc->cache_dev->bdev; + where.sector = EIO_SUPERBLOCK_START; + where.count = eio_to_sector(EIO_SUPERBLOCK_SIZE); + error = eio_io_sync_vm(dmc, &where, WRITE, sb_pages, nr_pages); + if (error) { + pr_err + ("sb_store: Could not write out superblock to sector %llu (error %d) for cache \"%s\".\n", + (unsigned long long)where.sector, error, dmc->cache_name); + } + + /* free the allocated pages here */ + if (sb_pages) { + kunmap(sb_pages[0].bv_page); + for (page_index = 0; page_index < nr_pages; page_index++) + put_page(sb_pages[page_index].bv_page); + kfree(sb_pages); + sb_pages = NULL; + } + + return error; +} + +/* + * Write out the metadata one sector at a time. + * Then dump out the superblock. + */ +int eio_md_store(struct cache_c *dmc) +{ + struct flash_cacheblock *next_ptr; + struct eio_io_region where; + sector_t i; + int j, k; + int num_valid = 0, num_dirty = 0; + int error; + int write_errors = 0; + sector_t sectors_written = 0, sectors_expected = 0; /* debug */ + int slots_written = 0; /* How many cache slots did we fill in this MD io block ? */ + + struct bio_vec *pages; + int nr_pages; + int page_count, page_index; + void **pg_virt_addr; + + if (unlikely(CACHE_FAILED_IS_SET(dmc)) + || unlikely(CACHE_DEGRADED_IS_SET(dmc))) { + pr_err + ("md_store: Cannot write metadata in failed/degraded mode for cache \"%s\".", + dmc->cache_name); + return -ENODEV; + } + + if (CACHE_FAST_REMOVE_IS_SET(dmc)) { + if (CACHE_VERBOSE_IS_SET(dmc)) + pr_info("Skipping writing out metadata to cache"); + if (!dmc->sb_version) { + + /* + * Incase of delete, flush the superblock + * irrespective of fast_remove being set. + */ + + goto sb_store; + } + return 0; + } + + if (!eio_mem_available(dmc, METADATA_IO_BLOCKSIZE_SECT)) { + pr_err + ("md_store: System memory too low for allocating metadata IO buffers"); + return -ENOMEM; + } + + page_count = 0; + pages = eio_alloc_pages(dmc->bio_nr_pages, &page_count); + if (pages == NULL) { + pr_err("eio_md_store: System memory too low."); + return -ENOMEM; + } + + /* get the exact number of pages allocated */ + nr_pages = page_count; + where.bdev = dmc->cache_dev->bdev; + where.sector = dmc->md_start_sect; + slots_written = 0; + page_index = 0; + + pg_virt_addr = kmalloc(nr_pages * (sizeof(void *)), GFP_KERNEL); + if (pg_virt_addr == NULL) { + pr_err("eio_md_store: System memory too low."); + for (k = 0; k < nr_pages; k++) + put_page(pages[k].bv_page); + kfree(pages); + return -ENOMEM; + } + + for (k = 0; k < nr_pages; k++) + pg_virt_addr[k] = kmap(pages[k].bv_page); + + next_ptr = (struct flash_cacheblock *)pg_virt_addr[page_index]; + j = MD_BLOCKS_PER_PAGE; + + pr_info("Writing out metadata to cache device. Please wait..."); + + for (i = 0; i < dmc->size; i++) { + if (EIO_CACHE_STATE_GET(dmc, (index_t)i) & VALID) + num_valid++; + if (EIO_CACHE_STATE_GET(dmc, (index_t)i) & DIRTY) + num_dirty++; + next_ptr->dbn = cpu_to_le64(EIO_DBN_GET(dmc, i)); + next_ptr->cache_state = cpu_to_le64(EIO_CACHE_STATE_GET(dmc, (index_t)i) & + (INVALID | VALID | DIRTY)); + + next_ptr++; + slots_written++; + j--; + if (j == 0) { + /* + * Filled the page, goto the next page. + */ + page_index++; + + if (slots_written == + (int)(MD_BLOCKS_PER_PAGE * nr_pages)) { + /* + * Wrote out an entire metadata IO block, write the block to the ssd. + */ + where.count = + slots_written / MD_BLOCKS_PER_SECTOR; + slots_written = 0; + page_index = 0; + sectors_written += where.count; /* debug */ + + error = + eio_io_sync_vm(dmc, &where, WRITE, pages, + nr_pages); + + if (error) { + write_errors++; + pr_err + ("md_store: Could not write out metadata to sector %llu (error %d)", + (unsigned long long)where.sector, error); + } + where.sector += where.count; /* Advance offset */ + } + /* Move next slot pointer into next sector */ + next_ptr = + (struct flash_cacheblock *)pg_virt_addr[page_index]; + j = MD_BLOCKS_PER_PAGE; + } + } + + if (next_ptr != (struct flash_cacheblock *)pg_virt_addr[0]) { + /* Write the remaining last page out */ + EIO_ASSERT(slots_written > 0); + + where.count = slots_written / MD_BLOCKS_PER_SECTOR; + + if (slots_written % MD_BLOCKS_PER_SECTOR) + where.count++; + + sectors_written += where.count; + + /* + * This may happen that we are at the beginning of the next page + * and did not fill up any slots in this page. Verify this condition + * and set page_index accordingly. + */ + + if (next_ptr != + (struct flash_cacheblock *)pg_virt_addr[page_index]) { + unsigned offset; + + slots_written = slots_written % MD_BLOCKS_PER_PAGE; + + /* + * We have some extra slots written at this page_index. + * Let us try to zero out the remaining page size before submitting + * this page. + */ + offset = + slots_written * (sizeof(struct flash_cacheblock)); + memset(pg_virt_addr[page_index] + offset, 0, + PAGE_SIZE - offset); + + page_index++; + } + + error = eio_io_sync_vm(dmc, &where, WRITE, pages, page_index); + /* XXX: should we call eio_sb_store() on error ?? */ + if (error) { + write_errors++; + pr_err + ("md_store: Could not write out metadata to sector %llu (error %d)", + (unsigned long long)where.sector, error); + } + } + + /* Debug Tests */ + sectors_expected = EIO_DIV(dmc->size, MD_BLOCKS_PER_SECTOR); + if (EIO_REM(dmc->size, MD_BLOCKS_PER_SECTOR)) + sectors_expected++; + EIO_ASSERT(sectors_expected == sectors_written); + /* XXX: should we call eio_sb_store() on error ?? */ + if (sectors_expected != sectors_written) { + pr_err + ("md_store: Sector mismatch! sectors_expected=%llu, sectors_written=%llu\n", + (unsigned long long)sectors_expected, (unsigned long long)sectors_written); + } + + for (k = 0; k < nr_pages; k++) + kunmap(pages[k].bv_page); + kfree(pg_virt_addr); + + if (pages) + for (k = 0; k < nr_pages; k++) + put_page(pages[k].bv_page); + kfree(pages); + pages = NULL; + + if (write_errors == 0) { + if (num_dirty == 0) + dmc->sb_state = CACHE_MD_STATE_CLEAN; + else + dmc->sb_state = CACHE_MD_STATE_FASTCLEAN; + } else + dmc->sb_state = CACHE_MD_STATE_UNSTABLE; + +sb_store: + error = eio_sb_store(dmc); + if (error) { + /* TBD. should we return error */ + write_errors++; + pr_err("md_store: superblock store failed(error %d)", error); + } + if (!dmc->sb_version && CACHE_FAST_REMOVE_IS_SET(dmc)) + return 0; + + if (write_errors == 0) + pr_info("Metadata saved on the cache device"); + else { + pr_info + ("CRITICAL: There were %d errors in saving metadata on cache device", + write_errors); + if (num_dirty) + pr_info + ("CRITICAL: %d dirty blocks could not be written out", + num_dirty); + } + + pr_info("Valid blocks: %d, Dirty blocks: %d, Metadata sectors: %llu", + num_valid, num_dirty, (long long unsigned int)dmc->md_sectors); + + return 0; +} + +static int eio_md_create(struct cache_c *dmc, int force, int cold) +{ + struct flash_cacheblock *next_ptr; + union eio_superblock *header; + struct eio_io_region where; + sector_t i; + int j, error; + uint64_t cache_size, dev_size; + sector_t order; + sector_t sectors_written = 0, sectors_expected = 0; /* debug */ + int slots_written = 0; /* How many cache slots did we fill in this MD io block ? */ + + struct bio_vec *header_page = NULL; /* Header page */ + struct bio_vec *pages = NULL; /* Metadata pages */ + int nr_pages = 0; + int page_count, page_index; + int ret = 0, k; + void **pg_virt_addr = NULL; + + /* Allocate single page for superblock header.*/ + page_count = 0; + header_page = eio_alloc_pages(1, &page_count); + if (header_page == NULL) { + pr_err("eio_md_create: System memory too low."); + return -ENOMEM; + } + + EIO_ASSERT(page_count = 1); + header = (union eio_superblock *)kmap(header_page[0].bv_page); + + /* + * Apart from normal cache creation, eio_md_create() is also called when + * the SSD is added as part of eio_resume_caching(). At this point, + * the CACHE_FLAGS_DEGRADED is set, but we do want to write to the md area. + * Therefore, if the CACHE_FLAGS_SSD_ADD_INPROG is set, then proceed instead + * of returning -ENODEV. + */ + if ((unlikely(CACHE_FAILED_IS_SET(dmc)) + || unlikely(CACHE_DEGRADED_IS_SET(dmc))) + && (!CACHE_SSD_ADD_INPROG_IS_SET(dmc))) { + pr_err + ("md_create: Cannot write metadata in failed/degraded mode for cache \"%s\".\n", + dmc->cache_name); + ret = -ENODEV; + goto free_header; + } + + where.bdev = dmc->cache_dev->bdev; + where.sector = EIO_SUPERBLOCK_START; + where.count = eio_to_sector(EIO_SUPERBLOCK_SIZE); + error = eio_io_sync_vm(dmc, &where, READ, header_page, 1); + if (error) { + pr_err + ("md_create: Could not read superblock sector %llu error %d for cache \"%s\".\n", + (unsigned long long)where.sector, error, dmc->cache_name); + ret = -EINVAL; + goto free_header; + } + + if (!force && + ((le32_to_cpu(header->sbf.cache_sb_state) == CACHE_MD_STATE_DIRTY) || + (le32_to_cpu(header->sbf.cache_sb_state) == CACHE_MD_STATE_CLEAN) || + (le32_to_cpu(header->sbf.cache_sb_state) == CACHE_MD_STATE_FASTCLEAN))) { + pr_err + ("md_create: Existing cache detected, use force to re-create.\n"); + ret = -EINVAL; + goto free_header; + } + + /* + * Compute the size of the metadata including header. + * and here we also are making sure that metadata and userdata + * on SSD is aligned at 8K boundary. + * + * Note dmc->size is in raw sectors + */ + dmc->md_start_sect = EIO_METADATA_START(dmc->cache_dev_start_sect); + dmc->md_sectors = + INDEX_TO_MD_SECTOR(EIO_DIV(dmc->size, (sector_t)dmc->block_size)); + dmc->md_sectors += + EIO_EXTRA_SECTORS(dmc->cache_dev_start_sect, dmc->md_sectors); + dmc->size -= dmc->md_sectors; /* total sectors available for cache */ + do_div(dmc->size, dmc->block_size); + dmc->size = EIO_DIV(dmc->size, dmc->assoc) * (sector_t)dmc->assoc; + /* Recompute since dmc->size was possibly trunc'ed down */ + dmc->md_sectors = INDEX_TO_MD_SECTOR(dmc->size); + dmc->md_sectors += + EIO_EXTRA_SECTORS(dmc->cache_dev_start_sect, dmc->md_sectors); + + error = eio_mem_init(dmc); + if (error == -1) { + ret = -EINVAL; + goto free_header; + } + if ((unlikely(CACHE_FAILED_IS_SET(dmc)) + || unlikely(CACHE_DEGRADED_IS_SET(dmc))) + && (!CACHE_SSD_ADD_INPROG_IS_SET(dmc))) { + pr_err + ("md_create: Cannot write metadata in failed/degraded mode for cache \"%s\".\n", + dmc->cache_name); + ret = -ENODEV; + goto free_header; + } + dev_size = eio_to_sector(eio_get_device_size(dmc->cache_dev)); + cache_size = dmc->md_sectors + (dmc->size * dmc->block_size); + if (cache_size > dev_size) { + pr_err + ("md_create: Requested cache size exceeds the cache device's capacity (%llu > %llu)", + (unsigned long long)cache_size, (unsigned long long)dev_size); + ret = -EINVAL; + goto free_header; + } + + order = + dmc->size * + (EIO_MD8(dmc) ? sizeof(struct cacheblock_md8) : + sizeof(struct cacheblock)); + i = EIO_MD8(dmc) ? sizeof(struct cacheblock_md8) : sizeof(struct + cacheblock); + pr_info("Allocate %lluKB (%lluB per) mem for %llu-entry cache " \ + "(capacity:%lluMB, associativity:%u, block size:%u bytes)", + (unsigned long long)order >> 10, (unsigned long long)i, + (long long unsigned int)dmc->size, + (unsigned long long)(cache_size >> (20 - SECTOR_SHIFT)), dmc->assoc, + dmc->block_size << SECTOR_SHIFT); + + if (!eio_mem_available(dmc, order) && !CACHE_SSD_ADD_INPROG_IS_SET(dmc)) { + pr_err + ("md_create: System memory too low for allocating cache metadata.\n"); + ret = -ENOMEM; + goto free_header; + } + + /* + * If we are called due to SSD add, the memory was already allocated + * as part of cache creation (i.e., eio_ctr()) in the past. + */ + if (!CACHE_SSD_ADD_INPROG_IS_SET(dmc)) { + if (EIO_MD8(dmc)) + dmc->cache_md8 = vmalloc((size_t)order); + else + dmc->cache = vmalloc((size_t)order); + if ((EIO_MD8(dmc) && !dmc->cache_md8) + || (!EIO_MD8(dmc) && !dmc->cache)) { + pr_err + ("md_create: Unable to allocate cache md for cache \"%s\".\n", + dmc->cache_name); + ret = -ENOMEM; + goto free_header; + } + } + if (eio_repl_blk_init(dmc->policy_ops) != 0) { + pr_err + ("md_create: Unable to allocate memory for policy cache block for cache \"%s\".\n", + dmc->cache_name); + ret = -ENOMEM; + goto free_header; + } + + if (cold) { + int retry = 0; + do { + for (i = 0; i < dmc->size; i++) { + if (CACHE_SSD_ADD_INPROG_IS_SET(dmc)) { + u_int8_t cache_state = + EIO_CACHE_STATE_GET(dmc, i); + if (cache_state & BLOCK_IO_INPROG) { + /* sleep for 1 sec and retry */ + msleep(1000); + break; + } + } + eio_invalidate_md(dmc, i); + } + } while ((retry++ < 10) && (i < dmc->size)); + + if (i < dmc->size) { + pr_err + ("md_create: Cache \"%s\" is not in quiesce state. Can't proceed to resume.\n", + dmc->cache_name); + ret = -EBUSY; + goto free_header; + } + + /* Allocate pages of the order dmc->bio_nr_pages */ + page_count = 0; + pages = eio_alloc_pages(dmc->bio_nr_pages, &page_count); + if (!pages) { + pr_err + ("md_create: Unable to allocate pages for cache \"%s\".\n", + dmc->cache_name); + pr_err + ("md_create: Could not write out cache metadata.\n"); + ret = -ENOMEM; + goto free_header; + } + + /* nr_pages is used for freeing the pages */ + nr_pages = page_count; + + where.bdev = dmc->cache_dev->bdev; + where.sector = dmc->md_start_sect; + slots_written = 0; + page_index = 0; + + pg_virt_addr = kmalloc(nr_pages * (sizeof(void *)), GFP_KERNEL); + if (pg_virt_addr == NULL) { + pr_err("md_create: System memory too low.\n"); + for (k = 0; k < nr_pages; k++) + put_page(pages[k].bv_page); + kfree(pages); + ret = -ENOMEM; + goto free_header; + } + + for (k = 0; k < nr_pages; k++) + pg_virt_addr[k] = kmap(pages[k].bv_page); + + next_ptr = (struct flash_cacheblock *)pg_virt_addr[page_index]; + j = MD_BLOCKS_PER_PAGE; + + for (i = 0; i < dmc->size; i++) { + next_ptr->dbn = cpu_to_le64(EIO_DBN_GET(dmc, i)); + next_ptr->cache_state = + cpu_to_le64(EIO_CACHE_STATE_GET(dmc, + (index_t)i) & (INVALID | VALID | DIRTY)); + next_ptr++; + slots_written++; + j--; + + if (j == 0) { + + page_index++; + + if ((unsigned)slots_written == + MD_BLOCKS_PER_PAGE * nr_pages) { + + where.count = + slots_written / + MD_BLOCKS_PER_SECTOR; + slots_written = 0; + page_index = 0; + sectors_written += where.count; /* debug */ + error = + eio_io_sync_vm(dmc, &where, WRITE, + pages, nr_pages); + + if (error) { + if (!CACHE_SSD_ADD_INPROG_IS_SET + (dmc)) + vfree(EIO_CACHE(dmc)); + pr_err + ("md_create: Could not write cache metadata sector %llu error %d.\n for cache \"%s\".\n", + (unsigned long long)where.sector, error, + dmc->cache_name); + ret = -EIO; + goto free_md; + } + where.sector += where.count; /* Advance offset */ + } + + /* Move next slot pointer into next page */ + next_ptr = + (struct flash_cacheblock *) + pg_virt_addr[page_index]; + j = MD_BLOCKS_PER_PAGE; + } + } + + if (next_ptr != (struct flash_cacheblock *)pg_virt_addr[0]) { + /* Write the remaining last page out */ + EIO_ASSERT(slots_written > 0); + + where.count = slots_written / MD_BLOCKS_PER_SECTOR; + + if (slots_written % MD_BLOCKS_PER_SECTOR) + where.count++; + + sectors_written += where.count; + + if (next_ptr != + (struct flash_cacheblock *)pg_virt_addr[page_index]) { + unsigned offset; + + slots_written = + slots_written % MD_BLOCKS_PER_PAGE; + + /* + * We have some extra slots written at this page_index. + * Let us try to zero out the remaining page size before submitting + * this page. + */ + offset = + slots_written * + (sizeof(struct flash_cacheblock)); + memset(pg_virt_addr[page_index] + offset, 0, + PAGE_SIZE - offset); + + page_index = page_index + 1; + } + + error = + eio_io_sync_vm(dmc, &where, WRITE, pages, + page_index); + if (error) { + if (!CACHE_SSD_ADD_INPROG_IS_SET(dmc)) + vfree((void *)EIO_CACHE(dmc)); + pr_err + ("md_create: Could not write cache metadata sector %llu error %d for cache \"%s\".\n", + (unsigned long long)where.sector, error, dmc->cache_name); + ret = -EIO; + goto free_md; + } + } + + /* Debug Tests */ + sectors_expected = EIO_DIV(dmc->size, MD_BLOCKS_PER_SECTOR); + if (EIO_REM(dmc->size, MD_BLOCKS_PER_SECTOR)) + sectors_expected++; + if (sectors_expected != sectors_written) { + pr_err + ("md_create: Sector mismatch! sectors_expected=%llu, sectors_written=%llu for cache \"%s\".\n", + (unsigned long long)sectors_expected, (unsigned long long)sectors_written, + dmc->cache_name); + ret = -EIO; + goto free_md; + } + } + + /* if cold ends here */ + /* Write the superblock */ + if ((unlikely(CACHE_FAILED_IS_SET(dmc)) + || unlikely(CACHE_DEGRADED_IS_SET(dmc))) + && (!CACHE_SSD_ADD_INPROG_IS_SET(dmc))) { + pr_err + ("md_create: Cannot write metadata in failed/degraded mode for cache \"%s\".\n", + dmc->cache_name); + vfree((void *)EIO_CACHE(dmc)); + ret = -ENODEV; + goto free_md; + } + + dmc->sb_state = CACHE_MD_STATE_DIRTY; + dmc->sb_version = EIO_SB_VERSION; + error = eio_sb_store(dmc); + if (error) { + if (!CACHE_SSD_ADD_INPROG_IS_SET(dmc)) + vfree((void *)EIO_CACHE(dmc)); + pr_err + ("md_create: Could not write cache superblock sector(error %d) for cache \"%s\"\n", + error, dmc->cache_name); + ret = -EIO; + goto free_md; + } + +free_md: + for (k = 0; k < nr_pages; k++) + kunmap(pages[k].bv_page); + kfree(pg_virt_addr); + + /* Free metadata pages here. */ + if (pages) { + for (k = 0; k < nr_pages; k++) + put_page(pages[k].bv_page); + kfree(pages); + pages = NULL; + } + +free_header: + /* Free header page here */ + if (header_page) { + kunmap(header_page[0].bv_page); + put_page(header_page[0].bv_page); + kfree(header_page); + header_page = NULL; + } + + return ret; +} + +static int eio_md_load(struct cache_c *dmc) +{ + struct flash_cacheblock *meta_data_cacheblock, *next_ptr; + union eio_superblock *header; + struct eio_io_region where; + int i; + index_t j, slots_read; + sector_t size; + int clean_shutdown; + int dirty_loaded = 0; + sector_t order, data_size; + int num_valid = 0; + int error; + sector_t sectors_read = 0, sectors_expected = 0; /* Debug */ + int force_warm_boot = 0; + + struct bio_vec *header_page, *pages; + int nr_pages, page_count, page_index; + int ret = 0; + void **pg_virt_addr; + + page_count = 0; + header_page = eio_alloc_pages(1, &page_count); + if (header_page == NULL) { + pr_err("md_load: Unable to allocate memory"); + return -ENOMEM; + } + + EIO_ASSERT(page_count == 1); + header = (union eio_superblock *)kmap(header_page[0].bv_page); + + if (CACHE_FAILED_IS_SET(dmc) || CACHE_DEGRADED_IS_SET(dmc)) { + pr_err + ("md_load: Cannot load metadata in failed / degraded mode"); + ret = -ENODEV; + goto free_header; + } + + where.bdev = dmc->cache_dev->bdev; + where.sector = EIO_SUPERBLOCK_START; + where.count = eio_to_sector(EIO_SUPERBLOCK_SIZE); + error = eio_io_sync_vm(dmc, &where, READ, header_page, 1); + if (error) { + pr_err + ("md_load: Could not read cache superblock sector %llu error %d", + (unsigned long long)where.sector, error); + ret = -EINVAL; + goto free_header; + } + + /* check ondisk superblock version */ + if (le32_to_cpu(header->sbf.cache_version) != EIO_SB_VERSION) { + pr_info("md_load: Cache superblock mismatch detected." \ + " (current: %u, ondisk: %u)", EIO_SB_VERSION, + header->sbf.cache_version); + + if (le32_to_cpu(header->sbf.cache_version) == 0) { + pr_err("md_load: Can't enable cache %s. Either " \ + "superblock version is invalid or cache has" \ + " been deleted", header->sbf.cache_name); + ret = 1; + goto free_header; + } + + if (le32_to_cpu(header->sbf.cache_version) > EIO_SB_VERSION) { + pr_err("md_load: Can't enable cache %s with newer " \ + " superblock version.", header->sbf.cache_name); + ret = 1; + goto free_header; + } + + if (le32_to_cpu(header->sbf.mode) == CACHE_MODE_WB) { + pr_err("md_load: Can't enable write-back cache %s" \ + " with newer superblock version.", + header->sbf.cache_name); + ret = 1; + goto free_header; + } else if ((le32_to_cpu(header->sbf.mode) == CACHE_MODE_RO) || + (le32_to_cpu(header->sbf.mode) == CACHE_MODE_WT)) { + dmc->persistence = CACHE_FORCECREATE; + pr_info("md_load: Can't enable cache, recreating" \ + " cache %s with newer superblock version.", + header->sbf.cache_name); + ret = 0; + goto free_header; + } + } + + /* check ondisk magic number */ + + if (le32_to_cpu(header->sbf.cache_version) >= EIO_SB_MAGIC_VERSION && + le32_to_cpu(header->sbf.magic) != EIO_MAGIC) { + pr_err("md_load: Magic number mismatch in superblock detected." \ + " (current: %u, ondisk: %u)", EIO_MAGIC, + le32_to_cpu(header->sbf.magic)); + ret = 1; + goto free_header; + } + + dmc->sb_version = EIO_SB_VERSION; + + /* + * TBD + * For writeback, only when the dirty blocks are non-zero + * and header state is unexpected, we should treat it as md corrupted. + * Otherwise, a bad write in last shutdown, can lead to data inaccessible + * in writeback case. + */ + if (!((le32_to_cpu(header->sbf.cache_sb_state) == CACHE_MD_STATE_DIRTY) || + (le32_to_cpu(header->sbf.cache_sb_state) == CACHE_MD_STATE_CLEAN) || + (le32_to_cpu(header->sbf.cache_sb_state) == CACHE_MD_STATE_FASTCLEAN))) { + pr_err("md_load: Corrupt cache superblock"); + ret = -EINVAL; + goto free_header; + } + + if (le32_to_cpu(header->sbf.cold_boot) & BOOT_FLAG_FORCE_WARM) { + force_warm_boot = 1; + header->sbf.cold_boot = cpu_to_le32(le32_to_cpu(header->sbf.cold_boot) & + ~BOOT_FLAG_FORCE_WARM); + } + + /* + * Determine if we can start as cold or hot cache + * - if cold_boot is set(unless force_warm_boot), start as cold cache + * - else if it is unclean shutdown, start as cold cache + * cold cache will still treat the dirty blocks as hot + */ + if (dmc->cold_boot != le32_to_cpu(header->sbf.cold_boot)) { + pr_info + ("superblock(%u) and config(%u) cold boot values do not match. Relying on config", + le32_to_cpu(header->sbf.cold_boot), dmc->cold_boot); + } + if (dmc->cold_boot && !force_warm_boot) { + pr_info + ("Cold boot is set, starting as if unclean shutdown(only dirty blocks will be hot)"); + clean_shutdown = 0; + } else { + if (le32_to_cpu(header->sbf.cache_sb_state) == CACHE_MD_STATE_DIRTY) { + pr_info("Unclean shutdown detected"); + pr_info("Only dirty blocks exist in cache"); + clean_shutdown = 0; + } else if (le32_to_cpu(header->sbf.cache_sb_state) == CACHE_MD_STATE_CLEAN) { + pr_info("Slow (clean) shutdown detected"); + pr_info("Only clean blocks exist in cache"); + clean_shutdown = 1; + } else if (le32_to_cpu(header->sbf.cache_sb_state) == + CACHE_MD_STATE_FASTCLEAN) { + pr_info("Fast (clean) shutdown detected"); + pr_info("Both clean and dirty blocks exist in cache"); + clean_shutdown = 1; + } else { + /* Won't reach here, but TBD may change the previous if condition */ + pr_info + ("cache state is %d. Treating as unclean shutdown", + le32_to_cpu(header->sbf.cache_sb_state)); + pr_info("Only dirty blocks exist in cache"); + clean_shutdown = 0; + } + } + + if (!dmc->mode) + dmc->mode = le32_to_cpu(header->sbf.mode); + if (!dmc->req_policy) + dmc->req_policy = le32_to_cpu(header->sbf.repl_policy); + + if (!dmc->cache_flags) + dmc->cache_flags = le32_to_cpu(header->sbf.cache_flags); + + error = eio_policy_init(dmc); + if (error) + goto free_header; + + dmc->block_size = le64_to_cpu(header->sbf.block_size); + dmc->block_shift = ffs(dmc->block_size) - 1; + dmc->block_mask = dmc->block_size - 1; + dmc->size = le32_to_cpu(header->sbf.size); + dmc->cache_size = le64_to_cpu(header->sbf.cache_devsize); + dmc->assoc = le32_to_cpu(header->sbf.assoc); + dmc->consecutive_shift = ffs(dmc->assoc) - 1; + dmc->md_start_sect = le64_to_cpu(header->sbf.cache_md_start_sect); + dmc->md_sectors = le64_to_cpu(header->sbf.cache_data_start_sect); + dmc->sysctl_active.dirty_high_threshold = + le32_to_cpu(header->sbf.dirty_high_threshold); + dmc->sysctl_active.dirty_low_threshold = + le32_to_cpu(header->sbf.dirty_low_threshold); + dmc->sysctl_active.dirty_set_high_threshold = + le32_to_cpu(header->sbf.dirty_set_high_threshold); + dmc->sysctl_active.dirty_set_low_threshold = + le32_to_cpu(header->sbf.dirty_set_low_threshold); + dmc->sysctl_active.time_based_clean_interval = + le32_to_cpu(header->sbf.time_based_clean_interval); + dmc->sysctl_active.autoclean_threshold = + le32_to_cpu(header->sbf.autoclean_threshold); + + i = eio_mem_init(dmc); + if (i == -1) { + pr_err("eio_md_load: Failed to initialize memory."); + ret = -EINVAL; + goto free_header; + } + + order = + dmc->size * + ((i == + 1) ? sizeof(struct cacheblock_md8) : sizeof(struct cacheblock)); + data_size = dmc->size * dmc->block_size; + size = + EIO_MD8(dmc) ? sizeof(struct cacheblock_md8) : sizeof(struct + cacheblock); + pr_info("Allocate %lluKB (%lluB per) mem for %llu-entry cache " \ + "(capacity:%lluMB, associativity:%u, block size:%u bytes)", + (unsigned long long)order >> 10, (unsigned long long)size, + (long long unsigned int)dmc->size, + (long long unsigned int)(dmc->md_sectors + data_size) >> (20 - + SECTOR_SHIFT), + dmc->assoc, dmc->block_size << SECTOR_SHIFT); + + if (EIO_MD8(dmc)) + dmc->cache_md8 = vmalloc((size_t)order); + else + dmc->cache = vmalloc((size_t)order); + + if ((EIO_MD8(dmc) && !dmc->cache_md8) || (!EIO_MD8(dmc) && !dmc->cache)) { + pr_err("md_load: Unable to allocate memory"); + vfree((void *)header); + return 1; + } + + if (eio_repl_blk_init(dmc->policy_ops) != 0) { + vfree((void *)EIO_CACHE(dmc)); + pr_err + ("md_load: Unable to allocate memory for policy cache block"); + ret = -EINVAL; + goto free_header; + } + + /* Allocate pages of the order dmc->bio_nr_pages */ + page_count = 0; + pages = eio_alloc_pages(dmc->bio_nr_pages, &page_count); + if (!pages) { + pr_err("md_create: unable to allocate pages"); + pr_err("md_create: Could not write out cache metadata"); + vfree((void *)EIO_CACHE(dmc)); + ret = -ENOMEM; + goto free_header; + } + + /* nr_pages is used for freeing the pages */ + nr_pages = page_count; + + pg_virt_addr = kmalloc(nr_pages * (sizeof(void *)), GFP_KERNEL); + if (pg_virt_addr == NULL) { + pr_err("eio_md_store: System memory too low."); + for (i = 0; i < nr_pages; i++) + put_page(pages[i].bv_page); + kfree(pages); + ret = -ENOMEM; + goto free_header; + } + + for (i = 0; i < nr_pages; i++) + pg_virt_addr[i] = kmap(pages[i].bv_page); + + /* + * Read 1 PAGE of the metadata at a time and load up the + * incore metadata struct. + */ + + page_index = 0; + page_count = 0; + meta_data_cacheblock = + (struct flash_cacheblock *)pg_virt_addr[page_index]; + + where.bdev = dmc->cache_dev->bdev; + where.sector = dmc->md_start_sect; + size = dmc->size; + i = 0; + while (size > 0) { + slots_read = + min((long)size, ((long)MD_BLOCKS_PER_PAGE * nr_pages)); + + if (slots_read % MD_BLOCKS_PER_SECTOR) + where.count = 1 + (slots_read / MD_BLOCKS_PER_SECTOR); + else + where.count = slots_read / MD_BLOCKS_PER_SECTOR; + + if (slots_read % MD_BLOCKS_PER_PAGE) + page_count = 1 + (slots_read / MD_BLOCKS_PER_PAGE); + else + page_count = slots_read / MD_BLOCKS_PER_PAGE; + + sectors_read += where.count; /* Debug */ + error = eio_io_sync_vm(dmc, &where, READ, pages, page_count); + if (error) { + vfree((void *)EIO_CACHE(dmc)); + pr_err + ("md_load: Could not read cache metadata sector %llu error %d", + (unsigned long long)where.sector, error); + ret = -EIO; + goto free_md; + } + + where.sector += where.count; + next_ptr = meta_data_cacheblock; + + for (j = 0, page_index = 0; j < slots_read; j++) { + + if ((j % MD_BLOCKS_PER_PAGE) == 0) + next_ptr = + (struct flash_cacheblock *) + pg_virt_addr[page_index++]; + + /* If unclean shutdown, only the DIRTY blocks are loaded.*/ + if (clean_shutdown || (next_ptr->cache_state & DIRTY)) { + + if (next_ptr->cache_state & DIRTY) + dirty_loaded++; + + EIO_CACHE_STATE_SET(dmc, i, + (u_int8_t)le64_to_cpu(next_ptr-> + cache_state) & ~QUEUED); + + EIO_ASSERT((EIO_CACHE_STATE_GET(dmc, i) & + (VALID | INVALID)) + != (VALID | INVALID)); + + if (EIO_CACHE_STATE_GET(dmc, i) & VALID) + num_valid++; + EIO_DBN_SET(dmc, i, le64_to_cpu(next_ptr->dbn)); + } else + eio_invalidate_md(dmc, i); + next_ptr++; + i++; + } + size -= slots_read; + } + + /* + * If the cache contains dirty data, the only valid mode is write back. + */ + if (dirty_loaded && dmc->mode != CACHE_MODE_WB) { + vfree((void *)EIO_CACHE(dmc)); + pr_err + ("md_load: Cannot use %s mode because dirty data exists in the cache", + (dmc->mode == + CACHE_MODE_RO) ? "read only" : "write through"); + ret = -EINVAL; + goto free_md; + } + + /* Debug Tests */ + sectors_expected = EIO_DIV(dmc->size, MD_BLOCKS_PER_SECTOR); + if (EIO_REM(dmc->size, MD_BLOCKS_PER_SECTOR)) + sectors_expected++; + if (sectors_expected != sectors_read) { + pr_err + ("md_load: Sector mismatch! sectors_expected=%llu, sectors_read=%llu\n", + (unsigned long long)sectors_expected, (unsigned long long)sectors_read); + vfree((void *)EIO_CACHE(dmc)); + ret = -EIO; + goto free_md; + } + + /* Before we finish loading, we need to dirty the superblock and write it out */ + dmc->sb_state = CACHE_MD_STATE_DIRTY; + error = eio_sb_store(dmc); + if (error) { + vfree((void *)EIO_CACHE(dmc)); + pr_err + ("md_load: Could not write cache superblock sector(error %d)", + error); + ret = 1; + goto free_md; + } + +free_md: + for (i = 0; i < nr_pages; i++) + kunmap(pages[i].bv_page); + kfree(pg_virt_addr); + + if (pages) { + for (i = 0; i < nr_pages; i++) + put_page(pages[i].bv_page); + kfree(pages); + pages = NULL; + } + +free_header: + /* Free header page here */ + if (header_page) { + kunmap(header_page[0].bv_page); + put_page(header_page[0].bv_page); + kfree(header_page); + header_page = NULL; + } + + pr_info("Cache metadata loaded from disk with %d valid %d dirty blocks", + num_valid, dirty_loaded); + return ret; +} + +void eio_policy_free(struct cache_c *dmc) +{ + + if (dmc->policy_ops != NULL) { + eio_put_policy(dmc->policy_ops); + vfree(dmc->policy_ops); + } + if (dmc->sp_cache_blk != NULL) + vfree(dmc->sp_cache_blk); + if (dmc->sp_cache_set != NULL) + vfree(dmc->sp_cache_set); + + dmc->policy_ops = NULL; + dmc->sp_cache_blk = dmc->sp_cache_set = NULL; + return; +} + +static int eio_clean_thread_init(struct cache_c *dmc) +{ + INIT_LIST_HEAD(&dmc->cleanq); + spin_lock_init(&dmc->clean_sl); + EIO_INIT_EVENT(&dmc->clean_event); + return eio_start_clean_thread(dmc); +} + +int +eio_handle_ssd_message(char *cache_name, char *ssd_name, enum dev_notifier note) +{ + struct cache_c *dmc; + + dmc = eio_cache_lookup(cache_name); + if (NULL == dmc) { + pr_err("eio_handle_ssd_message: cache %s does not exist", + cache_name); + return -EINVAL; + } + + switch (note) { + + case NOTIFY_SSD_ADD: + /* Making sure that CACHE state is not active */ + if (CACHE_FAILED_IS_SET(dmc) || CACHE_DEGRADED_IS_SET(dmc)) + eio_resume_caching(dmc, ssd_name); + else + pr_err + ("eio_handle_ssd_message: SSD_ADD event called for ACTIVE cache \"%s\", ignoring!!!", + dmc->cache_name); + break; + + case NOTIFY_SSD_REMOVED: + eio_suspend_caching(dmc, note); + break; + + default: + pr_err("Wrong notifier passed for eio_handle_ssd_message\n"); + } + + return 0; +} + +static void eio_init_ssddev_props(struct cache_c *dmc) +{ + struct request_queue *rq; + uint32_t max_hw_sectors, max_nr_pages; + uint32_t nr_pages = 0; + + rq = bdev_get_queue(dmc->cache_dev->bdev); + max_hw_sectors = to_bytes(queue_max_hw_sectors(rq)) / PAGE_SIZE; + max_nr_pages = (u_int32_t)bio_get_nr_vecs(dmc->cache_dev->bdev); + nr_pages = min_t(u_int32_t, max_hw_sectors, max_nr_pages); + dmc->bio_nr_pages = nr_pages; + + /* + * If the cache device is not a physical device (eg: lv), then + * driverfs_dev will be null and we make cache_gendisk_name a null + * string. The eio_notify_ssd_rm() function in this case, + * cannot detect device removal, and therefore, we will have to rely + * on user space udev for the notification. + */ + + if (dmc->cache_dev && dmc->cache_dev->bdev && + dmc->cache_dev->bdev->bd_disk && + dmc->cache_dev->bdev->bd_disk->driverfs_dev) { + strncpy(dmc->cache_gendisk_name, + dev_name(dmc->cache_dev->bdev->bd_disk->driverfs_dev), + DEV_PATHLEN); + } else + dmc->cache_gendisk_name[0] = '\0'; +} + +static void eio_init_srcdev_props(struct cache_c *dmc) +{ + /* Same applies for source device as well. */ + if (dmc->disk_dev && dmc->disk_dev->bdev && + dmc->disk_dev->bdev->bd_disk && + dmc->disk_dev->bdev->bd_disk->driverfs_dev) { + strncpy(dmc->cache_srcdisk_name, + dev_name(dmc->disk_dev->bdev->bd_disk->driverfs_dev), + DEV_PATHLEN); + } else + dmc->cache_srcdisk_name[0] = '\0'; +} + +int eio_cache_create(struct cache_rec_short *cache) +{ + struct cache_c *dmc; + struct cache_c **nodepp; + unsigned int consecutive_blocks; + u_int64_t i; + index_t prev_set; + index_t cur_set; + sector_t order; + int error = -EINVAL; + uint32_t persistence = 0; + fmode_t mode = (FMODE_READ | FMODE_WRITE); + char *strerr = NULL; + + dmc = kzalloc(sizeof(*dmc), GFP_KERNEL); + if (dmc == NULL) { + strerr = "Failed to allocate memory for cache context"; + error = -ENOMEM; + goto bad; + } + + /* + * Source device. + */ + + error = eio_ttc_get_device(cache->cr_src_devname, mode, &dmc->disk_dev); + if (error) { + strerr = "get_device for source device failed"; + goto bad1; + } + + dmc->disk_size = eio_to_sector(eio_get_device_size(dmc->disk_dev)); + if (dmc->disk_size >= EIO_MAX_SECTOR) { + strerr = "Source device too big to support"; + error = -EFBIG; + goto bad2; + } + strncpy(dmc->disk_devname, cache->cr_src_devname, DEV_PATHLEN); + + /* + * Cache device. + */ + + error = + eio_ttc_get_device(cache->cr_ssd_devname, mode | FMODE_EXCL, &dmc->cache_dev); + if (error) { + strerr = "get_device for cache device failed"; + goto bad2; + } + + if (dmc->disk_dev == dmc->cache_dev) { + error = -EINVAL; + strerr = "Same devices specified"; + goto bad3; + } + strncpy(dmc->cache_devname, cache->cr_ssd_devname, DEV_PATHLEN); + + if (cache->cr_name[0] != '\0') { + strncpy(dmc->cache_name, cache->cr_name, + sizeof(dmc->cache_name)); + /* make sure it is zero terminated */ + dmc->cache_name[sizeof(dmc->cache_name) - 1] = '\x00'; + } else { + strerr = "Need cache name"; + error = -EINVAL; + goto bad3; + } + + strncpy(dmc->ssd_uuid, cache->cr_ssd_uuid, DEV_PATHLEN - 1); + + dmc->cache_dev_start_sect = eio_get_device_start_sect(dmc->cache_dev); + error = eio_do_preliminary_checks(dmc); + if (error) { + if (error == -EINVAL) + strerr = "Either Source and Cache devices belong to " + "same device or a cache already exists on" + " specified source device"; + else if (error == -EEXIST) + strerr = "Cache already exists"; + goto bad3; + } + + eio_init_ssddev_props(dmc); + eio_init_srcdev_props(dmc); + + /* + * Initialize the io callback queue. + */ + + dmc->callback_q = create_singlethread_workqueue("eio_callback"); + if (!dmc->callback_q) { + error = -ENOMEM; + strerr = "Failed to initialize callback workqueue"; + goto bad4; + } + error = eio_kcached_init(dmc); + if (error) { + strerr = "Failed to initialize kcached"; + goto bad4; + } + + /* + * We read policy before reading other args. The reason is that + * if there is a policy module loaded, we first need dmc->p_ops to be + * allocated so that it is non NULL. Once p_ops is !NULL, cache_blk_init + * and cache_set_init can set their pointers to dmc->p_ops->xxx + * + * policy_ops == NULL is not really an error. It just means that there + * is no registered policy and therefore we use EIO_REPL_RANDOM (random) + * as the replacement policy. + */ + + /* We do a kzalloc for dmc, but being extra careful here */ + dmc->sp_cache_blk = NULL; + dmc->sp_cache_set = NULL; + dmc->policy_ops = NULL; + if (cache->cr_policy) { + dmc->req_policy = cache->cr_policy; + if (dmc->req_policy && (dmc->req_policy < CACHE_REPL_FIRST || + dmc->req_policy > CACHE_REPL_LAST)) { + strerr = "Invalid cache policy"; + error = -EINVAL; + goto bad5; + } + } + + /* + * We need to determine the requested cache mode before we call + * eio_md_load becuase it examines dmc->mode. The cache mode is + * set as follows: + * 1. For a "reload" operation: + * - if mode is not provided as an argument, + it is read from superblock. + * - if mode is provided as an argument, + eio_md_load verifies that it is valid. + * 2. For a "create" operation: + * - if mode is not provided, it is set to CACHE_MODE_DEFAULT. + * - if mode is provided, it is validate and set. + */ + if (cache->cr_mode) { + dmc->mode = cache->cr_mode; + if (dmc->mode && (dmc->mode < CACHE_MODE_FIRST || + dmc->mode > CACHE_MODE_LAST)) { + strerr = "Invalid cache mode"; + error = -EINVAL; + goto bad5; + } + } + + dmc->cold_boot = cache->cr_cold_boot; + if ((dmc->cold_boot != 0) && (dmc->cold_boot != BOOT_FLAG_COLD_ENABLE)) { + strerr = "Invalid cold boot option"; + error = -EINVAL; + goto bad5; + } + + if (cache->cr_persistence) { + persistence = cache->cr_persistence; + if (persistence < CACHE_RELOAD || + persistence > CACHE_FORCECREATE) { + pr_err("ctr: persistence = %d", persistence); + strerr = "Invalid cache persistence"; + error = -EINVAL; + goto bad5; + } + dmc->persistence = persistence; + } + if (persistence == CACHE_RELOAD) { + if (eio_md_load(dmc)) { + strerr = "Failed to reload cache"; + error = -EINVAL; + goto bad5; + } + + /* + * "eio_md_load" will reset "dmc->persistence" from + * CACHE_RELOAD to CACHE_FORCECREATE in the case of + * cache superblock version mismatch and cache mode + * is Read-Only or Write-Through. + */ + if (dmc->persistence != persistence) + persistence = dmc->persistence; + } + + /* + * Now that we're back from "eio_md_load" in the case of a reload, + * we're ready to finish setting up the mode and policy. + */ + if (dmc->mode == 0) { + dmc->mode = CACHE_MODE_DEFAULT; + pr_info("Setting mode to default"); + } else { + pr_info("Setting mode to %s ", + (dmc->mode == CACHE_MODE_WB) ? "write back" : + ((dmc->mode == CACHE_MODE_RO) ? "read only" : + "write through")); + } + + /* eio_policy_init() is already called from within eio_md_load() */ + if (persistence != CACHE_RELOAD) { + error = eio_policy_init(dmc); + if (error) { + strerr = "Failed to initialize policy"; + goto bad5; + } + } + + if (cache->cr_flags) { + int flags; + flags = cache->cr_flags; + if (flags == 0) + dmc->cache_flags &= ~CACHE_FLAGS_INVALIDATE; + else if (flags == 1) { + dmc->cache_flags |= CACHE_FLAGS_INVALIDATE; + pr_info("Enabling invalidate API"); + } else + pr_info("Ignoring unknown flags value: %u", flags); + } + + if (persistence == CACHE_RELOAD) + goto init; /* Skip reading cache parameters from command line */ + + if (cache->cr_blksize && cache->cr_ssd_sector_size) { + dmc->block_size = EIO_DIV(cache->cr_blksize, cache->cr_ssd_sector_size); + if (dmc->block_size & (dmc->block_size - 1)) { + strerr = "Invalid block size"; + error = -EINVAL; + goto bad5; + } + if (dmc->block_size == 0) + dmc->block_size = DEFAULT_CACHE_BLKSIZE; + } else + dmc->block_size = DEFAULT_CACHE_BLKSIZE; + dmc->block_shift = ffs(dmc->block_size) - 1; + dmc->block_mask = dmc->block_size - 1; + + /* + * dmc->size is specified in sectors here, and converted to blocks later + * + * Giving preference to kernel got cache size. + * Only when we can't get the cache size in kernel, we accept user passed size. + * User mode may be using a different API or could also do some rounding, so we + * prefer kernel getting the cache size. In case of device failure and coming back, we + * rely on the device size got in kernel and we hope that it is equal to the + * one we used for creating the cache, so we ideally should always use the kernel + * got cache size. + */ + dmc->size = eio_to_sector(eio_get_device_size(dmc->cache_dev)); + if (dmc->size == 0) { + if (cache->cr_ssd_dev_size && cache->cr_ssd_sector_size) + dmc->size = + EIO_DIV(cache->cr_ssd_dev_size, cache->cr_ssd_sector_size); + + if (dmc->size == 0) { + strerr = "Invalid cache size or can't be fetched"; + error = -EINVAL; + goto bad5; + } + } + + dmc->cache_size = dmc->size; + + if (cache->cr_assoc) { + dmc->assoc = cache->cr_assoc; + if ((dmc->assoc & (dmc->assoc - 1)) || + dmc->assoc > EIO_MAX_ASSOC || dmc->size < dmc->assoc) { + strerr = "Invalid cache associativity"; + error = -EINVAL; + goto bad5; + } + if (dmc->assoc == 0) + dmc->assoc = DEFAULT_CACHE_ASSOC; + } else + dmc->assoc = DEFAULT_CACHE_ASSOC; + + /* + * initialize to an invalid index + */ + + dmc->index_zero = dmc->assoc + 1; + + /* + * Although it's very unlikely, we need to make sure that + * for the given associativity and block size our source + * device will have less than 4 billion sets. + */ + + i = EIO_DIV(eio_to_sector(eio_get_device_size(dmc->disk_dev)), + (dmc->assoc * dmc->block_size)); + if (i >= (((u_int64_t)1) << 32)) { + strerr = "Too many cache sets to support"; + goto bad5; + } + + consecutive_blocks = dmc->assoc; + dmc->consecutive_shift = ffs(consecutive_blocks) - 1; + + /* Initialize persistent thresholds */ + dmc->sysctl_active.dirty_high_threshold = DIRTY_HIGH_THRESH_DEF; + dmc->sysctl_active.dirty_low_threshold = DIRTY_LOW_THRESH_DEF; + dmc->sysctl_active.dirty_set_high_threshold = DIRTY_SET_HIGH_THRESH_DEF; + dmc->sysctl_active.dirty_set_low_threshold = DIRTY_SET_LOW_THRESH_DEF; + dmc->sysctl_active.autoclean_threshold = AUTOCLEAN_THRESH_DEF; + dmc->sysctl_active.time_based_clean_interval = + TIME_BASED_CLEAN_INTERVAL_DEF(dmc); + + spin_lock_init(&dmc->cache_spin_lock); + if (persistence == CACHE_CREATE) { + error = eio_md_create(dmc, /* force */ 0, /* cold */ 1); + if (error) { + strerr = "Failed to create cache"; + goto bad5; + } + } else { + error = eio_md_create(dmc, /* force */ 1, /* cold */ 1); + if (error) { + strerr = "Failed to force create cache"; + goto bad5; + } + } + +init: + order = (dmc->size >> dmc->consecutive_shift) * + sizeof(struct cache_set); + + if (!eio_mem_available(dmc, order)) { + strerr = "System memory too low" + " for allocating cache set metadata"; + error = -ENOMEM; + vfree((void *)EIO_CACHE(dmc)); + goto bad5; + } + + dmc->cache_sets = vmalloc((size_t)order); + if (!dmc->cache_sets) { + strerr = "Failed to allocate memory"; + error = -ENOMEM; + vfree((void *)EIO_CACHE(dmc)); + goto bad5; + } + + for (i = 0; i < (dmc->size >> dmc->consecutive_shift); i++) { + dmc->cache_sets[i].nr_dirty = 0; + spin_lock_init(&dmc->cache_sets[i].cs_lock); + init_rwsem(&dmc->cache_sets[i].rw_lock); + dmc->cache_sets[i].mdreq = NULL; + dmc->cache_sets[i].flags = 0; + } + error = eio_repl_sets_init(dmc->policy_ops); + if (error < 0) { + strerr = "Failed to allocate memory for cache policy"; + vfree((void *)dmc->cache_sets); + vfree((void *)EIO_CACHE(dmc)); + goto bad5; + } + eio_policy_lru_pushblks(dmc->policy_ops); + + if (dmc->mode == CACHE_MODE_WB) { + error = eio_allocate_wb_resources(dmc); + if (error) { + vfree((void *)dmc->cache_sets); + vfree((void *)EIO_CACHE(dmc)); + goto bad5; + } + } + + dmc->sysctl_active.error_inject = 0; + dmc->sysctl_active.fast_remove = 0; + dmc->sysctl_active.zerostats = 0; + dmc->sysctl_active.do_clean = 0; + + atomic_set(&dmc->clean_index, 0); + + atomic64_set(&dmc->nr_ios, 0); + + /* + * sysctl_mem_limit_pct [0 - 100]. Before doing a vmalloc() + * make sure that the allocation size requested is less than + * sysctl_mem_limit_pct percentage of the free RAM available + * in the system. This is to avoid OOM errors in Linux. + * 0 => do the vmalloc without checking system memory. + */ + + dmc->sysctl_active.mem_limit_pct = 75; + + (void)wait_on_bit_lock((void *)&eio_control->synch_flags, + EIO_UPDATE_LIST, eio_wait_schedule, + TASK_UNINTERRUPTIBLE); + dmc->next_cache = cache_list_head; + cache_list_head = dmc; + clear_bit(EIO_UPDATE_LIST, (void *)&eio_control->synch_flags); + smp_mb__after_clear_bit(); + wake_up_bit((void *)&eio_control->synch_flags, EIO_UPDATE_LIST); + + prev_set = -1; + for (i = 0; i < dmc->size; i++) { + if (EIO_CACHE_STATE_GET(dmc, i) & VALID) + atomic64_inc(&dmc->eio_stats.cached_blocks); + if (EIO_CACHE_STATE_GET(dmc, i) & DIRTY) { + dmc->cache_sets[EIO_DIV(i, dmc->assoc)].nr_dirty++; + atomic64_inc(&dmc->nr_dirty); + cur_set = EIO_DIV(i, dmc->assoc); + if (prev_set != cur_set) { + /* Move the given set at the head of the set LRU list */ + eio_touch_set_lru(dmc, cur_set); + prev_set = cur_set; + } + } + } + + INIT_WORK(&dmc->readfill_wq, eio_do_readfill); + + /* + * invalid index, but signifies cache successfully built + */ + + dmc->index_zero = dmc->assoc; + + eio_procfs_ctr(dmc); + + /* + * Activate Application Transparent Caching. + */ + + error = eio_ttc_activate(dmc); + if (error) + goto bad6; + + /* + * In future if anyone adds code here and something fails, + * do call eio_ttc_deactivate(dmc) as part of cleanup. + */ + + return 0; + +bad6: + eio_procfs_dtr(dmc); + if (dmc->mode == CACHE_MODE_WB) { + eio_stop_async_tasks(dmc); + eio_free_wb_resources(dmc); + } + vfree((void *)dmc->cache_sets); + vfree((void *)EIO_CACHE(dmc)); + + (void)wait_on_bit_lock((void *)&eio_control->synch_flags, + EIO_UPDATE_LIST, eio_wait_schedule, + TASK_UNINTERRUPTIBLE); + nodepp = &cache_list_head; + while (*nodepp != NULL) { + if (*nodepp == dmc) { + *nodepp = dmc->next_cache; + break; + } + nodepp = &((*nodepp)->next_cache); + } + clear_bit(EIO_UPDATE_LIST, (void *)&eio_control->synch_flags); + smp_mb__after_clear_bit(); + wake_up_bit((void *)&eio_control->synch_flags, EIO_UPDATE_LIST); +bad5: + eio_kcached_client_destroy(dmc); +bad4: +bad3: + eio_put_cache_device(dmc); +bad2: + eio_ttc_put_device(&dmc->disk_dev); +bad1: + eio_policy_free(dmc); + kfree(dmc); +bad: + if (strerr) + pr_err("Cache creation failed: %s.\n", strerr); + return error; +} + +/* + * Destroy the cache mapping. + */ + +int eio_cache_delete(char *cache_name, int do_delete) +{ + struct cache_c *dmc; + struct cache_c **nodepp; + int ret, error; + int restart_async_task; + + ret = 0; + restart_async_task = 0; + + dmc = eio_cache_lookup(cache_name); + if (NULL == dmc) { + pr_err("cache delete: cache \"%s\" doesn't exist.", cache_name); + return -EINVAL; + } + + spin_lock_irqsave(&dmc->cache_spin_lock, dmc->cache_spin_lock_flags); + if (dmc->cache_flags & CACHE_FLAGS_SHUTDOWN_INPROG) { + pr_err("cache_delete: system shutdown in progress, cannot " \ + "delete cache %s", cache_name); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return -EINVAL; + } + if (dmc->cache_flags & CACHE_FLAGS_MOD_INPROG) { + pr_err + ("cache_delete: simultaneous edit/delete operation " \ + " on cache %s is not permitted", cache_name); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return -EINVAL; + } + dmc->cache_flags |= CACHE_FLAGS_MOD_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + + /* + * Earlier attempt to delete failed. + * Allow force deletes only for FAILED caches. + */ + if (unlikely(CACHE_STALE_IS_SET(dmc))) { + if (likely(CACHE_FAILED_IS_SET(dmc))) { + pr_err + ("cache_delete: Cache \"%s\" is in STALE state. Force deleting!!!", + dmc->cache_name); + goto force_delete; + } else { + if (atomic64_read(&dmc->nr_dirty) != 0) { + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_MOD_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc-> + cache_spin_lock_flags); + pr_err + ("cache_delete: Stale Cache detected with dirty blocks=%lld.\n", + (long long)atomic64_read(&dmc->nr_dirty)); + pr_err + ("cache_delete: Cache \"%s\" wont be deleted. Deleting will result in data corruption.\n", + dmc->cache_name); + return -EINVAL; + } + } + } + + eio_stop_async_tasks(dmc); + + /* + * Deactivate Application Transparent Caching. + * For wb cache, finish_nr_dirty may take long time. + * It should be guaranteed that normal cache delete should succeed + * only when finish_nr_dirty is completely done. + */ + + if (eio_ttc_deactivate(dmc, 0)) { + + /* If deactivate fails; only option is to delete cache. */ + pr_err("cache_delete: Failed to deactivate the cache \"%s\".", + dmc->cache_name); + if (CACHE_FAILED_IS_SET(dmc)) + pr_err + ("cache_delete: Use -f option to delete the cache \"%s\".", + dmc->cache_name); + ret = -EPERM; + dmc->cache_flags |= CACHE_FLAGS_STALE; + + /* Restart async tasks. */ + restart_async_task = 1; + goto out; + } + + if (!CACHE_FAILED_IS_SET(dmc)) + EIO_ASSERT(dmc->sysctl_active.fast_remove + || (atomic64_read(&dmc->nr_dirty) == 0)); + + /* + * If ttc_deactivate succeeded... proceed with cache delete. + * Dont entertain device failure hereafter. + */ + if (unlikely(CACHE_FAILED_IS_SET(dmc)) || + unlikely(CACHE_DEGRADED_IS_SET(dmc))) { + pr_err + ("cache_delete: Cannot update metadata of cache \"%s\" in failed/degraded mode.", + dmc->cache_name); + } else + eio_md_store(dmc); + +force_delete: + eio_procfs_dtr(dmc); + + if (CACHE_STALE_IS_SET(dmc)) { + pr_info("Force deleting cache \"%s\"!!!.", dmc->cache_name); + eio_ttc_deactivate(dmc, 1); + } + + eio_free_wb_resources(dmc); + vfree((void *)EIO_CACHE(dmc)); + vfree((void *)dmc->cache_sets); + eio_ttc_put_device(&dmc->disk_dev); + eio_put_cache_device(dmc); + (void)wait_on_bit_lock((void *)&eio_control->synch_flags, + EIO_UPDATE_LIST, eio_wait_schedule, + TASK_UNINTERRUPTIBLE); + nodepp = &cache_list_head; + while (*nodepp != NULL) { + if (*nodepp == dmc) { + *nodepp = dmc->next_cache; + break; + } + nodepp = &((*nodepp)->next_cache); + } + clear_bit(EIO_UPDATE_LIST, &eio_control->synch_flags); + smp_mb__after_clear_bit(); + wake_up_bit((void *)&eio_control->synch_flags, EIO_UPDATE_LIST); + +out: + if (restart_async_task) { + EIO_ASSERT(dmc->clean_thread == NULL); + error = eio_start_clean_thread(dmc); + if (error) + pr_err + ("cache_delete: Failed to restart async tasks. error=%d\n", + error); + } + spin_lock_irqsave(&dmc->cache_spin_lock, dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_MOD_INPROG; + if (!ret) + dmc->cache_flags |= CACHE_FLAGS_DELETED; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + + if (!ret) { + eio_policy_free(dmc); + + /* + * We don't need synchronisation since at this point the dmc is + * no more accessible via lookup. + */ + + if (!(dmc->cache_flags & CACHE_FLAGS_SHUTDOWN_INPROG)) + kfree(dmc); + } + + return ret; +} + +/* + * Reconstruct a degraded cache after the SSD is added. + * This function mimics the constructor eio_ctr() except + * for code that does not require re-initialization. + */ +int eio_ctr_ssd_add(struct cache_c *dmc, char *dev) +{ + int r = 0; + struct eio_bdev *prev_cache_dev; + u_int32_t prev_persistence = dmc->persistence; + fmode_t mode = (FMODE_READ | FMODE_WRITE); + + /* verify if source device is present */ + EIO_ASSERT(dmc->eio_errors.no_source_dev == 0); + + /* mimic relevant portions from eio_ctr() */ + + prev_cache_dev = dmc->cache_dev; + r = eio_ttc_get_device(dev, mode, &dmc->cache_dev); + if (r) { + dmc->cache_dev = prev_cache_dev; + pr_err("ctr_ssd_add: Failed to lookup cache device %s", dev); + return -EINVAL; + } + /* + * For Linux, we have to put the old SSD device now because + * we did not do so during SSD removal. + */ + eio_ttc_put_device(&prev_cache_dev); + + /* sanity check */ + if (dmc->cache_size != eio_to_sector(eio_get_device_size(dmc->cache_dev))) { + pr_err("ctr_ssd_add: Cache device size has changed," \ + "expected (%llu) found (%llu)" \ + "continuing in degraded mode", + (unsigned long long)dmc->cache_size, + (unsigned long long)eio_to_sector( + eio_get_device_size(dmc->cache_dev))); + r = -EINVAL; + goto out; + } + + /* sanity check for cache device start sector */ + if (dmc->cache_dev_start_sect != + eio_get_device_start_sect(dmc->cache_dev)) { + pr_err("ctr_ssd_add: Cache device starting sector changed," \ + "expected (%llu) found (%llu) continuing in" \ + "degraded mode", (unsigned long long)dmc->cache_dev_start_sect, + (unsigned long long)eio_get_device_start_sect(dmc->cache_dev)); + r = -EINVAL; + goto out; + } + + strncpy(dmc->cache_devname, dev, DEV_PATHLEN); + eio_init_ssddev_props(dmc); + dmc->size = dmc->cache_size; /* dmc->size will be recalculated in eio_md_create() */ + + /* + * In case of writeback mode, trust the content of SSD and reload the MD. + */ + dmc->persistence = CACHE_FORCECREATE; + + eio_policy_free(dmc); + r = eio_policy_init(dmc); + if (r) { + pr_err("ctr_ssd_add: Failed to initialize policy"); + goto out; + } + + r = eio_md_create(dmc, /* force */ 1, /* cold */ + (dmc->mode != CACHE_MODE_WB)); + if (r) { + pr_err + ("ctr_ssd_add: Failed to create md, continuing in degraded mode"); + goto out; + } + + r = eio_repl_sets_init(dmc->policy_ops); + if (r < 0) { + pr_err + ("ctr_ssd_add: Failed to allocate memory for cache policy"); + goto out; + } + eio_policy_lru_pushblks(dmc->policy_ops); + if (dmc->mode != CACHE_MODE_WB) + /* Cold cache will reset the stats */ + memset(&dmc->eio_stats, 0, sizeof(dmc->eio_stats)); + + return 0; +out: + dmc->persistence = prev_persistence; + + return r; +} + +/* + * Stop the async tasks for a cache(threads, scheduled works). + * Used during the cache remove + */ +void eio_stop_async_tasks(struct cache_c *dmc) +{ + unsigned long flags = 0; + + if (dmc->clean_thread) { + dmc->sysctl_active.fast_remove = 1; + spin_lock_irqsave(&dmc->clean_sl, flags); + EIO_SET_EVENT_AND_UNLOCK(&dmc->clean_event, &dmc->clean_sl, + flags); + eio_wait_thread_exit(dmc->clean_thread, + &dmc->clean_thread_running); + EIO_CLEAR_EVENT(&dmc->clean_event); + dmc->clean_thread = NULL; + } + + dmc->sysctl_active.fast_remove = CACHE_FAST_REMOVE_IS_SET(dmc) ? 1 : 0; + + if (dmc->mode == CACHE_MODE_WB) { + /* + * Prevent new I/Os to schedule the time based cleaning. + * Cancel existing delayed work + */ + dmc->sysctl_active.time_based_clean_interval = 0; + cancel_delayed_work_sync(&dmc->clean_aged_sets_work); + } +} + +int eio_start_clean_thread(struct cache_c *dmc) +{ + EIO_ASSERT(dmc->clean_thread == NULL); + EIO_ASSERT(dmc->mode == CACHE_MODE_WB); + EIO_ASSERT(dmc->clean_thread_running == 0); + EIO_ASSERT(!(dmc->sysctl_active.do_clean & EIO_CLEAN_START)); + + dmc->clean_thread = eio_create_thread(eio_clean_thread_proc, + (void *)dmc, "eio_clean_thread"); + if (!dmc->clean_thread) + return -EFAULT; + return 0; +} + +int eio_allocate_wb_resources(struct cache_c *dmc) +{ + int nr_bvecs, nr_pages; + unsigned iosize; + int ret; + + EIO_ASSERT(dmc->clean_dbvecs == NULL); + EIO_ASSERT(dmc->clean_mdpages == NULL); + EIO_ASSERT(dmc->dbvec_count == 0); + EIO_ASSERT(dmc->mdpage_count == 0); + + /* Data page allocations are done in terms of "bio_vec" structures */ + iosize = (dmc->block_size * dmc->assoc) << SECTOR_SHIFT; + nr_bvecs = IO_BVEC_COUNT(iosize, dmc->block_size); + dmc->clean_dbvecs = kmalloc(sizeof(struct bio_vec) * nr_bvecs, + GFP_KERNEL); + if (dmc->clean_dbvecs == NULL) { + pr_err("cache_create: Failed to allocated memory.\n"); + ret = -ENOMEM; + goto errout; + } + /* Allocate pages for each bio_vec */ + ret = eio_alloc_wb_bvecs(dmc->clean_dbvecs, nr_bvecs, dmc->block_size); + if (ret) + goto errout; + EIO_ASSERT(dmc->clean_dbvecs != NULL); + dmc->dbvec_count = nr_bvecs; + + /* Metadata page allocations are done in terms of pages only */ + iosize = dmc->assoc * sizeof(struct flash_cacheblock); + nr_pages = IO_PAGE_COUNT(iosize); + dmc->clean_mdpages = kmalloc(sizeof(struct page *) * nr_pages, + GFP_KERNEL); + if (dmc->clean_mdpages == NULL) { + pr_err("cache_create: Failed to allocated memory.\n"); + ret = -ENOMEM; + eio_free_wb_bvecs(dmc->clean_dbvecs, dmc->dbvec_count, + dmc->block_size); + goto errout; + } + ret = eio_alloc_wb_pages(dmc->clean_mdpages, nr_pages); + if (ret) { + eio_free_wb_bvecs(dmc->clean_dbvecs, dmc->dbvec_count, + dmc->block_size); + goto errout; + } + EIO_ASSERT(dmc->clean_mdpages != NULL); + dmc->mdpage_count = nr_pages; + + /* + * For writeback cache: + * 1. Initialize the time based clean work queue + * 2. Initialize the dirty set lru + * 3. Initialize clean thread + */ + + /* + * Reset dmc->is_clean_aged_sets_sched. + * Time based clean will be enabled in eio_touch_set_lru() + * only when dmc->is_clean_aged_sets_sched is zero and + * dmc->sysctl_active.time_based_clean_interval > 0. + */ + + dmc->is_clean_aged_sets_sched = 0; + INIT_DELAYED_WORK(&dmc->clean_aged_sets_work, eio_clean_aged_sets); + dmc->dirty_set_lru = NULL; + ret = + lru_init(&dmc->dirty_set_lru, + (dmc->size >> dmc->consecutive_shift)); + if (ret == 0) { + spin_lock_init(&dmc->dirty_set_lru_lock); + ret = eio_clean_thread_init(dmc); + } + EIO_ASSERT(dmc->mdupdate_q == NULL); + dmc->mdupdate_q = create_singlethread_workqueue("eio_mdupdate"); + if (!dmc->mdupdate_q) + ret = -ENOMEM; + + if (ret < 0) { + pr_err("cache_create: Failed to initialize dirty lru set or" \ + "clean/mdupdate thread for wb cache.\n"); + if (dmc->dirty_set_lru) { + lru_uninit(dmc->dirty_set_lru); + dmc->dirty_set_lru = NULL; + } + + eio_free_wb_pages(dmc->clean_mdpages, dmc->mdpage_count); + eio_free_wb_bvecs(dmc->clean_dbvecs, dmc->dbvec_count, + dmc->block_size); + goto errout; + } + + goto out; + +errout: + if (dmc->clean_mdpages) { + kfree(dmc->clean_mdpages); + dmc->clean_mdpages = NULL; + dmc->mdpage_count = 0; + } + if (dmc->clean_dbvecs) { + kfree(dmc->clean_dbvecs); + dmc->clean_dbvecs = NULL; + dmc->dbvec_count = 0; + } + +out: + return ret; +} + +void eio_free_wb_resources(struct cache_c *dmc) +{ + + if (dmc->mdupdate_q) { + flush_workqueue(dmc->mdupdate_q); + destroy_workqueue(dmc->mdupdate_q); + dmc->mdupdate_q = NULL; + } + if (dmc->dirty_set_lru) { + lru_uninit(dmc->dirty_set_lru); + dmc->dirty_set_lru = NULL; + } + if (dmc->clean_mdpages) { + eio_free_wb_pages(dmc->clean_mdpages, dmc->mdpage_count); + kfree(dmc->clean_mdpages); + dmc->clean_mdpages = NULL; + } + if (dmc->clean_dbvecs) { + eio_free_wb_bvecs(dmc->clean_dbvecs, dmc->dbvec_count, + dmc->block_size); + kfree(dmc->clean_dbvecs); + dmc->clean_dbvecs = NULL; + } + + dmc->dbvec_count = dmc->mdpage_count = 0; + return; +} + +static int +eio_notify_reboot(struct notifier_block *this, unsigned long code, void *x) +{ + struct cache_c *dmc; + + if (eio_reboot_notified == EIO_REBOOT_HANDLING_DONE) + return NOTIFY_DONE; + + (void)wait_on_bit_lock((void *)&eio_control->synch_flags, + EIO_HANDLE_REBOOT, eio_wait_schedule, + TASK_UNINTERRUPTIBLE); + if (eio_reboot_notified == EIO_REBOOT_HANDLING_DONE) { + clear_bit(EIO_HANDLE_REBOOT, (void *)&eio_control->synch_flags); + smp_mb__after_clear_bit(); + wake_up_bit((void *)&eio_control->synch_flags, + EIO_HANDLE_REBOOT); + return NOTIFY_DONE; + } + EIO_ASSERT(eio_reboot_notified == 0); + eio_reboot_notified = EIO_REBOOT_HANDLING_INPROG; + + (void)wait_on_bit_lock((void *)&eio_control->synch_flags, + EIO_UPDATE_LIST, eio_wait_schedule, + TASK_UNINTERRUPTIBLE); + for (dmc = cache_list_head; dmc != NULL; dmc = dmc->next_cache) { + if (unlikely(CACHE_FAILED_IS_SET(dmc)) + || unlikely(CACHE_DEGRADED_IS_SET(dmc))) { + pr_err + ("notify_reboot: Cannot sync in failed / degraded mode"); + continue; + } + if (dmc->cold_boot && atomic64_read(&dmc->nr_dirty) + && !eio_force_warm_boot) { + pr_info + ("Cold boot set for cache %s: Draining dirty blocks: %lld", + dmc->cache_name, (long long)atomic64_read(&dmc->nr_dirty)); + eio_clean_for_reboot(dmc); + } + eio_md_store(dmc); + } + clear_bit(EIO_UPDATE_LIST, (void *)&eio_control->synch_flags); + smp_mb__after_clear_bit(); + wake_up_bit((void *)&eio_control->synch_flags, EIO_UPDATE_LIST); + + eio_reboot_notified = EIO_REBOOT_HANDLING_DONE; + clear_bit(EIO_HANDLE_REBOOT, (void *)&eio_control->synch_flags); + smp_mb__after_clear_bit(); + wake_up_bit((void *)&eio_control->synch_flags, EIO_HANDLE_REBOOT); + return NOTIFY_DONE; +} + +/* + * The SSD add/remove is handled using udev from the user space. The driver + * is notified from the user space via dmsetup message. Both device addition + * and removal events are handled in the driver by eio_handle_message(). + * + * The device remove has a special case. From the time the device is removed, + * until the time the driver gets notified from the user space could be a few msec + * or a couple of seconds. During this time, any IO to the SSD fails. While this + * is handled gracefully, the logs can get filled with IO error messages. + * + * In order to cover that gap, we handle the device removal within the kernel + * using this function. Note that using the scsi notifier function in the kernel + * (vs. receiving the message from user space) minimizes the notification delay + * between the time the SSD is removed until the driver is notified. This cannot, + * however, make this delay zero. Therefore, there will be a small window during + * which eio_io_callback() may fail on CACHEWRITE action. + * + * We still need the user space (udev) method of handling for the following + * reasons: + * (i) This notifier is only for a scsi device. + * (ii) The add/remove feature in user space can also be used to dynamically + * turn the cache on and off. + * + * This notifier is used only when SSD is removed. The add event can + * be caught using the BUS_NOTIFY_ADD_DEVICE in action. However, we only + * get a scsi handle and do not have a reference to our device pointer. + */ +static int +eio_notify_ssd_rm(struct notifier_block *nb, unsigned long action, void *data) +{ + struct device *dev = data; + struct cache_c *dmc; + const char *device_name; + size_t len; + unsigned long int flags = 0; + struct ssd_rm_list *ssd_list_ptr; + unsigned check_src = 0, check_ssd = 0; + enum dev_notifier notify = NOTIFY_INITIALIZER; + + if (likely(action != BUS_NOTIFY_DEL_DEVICE)) + return 0; + + if (unlikely(dev == NULL)) { + pr_info("notify_cache_dev: device is NULL!"); + return 0; + } + + if (!scsi_is_sdev_device(dev)) + return 0; + device_name = dev_name(dev); + if (device_name == NULL) + return 0; + len = strlen(device_name); + + /* push to a list for future processing as we could be in an interrupt context */ + for (dmc = cache_list_head; dmc != NULL; dmc = dmc->next_cache) { + notify = NOTIFY_INITIALIZER; + check_src = ('\0' == dmc->cache_srcdisk_name[0] ? 0 : 1); + check_ssd = ('\0' == dmc->cache_gendisk_name[0] ? 0 : 1); + + if (check_src == 0 && check_ssd == 0) + continue; + + /*Check if source dev name or ssd dev name is available or not. */ + if (check_ssd + && 0 == strncmp(device_name, dmc->cache_gendisk_name, + len)) { + pr_info("SSD Removed for cache name %s", + dmc->cache_name); + notify = NOTIFY_SSD_REMOVED; + } + + if (check_src + && 0 == strncmp(device_name, dmc->cache_srcdisk_name, + len)) { + pr_info("SRC Removed for cache name %s", + dmc->cache_name); + notify = NOTIFY_SRC_REMOVED; + } + + if (notify == NOTIFY_INITIALIZER) + continue; + + ssd_list_ptr = kmalloc(sizeof(struct ssd_rm_list), GFP_ATOMIC); + if (unlikely(ssd_list_ptr == NULL)) { + pr_err("Cannot allocate memory for ssd_rm_list"); + return -ENOMEM; + } + ssd_list_ptr->dmc = dmc; + ssd_list_ptr->action = action; + ssd_list_ptr->devt = dev->devt; + ssd_list_ptr->note = notify; + spin_lock_irqsave(&ssd_rm_list_lock, flags); + list_add_tail(&ssd_list_ptr->list, &ssd_rm_list); + ssd_rm_list_not_empty = 1; + spin_unlock_irqrestore(&ssd_rm_list_lock, flags); + } + + spin_lock_irqsave(&ssd_rm_list_lock, flags); + if (ssd_rm_list_not_empty) { + spin_unlock_irqrestore(&ssd_rm_list_lock, flags); + schedule_work(&_kcached_wq); + } else + spin_unlock_irqrestore(&ssd_rm_list_lock, flags); + + return 0; +} + +/* + * Initiate a cache target. + */ +static int __init eio_init(void) +{ + int r; + extern struct bus_type scsi_bus_type; + + eio_ttc_init(); + r = eio_create_misc_device(); + if (r) + return r; + + r = eio_jobs_init(); + if (r) { + (void)eio_delete_misc_device(); + return r; + } + atomic_set(&nr_cache_jobs, 0); + INIT_WORK(&_kcached_wq, eio_do_work); + + eio_module_procfs_init(); + eio_control = kmalloc(sizeof(*eio_control), GFP_KERNEL); + if (eio_control == NULL) { + pr_err("init: Cannot allocate memory for eio_control"); + (void)eio_delete_misc_device(); + return -ENOMEM; + } + eio_control->synch_flags = 0; + + register_reboot_notifier(&eio_reboot_notifier); + r = bus_register_notifier(&scsi_bus_type, &eio_ssd_rm_notifier); + if (r) { + pr_err("init: bus register notifier failed %d", r); + (void)eio_delete_misc_device(); + } + return r; +} + +/* + * Destroy a cache target. + */ +static void eio_exit(void) +{ + int r; + extern struct bus_type scsi_bus_type; + + unregister_reboot_notifier(&eio_reboot_notifier); + r = bus_unregister_notifier(&scsi_bus_type, &eio_ssd_rm_notifier); + if (r) + pr_err("exit: Bus unregister notifier failed %d", r); + + eio_jobs_exit(); + eio_module_procfs_exit(); + if (eio_control) { + eio_control->synch_flags = 0; + kfree(eio_control); + eio_control = NULL; + } + (void)eio_delete_misc_device(); +} + +/* + * eio_get_device_size + */ +sector_t eio_get_device_size(struct eio_bdev *dev) +{ + + return dev->bdev->bd_inode->i_size; +} + +/* + * To get starting sector of the device + */ +sector_t eio_get_device_start_sect(struct eio_bdev *dev) +{ + + if (dev == NULL || dev->bdev == NULL || dev->bdev->bd_part == NULL) + return 0; + + return dev->bdev->bd_part->start_sect; +} + +module_init(eio_init); +module_exit(eio_exit); + +MODULE_DESCRIPTION(DM_NAME "STEC EnhanceIO target"); +MODULE_AUTHOR("STEC, Inc. based on code by Facebook"); + +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_fifo.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_fifo.c --- linux-3.10.30/drivers/block/enhanceio/eio_fifo.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_fifo.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,239 @@ +/* + * eio_fifo.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "eio.h" +/* Generic policy functions prototypes */ +int eio_fifo_init(struct cache_c *); +void eio_fifo_exit(void); +int eio_fifo_cache_sets_init(struct eio_policy *); +int eio_fifo_cache_blk_init(struct eio_policy *); +void eio_fifo_find_reclaim_dbn(struct eio_policy *, index_t, index_t *); +int eio_fifo_clean_set(struct eio_policy *, index_t, int); + +/* Per policy instance initialization */ +struct eio_policy *eio_fifo_instance_init(void); + +/* Per cache set data structure */ +struct eio_fifo_cache_set { + index_t set_fifo_next; + index_t set_clean_next; +}; + +/* + * Context that captures the FIFO replacement policy + */ +static struct eio_policy_header eio_fifo_ops = { + .sph_name = CACHE_REPL_FIFO, + .sph_instance_init = eio_fifo_instance_init, +}; + +/* + * Initialize FIFO policy. + */ +int eio_fifo_init(struct cache_c *dmc) +{ + return 0; +} + +/* + * Initialize FIFO data structure called from ctr. + */ +int eio_fifo_cache_sets_init(struct eio_policy *p_ops) +{ + int i; + sector_t order; + struct cache_c *dmc = p_ops->sp_dmc; + struct eio_fifo_cache_set *cache_sets; + + pr_info("Initializing fifo cache sets\n"); + order = (dmc->size >> dmc->consecutive_shift) * + sizeof(struct eio_fifo_cache_set); + + dmc->sp_cache_set = vmalloc((size_t)order); + if (dmc->sp_cache_set == NULL) + return -ENOMEM; + + cache_sets = (struct eio_fifo_cache_set *)dmc->sp_cache_set; + + for (i = 0; i < (int)(dmc->size >> dmc->consecutive_shift); i++) { + cache_sets[i].set_fifo_next = i * dmc->assoc; + cache_sets[i].set_clean_next = i * dmc->assoc; + } + + return 0; +} + +/* + * The actual function that returns a victim block in index. + */ +void +eio_fifo_find_reclaim_dbn(struct eio_policy *p_ops, index_t start_index, + index_t *index) +{ + index_t end_index; + int slots_searched = 0; + index_t i; + index_t set; + struct eio_fifo_cache_set *cache_sets; + struct cache_c *dmc = p_ops->sp_dmc; + + set = start_index / dmc->assoc; + end_index = start_index + dmc->assoc; + cache_sets = (struct eio_fifo_cache_set *)dmc->sp_cache_set; + + i = cache_sets[set].set_fifo_next; + while (slots_searched < (int)dmc->assoc) { + EIO_ASSERT(i >= start_index); + EIO_ASSERT(i < end_index); + if (EIO_CACHE_STATE_GET(dmc, i) == VALID) { + *index = i; + break; + } + slots_searched++; + i++; + if (i == end_index) + i = start_index; + } + i++; + if (i == end_index) + i = start_index; + cache_sets[set].set_fifo_next = i; +} + +/* + * Go through the entire set and clean. + */ +int eio_fifo_clean_set(struct eio_policy *p_ops, index_t set, int to_clean) +{ + index_t i; + int scanned = 0, nr_writes = 0; + index_t start_index; + index_t end_index; + struct eio_fifo_cache_set *cache_sets; + struct cache_c *dmc; + + dmc = p_ops->sp_dmc; + cache_sets = (struct eio_fifo_cache_set *)dmc->sp_cache_set; + start_index = set * dmc->assoc; + end_index = start_index + dmc->assoc; + i = cache_sets[set].set_clean_next; + + while ((scanned < (int)dmc->assoc) && (nr_writes < to_clean)) { + if ((EIO_CACHE_STATE_GET(dmc, i) & (DIRTY | BLOCK_IO_INPROG)) == + DIRTY) { + EIO_CACHE_STATE_ON(dmc, i, DISKWRITEINPROG); + nr_writes++; + } + scanned++; + i++; + if (i == end_index) + i = start_index; + } + cache_sets[set].set_clean_next = i; + + return nr_writes; +} + +/* + * FIFO is per set, so do nothing on a per block init. + */ +int eio_fifo_cache_blk_init(struct eio_policy *p_ops) +{ + return 0; +} + +/* + * Allocate a new instance of eio_policy per dmc + */ +struct eio_policy *eio_fifo_instance_init(void) +{ + struct eio_policy *new_instance; + + new_instance = vmalloc(sizeof(struct eio_policy)); + if (new_instance == NULL) { + pr_err("ssdscache_fifo_instance_init: vmalloc failed"); + return NULL; + } + + /* Initialize the FIFO specific functions and variables */ + new_instance->sp_name = CACHE_REPL_FIFO; + new_instance->sp_policy.lru = NULL; + new_instance->sp_repl_init = eio_fifo_init; + new_instance->sp_repl_exit = eio_fifo_exit; + new_instance->sp_repl_sets_init = eio_fifo_cache_sets_init; + new_instance->sp_repl_blk_init = eio_fifo_cache_blk_init; + new_instance->sp_find_reclaim_dbn = eio_fifo_find_reclaim_dbn; + new_instance->sp_clean_set = eio_fifo_clean_set; + new_instance->sp_dmc = NULL; + + try_module_get(THIS_MODULE); + + pr_info("eio_fifo_instance_init: created new instance of FIFO"); + + return new_instance; +} + +/* + * Cleanup an instance of eio_policy (called from dtr). + */ +void eio_fifo_exit(void) +{ + module_put(THIS_MODULE); +} + +static +int __init fifo_register(void) +{ + int ret; + + ret = eio_register_policy(&eio_fifo_ops); + if (ret != 0) + pr_info("eio_fifo already registered"); + + return ret; +} + +static +void __exit fifo_unregister(void) +{ + int ret; + + ret = eio_unregister_policy(&eio_fifo_ops); + if (ret != 0) + pr_err("eio_fifo unregister failed"); +} + +module_init(fifo_register); +module_exit(fifo_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("FIFO policy for EnhanceIO"); +MODULE_AUTHOR("STEC, Inc. based on code by Facebook"); diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_ioctl.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_ioctl.c --- linux-3.10.30/drivers/block/enhanceio/eio_ioctl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_ioctl.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,157 @@ +/* + * eio_ioctl.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "eio.h" +#include "eio_ttc.h" + +long eio_ioctl(struct file *filp, unsigned cmd, unsigned long arg) +{ + int error = 0; + struct cache_rec_short *cache; + uint64_t ncaches; + enum dev_notifier note; + int do_delete = 0; + + switch (cmd) { + case EIO_IOC_CREATE: + case EIO_IOC_ENABLE: + + cache = vmalloc(sizeof(struct cache_rec_short)); + if (!cache) + return -ENOMEM; + + if (copy_from_user(cache, (void __user *)arg, + sizeof(struct cache_rec_short))) { + vfree(cache); + return -EFAULT; + } + error = eio_cache_create(cache); + vfree(cache); + break; + + case EIO_IOC_DELETE: + do_delete = 1; + + case EIO_IOC_DISABLE: + + cache = vmalloc(sizeof(struct cache_rec_short)); + if (!cache) + return -ENOMEM; + + if (copy_from_user(cache, (void __user *)arg, + sizeof(struct cache_rec_short))) { + vfree(cache); + return -EFAULT; + } + error = eio_cache_delete(cache->cr_name, do_delete); + vfree(cache); + break; + + case EIO_IOC_EDIT: + cache = vmalloc(sizeof(struct cache_rec_short)); + if (!cache) + return -ENOMEM; + + if (copy_from_user(cache, (void __user *)arg, + sizeof(struct cache_rec_short))) { + vfree(cache); + return -EFAULT; + } + error = eio_cache_edit(cache->cr_name, + (u_int32_t)cache->cr_mode, + (u_int32_t)cache->cr_policy); + vfree(cache); + break; + + case EIO_IOC_NCACHES: + ncaches = eio_get_cache_count(); + if (copy_to_user((uint64_t __user *)arg, &ncaches, + sizeof(uint64_t))) + return -EFAULT; + break; + + case EIO_IOC_CACHE_LIST: + error = eio_get_cache_list((unsigned long __user *)arg); + break; + + case EIO_IOC_SET_WARM_BOOT: + eio_set_warm_boot(); + break; + + case EIO_IOC_SSD_ADD: + cache = vmalloc(sizeof(struct cache_rec_short)); + if (!cache) + return -ENOMEM; + + if (copy_from_user(cache, (void __user *)arg, + sizeof(struct cache_rec_short))) { + vfree(cache); + return -EFAULT; + } + note = NOTIFY_SSD_ADD; + error = + eio_handle_ssd_message(cache->cr_name, + cache->cr_ssd_devname, note); + vfree(cache); + + break; + + case EIO_IOC_SSD_REMOVE: + cache = vmalloc(sizeof(struct cache_rec_short)); + if (!cache) + return -ENOMEM; + if (copy_from_user(cache, (void __user *)arg, + sizeof(struct cache_rec_short))) { + vfree(cache); + return -EFAULT; + } + note = NOTIFY_SSD_REMOVED; + error = + eio_handle_ssd_message(cache->cr_name, + cache->cr_ssd_devname, note); + vfree(cache); + break; + + case EIO_IOC_SRC_ADD: + break; + + case EIO_IOC_NOTIFY_REBOOT: + eio_reboot_handling(); + break; + + default: + error = EINVAL; + } + return error; +} + +long eio_compact_ioctl(struct file *filp, unsigned cmd, unsigned long arg) +{ + return eio_ioctl(filp, cmd, arg); +} diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_ioctl.h linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_ioctl.h --- linux-3.10.30/drivers/block/enhanceio/eio_ioctl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_ioctl.h 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,86 @@ +/* + * eio_ioctl.h + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ + +#ifndef EIO_IOCTL_H +#define EIO_IOCTL_H + +#define EIO_DEVPATH "/dev/eiodev" +#define MISC_DEVICE "eiodev" + +#define CACHE_NAME_LEN 31 +#define CACHE_NAME_SZ (CACHE_NAME_LEN + 1) + +#define NAME_LEN 127 +#define NAME_SZ (NAME_LEN + 1) + +#define EIO_IOC_CREATE _IOW('E', 0, struct cache_rec_short) +#define EIO_IOC_DELETE _IOW('E', 1, struct cache_rec_short) +#define EIO_IOC_ENABLE _IOW('E', 2, struct cache_rec_short) +#define EIO_IOC_DISABLE _IOW('E', 3, struct cache_rec_short) +#define EIO_IOC_EDIT _IOW('E', 4, struct cache_rec_short) +#define EIO_IOC_NCACHES _IOR('E', 5, uint64_t) +#define EIO_IOC_CACHE_LIST _IOWR('E', 6, struct cache_list) +#define EIO_IOC_SSD_ADD _IOW('E', 7, struct cache_rec_short) +#define EIO_IOC_SSD_REMOVE _IOW('E', 8, struct cache_rec_short) +#define EIO_IOC_SRC_ADD _IOW('E', 9, struct cache_rec_short) +#define EIO_IOC_SRC_REMOVE _IOW('E', 10, struct cache_rec_short) +#define EIO_IOC_NOTIFY_REBOOT _IO('E', 11) +#define EIO_IOC_SET_WARM_BOOT _IO('E', 12) +#define EIO_IOC_UNUSED _IO('E', 13) + + +struct cache_rec_short { + char cr_name[CACHE_NAME_SZ]; + char cr_src_devname[NAME_SZ]; + char cr_ssd_devname[NAME_SZ]; + char cr_ssd_uuid[NAME_SZ]; + uint64_t cr_src_dev_size; + uint64_t cr_ssd_dev_size; + uint32_t cr_src_sector_size; + uint32_t cr_ssd_sector_size; + uint32_t cr_flags; /* CACHE_FLAGS_INV* etc. */ + char cr_policy; + char cr_mode; + char cr_persistence; + char cr_cold_boot; + uint64_t cr_blksize; + uint64_t cr_assoc; +}; + +struct cache_list { + uint64_t ncaches; + struct cache_rec_short *cachelist; +}; + +#ifdef __KERNEL__ +long eio_ioctl(struct file *filp, unsigned cmd, unsigned long arg); +long eio_compact_ioctl(struct file *filp, unsigned cmd, unsigned long arg); +#endif /* __KERNEL__ */ + +#endif /* !EIO_IOCTL_H */ diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_lru.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_lru.c --- linux-3.10.30/drivers/block/enhanceio/eio_lru.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_lru.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,322 @@ +/* + * eio_lru.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "eio.h" +/* Generic policy functions prototyes */ +int eio_lru_init(struct cache_c *); +void eio_lru_exit(void); +int eio_lru_cache_sets_init(struct eio_policy *); +int eio_lru_cache_blk_init(struct eio_policy *); +void eio_lru_find_reclaim_dbn(struct eio_policy *, index_t, index_t *); +int eio_lru_clean_set(struct eio_policy *, index_t, int); +/* Per policy instance initialization */ +struct eio_policy *eio_lru_instance_init(void); + +/* LRU specific policy functions prototype */ +void eio_lru_pushblks(struct eio_policy *); +void eio_reclaim_lru_movetail(struct cache_c *, index_t, struct eio_policy *); + +/* Per cache set data structure */ +struct eio_lru_cache_set { + u_int16_t lru_head, lru_tail; +}; + +/* Per cache block data structure */ +struct eio_lru_cache_block { + u_int16_t lru_prev, lru_next; +}; + +/* LRU specifc data structures */ +static struct eio_lru eio_lru = { + .sl_lru_pushblks = eio_lru_pushblks, + .sl_reclaim_lru_movetail = eio_reclaim_lru_movetail, +}; + +/* + * Context that captures the LRU replacement policy + */ +static struct eio_policy_header eio_lru_ops = { + .sph_name = CACHE_REPL_LRU, + .sph_instance_init = eio_lru_instance_init, +}; + +/* + * Intialize LRU. Called from ctr. + */ +int eio_lru_init(struct cache_c *dmc) +{ + return 0; +} + +/* + * Initialize per set LRU data structures. + */ +int eio_lru_cache_sets_init(struct eio_policy *p_ops) +{ + sector_t order; + int i; + struct cache_c *dmc = p_ops->sp_dmc; + struct eio_lru_cache_set *cache_sets; + + order = + (dmc->size >> dmc->consecutive_shift) * + sizeof(struct eio_lru_cache_set); + + dmc->sp_cache_set = vmalloc((size_t)order); + if (dmc->sp_cache_set == NULL) + return -ENOMEM; + + cache_sets = (struct eio_lru_cache_set *)dmc->sp_cache_set; + + for (i = 0; i < (int)(dmc->size >> dmc->consecutive_shift); i++) { + cache_sets[i].lru_tail = EIO_LRU_NULL; + cache_sets[i].lru_head = EIO_LRU_NULL; + } + pr_info("Initialized %d sets in LRU", i); + + return 0; +} + +/* + * Initialize per block LRU data structures + */ +int eio_lru_cache_blk_init(struct eio_policy *p_ops) +{ + sector_t order; + struct cache_c *dmc = p_ops->sp_dmc; + + order = dmc->size * sizeof(struct eio_lru_cache_block); + + dmc->sp_cache_blk = vmalloc((size_t)order); + if (dmc->sp_cache_blk == NULL) + return -ENOMEM; + + return 0; +} + +/* + * Allocate a new instance of eio_policy per dmc + */ +struct eio_policy *eio_lru_instance_init(void) +{ + struct eio_policy *new_instance; + + new_instance = vmalloc(sizeof(struct eio_policy)); + if (new_instance == NULL) { + pr_err("eio_lru_instance_init: vmalloc failed"); + return NULL; + } + + /* Initialize the LRU specific functions and variables */ + new_instance->sp_name = CACHE_REPL_LRU; + new_instance->sp_policy.lru = &eio_lru; + new_instance->sp_repl_init = eio_lru_init; + new_instance->sp_repl_exit = eio_lru_exit; + new_instance->sp_repl_sets_init = eio_lru_cache_sets_init; + new_instance->sp_repl_blk_init = eio_lru_cache_blk_init; + new_instance->sp_find_reclaim_dbn = eio_lru_find_reclaim_dbn; + new_instance->sp_clean_set = eio_lru_clean_set; + new_instance->sp_dmc = NULL; + + try_module_get(THIS_MODULE); + + pr_info("eio_lru_instance_init: created new instance of LRU"); + + return new_instance; +} + +/* + * Cleanup an instance of eio_policy (called from dtr). + */ +void eio_lru_exit(void) +{ + module_put(THIS_MODULE); +} + +/* + * Find a victim block to evict and return it in index. + */ +void +eio_lru_find_reclaim_dbn(struct eio_policy *p_ops, + index_t start_index, index_t *index) +{ + index_t lru_rel_index; + struct eio_lru_cache_set *lru_sets; + struct eio_lru_cache_block *lru_blk; + struct cache_c *dmc = p_ops->sp_dmc; + index_t set; + + set = start_index / dmc->assoc; + lru_sets = (struct eio_lru_cache_set *)(dmc->sp_cache_set); + + lru_rel_index = lru_sets[set].lru_head; + while (lru_rel_index != EIO_LRU_NULL) { + lru_blk = + ((struct eio_lru_cache_block *)dmc->sp_cache_blk + + lru_rel_index + start_index); + if (EIO_CACHE_STATE_GET(dmc, (lru_rel_index + start_index)) == + VALID) { + EIO_ASSERT((lru_blk - (struct eio_lru_cache_block *) + dmc->sp_cache_blk) == + (lru_rel_index + start_index)); + *index = lru_rel_index + start_index; + eio_reclaim_lru_movetail(dmc, *index, p_ops); + break; + } + lru_rel_index = lru_blk->lru_next; + } + + return; +} + +/* + * Go through the entire set and clean. + */ +int eio_lru_clean_set(struct eio_policy *p_ops, index_t set, int to_clean) +{ + struct cache_c *dmc = p_ops->sp_dmc; + index_t lru_rel_index; + int nr_writes = 0; + struct eio_lru_cache_set *lru_cache_sets; + struct eio_lru_cache_block *lru_cacheblk; + index_t dmc_idx; + index_t start_index; + + lru_cache_sets = (struct eio_lru_cache_set *)dmc->sp_cache_set; + start_index = set * dmc->assoc; + lru_rel_index = lru_cache_sets[set].lru_head; + + while ((lru_rel_index != EIO_LRU_NULL) && (nr_writes < to_clean)) { + dmc_idx = lru_rel_index + start_index; + lru_cacheblk = + ((struct eio_lru_cache_block *)dmc->sp_cache_blk + + lru_rel_index + start_index); + EIO_ASSERT((lru_cacheblk - + (struct eio_lru_cache_block *)dmc->sp_cache_blk) == + (lru_rel_index + start_index)); + if ((EIO_CACHE_STATE_GET(dmc, dmc_idx) & + (DIRTY | BLOCK_IO_INPROG)) == DIRTY) { + EIO_CACHE_STATE_ON(dmc, dmc_idx, DISKWRITEINPROG); + nr_writes++; + } + lru_rel_index = lru_cacheblk->lru_next; + } + + return nr_writes; +} + +/* + * LRU specific functions. + */ +void +eio_reclaim_lru_movetail(struct cache_c *dmc, index_t index, + struct eio_policy *p_ops) +{ + index_t set = index / dmc->assoc; + index_t start_index = set * dmc->assoc; + index_t my_index = index - start_index; + struct eio_lru_cache_block *cacheblk; + struct eio_lru_cache_set *cache_sets; + struct eio_lru_cache_block *blkptr; + + cacheblk = + (((struct eio_lru_cache_block *)(dmc->sp_cache_blk)) + index); + cache_sets = (struct eio_lru_cache_set *)dmc->sp_cache_set; + blkptr = (struct eio_lru_cache_block *)(dmc->sp_cache_blk); + + /* Remove from LRU */ + if (likely((cacheblk->lru_prev != EIO_LRU_NULL) || + (cacheblk->lru_next != EIO_LRU_NULL))) { + if (cacheblk->lru_prev != EIO_LRU_NULL) + blkptr[cacheblk->lru_prev + start_index].lru_next = + cacheblk->lru_next; + else + cache_sets[set].lru_head = cacheblk->lru_next; + if (cacheblk->lru_next != EIO_LRU_NULL) + blkptr[cacheblk->lru_next + start_index].lru_prev = + cacheblk->lru_prev; + else + cache_sets[set].lru_tail = cacheblk->lru_prev; + } + /* And add it to LRU Tail */ + cacheblk->lru_next = EIO_LRU_NULL; + cacheblk->lru_prev = cache_sets[set].lru_tail; + if (cache_sets[set].lru_tail == EIO_LRU_NULL) + cache_sets[set].lru_head = (u_int16_t)my_index; + else + blkptr[cache_sets[set].lru_tail + start_index].lru_next = + (u_int16_t)my_index; + cache_sets[set].lru_tail = (u_int16_t)my_index; +} + +void eio_lru_pushblks(struct eio_policy *p_ops) +{ + struct cache_c *dmc = p_ops->sp_dmc; + struct eio_lru_cache_block *cache_block; + int i; + + cache_block = dmc->sp_cache_blk; + for (i = 0; i < (int)dmc->size; i++) { + cache_block[i].lru_prev = EIO_LRU_NULL; + cache_block[i].lru_next = EIO_LRU_NULL; + eio_reclaim_lru_movetail(dmc, i, p_ops); + } + return; +} + +static +int __init lru_register(void) +{ + int ret; + + ret = eio_register_policy(&eio_lru_ops); + if (ret != 0) + pr_info("eio_lru already registered"); + + return ret; +} + +static +void __exit lru_unregister(void) +{ + int ret; + + ret = eio_unregister_policy(&eio_lru_ops); + if (ret != 0) + pr_err("eio_lru unregister failed"); +} + +module_init(lru_register); +module_exit(lru_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LRU policy for EnhanceIO"); +MODULE_AUTHOR("STEC, Inc. based on code by Facebook"); diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_main.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_main.c --- linux-3.10.30/drivers/block/enhanceio/eio_main.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_main.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,3546 @@ +/* + * eio_main.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * Amit Kale + * Restructured much of the io code to split bio within map function instead + * of letting dm do it. + * Simplified queued logic for write through. + * Created per-cache spinlocks for reducing contention in IO codepath. + * Amit Kale + * Harish Pujari + * Designed and implemented the writeback caching mode + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "eio.h" +#include "eio_ttc.h" + +#define CTRACE(X) { } + +/* + * TODO List : + * 1) sysctls : Create per-cache device sysctls instead of global sysctls. + * 2) Management of non cache pids : Needs improvement. Remove registration + * on process exits (with a pseudo filesstem'ish approach perhaps) ? + * 3) Breaking up the cache spinlock : Right now contention on the spinlock + * is not a problem. Might need change in future. + * 4) Use the standard linked list manipulation macros instead rolling our own. + * 5) Fix a security hole : A malicious process with 'ro' access to a file can + * potentially corrupt file data. This can be fixed by copying the data on a + * cache read miss. + */ + +static int eio_read_peek(struct cache_c *dmc, struct eio_bio *ebio); +static int eio_write_peek(struct cache_c *dmc, struct eio_bio *ebio); +static void eio_read(struct cache_c *dmc, struct bio_container *bc, + struct eio_bio *ebegin); +static void eio_write(struct cache_c *dmc, struct bio_container *bc, + struct eio_bio *ebegin); +static int eio_inval_block(struct cache_c *dmc, sector_t iosector); +static void eio_enqueue_readfill(struct cache_c *dmc, struct kcached_job *job); +static int eio_acquire_set_locks(struct cache_c *dmc, struct bio_container *bc); +static int eio_release_io_resources(struct cache_c *dmc, + struct bio_container *bc); +static void eio_clean_set(struct cache_c *dmc, index_t set, int whole, + int force); +static void eio_do_mdupdate(struct work_struct *work); +static void eio_mdupdate_callback(int error, void *context); +static void eio_enq_mdupdate(struct bio_container *bc); +static void eio_uncached_read_done(struct kcached_job *job); +static void eio_addto_cleanq(struct cache_c *dmc, index_t set, int whole); +static int eio_alloc_mdreqs(struct cache_c *, struct bio_container *); +static void eio_check_dirty_set_thresholds(struct cache_c *dmc, index_t set); +static void eio_check_dirty_cache_thresholds(struct cache_c *dmc); +static void eio_post_mdupdate(struct work_struct *work); +static void eio_post_io_callback(struct work_struct *work); + +static void bc_addfb(struct bio_container *bc, struct eio_bio *ebio) +{ + + atomic_inc(&bc->bc_holdcount); + + ebio->eb_bc = bc; +} + +static void bc_put(struct bio_container *bc, unsigned int doneio) +{ + struct cache_c *dmc; + int data_dir; + long elapsed; + + if (atomic_dec_and_test(&bc->bc_holdcount)) { + if (bc->bc_dmc->mode == CACHE_MODE_WB) + eio_release_io_resources(bc->bc_dmc, bc); + bc->bc_bio->bi_size = 0; + dmc = bc->bc_dmc; + + /* update iotime for latency */ + data_dir = bio_data_dir(bc->bc_bio); + elapsed = (long)jiffies_to_msecs(jiffies - bc->bc_iotime); + + if (data_dir == READ) + atomic64_add(elapsed, &dmc->eio_stats.rdtime_ms); + else + atomic64_add(elapsed, &dmc->eio_stats.wrtime_ms); + + bio_endio(bc->bc_bio, bc->bc_error); + atomic64_dec(&bc->bc_dmc->nr_ios); + kfree(bc); + } +} + +static void eb_endio(struct eio_bio *ebio, int error) +{ + + EIO_ASSERT(ebio->eb_bc); + + /*Propagate only main io errors and sizes*/ + if (ebio->eb_iotype == EB_MAIN_IO) { + if (error) + ebio->eb_bc->bc_error = error; + bc_put(ebio->eb_bc, ebio->eb_size); + } else + bc_put(ebio->eb_bc, 0); + ebio->eb_bc = NULL; + kfree(ebio); +} + +static int +eio_io_async_bvec(struct cache_c *dmc, struct eio_io_region *where, int rw, + struct bio_vec *pages, unsigned nr_bvecs, eio_notify_fn fn, + void *context, int hddio) +{ + struct eio_io_request req; + int error = 0; + + memset((char *)&req, 0, sizeof(req)); + + if (unlikely(CACHE_DEGRADED_IS_SET(dmc))) { + if (where->bdev != dmc->disk_dev->bdev) { + pr_err + ("eio_io_async_bvec: Cache is in degraded mode.\n"); + pr_err + ("eio_io_async_Bvec: Can not issue i/o to ssd device.\n"); + return -ENODEV; + } + } + + req.mtype = EIO_BVECS; + req.dptr.pages = pages; + req.num_bvecs = nr_bvecs; + req.notify = fn; + req.context = context; + req.hddio = hddio; + + error = eio_do_io(dmc, where, rw, &req); + + return error; +} + +static void +eio_flag_abios(struct cache_c *dmc, struct eio_bio *abio, int invalidated) +{ + struct eio_bio *nbio; + + while (abio) { + int invalidate; + unsigned long flags; + int cwip_on = 0; + int dirty_on = 0; + int callendio = 0; + nbio = abio->eb_next; + + EIO_ASSERT(!(abio->eb_iotype & EB_INVAL) || abio->eb_index == -1); + invalidate = !invalidated && (abio->eb_iotype & EB_INVAL); + + spin_lock_irqsave(&dmc->cache_sets[abio->eb_cacheset].cs_lock, + flags); + + if (abio->eb_index != -1) { + if (EIO_CACHE_STATE_GET(dmc, abio->eb_index) & DIRTY) + dirty_on = 1; + + if (unlikely + (EIO_CACHE_STATE_GET(dmc, abio->eb_index) & + CACHEWRITEINPROG)) + cwip_on = 1; + } + + if (dirty_on) { + /* + * For dirty blocks, we don't change the cache state flags. + * We however, need to end the ebio, if this was the last + * hold on it. + */ + if (atomic_dec_and_test(&abio->eb_holdcount)) { + callendio = 1; + /* We shouldn't reach here when the DIRTY_INPROG flag + * is set on the cache block. It should either have been + * cleared to become DIRTY or INVALID elsewhere. + */ + EIO_ASSERT(EIO_CACHE_STATE_GET(dmc, abio->eb_index) + != DIRTY_INPROG); + } + } else if (abio->eb_index != -1) { + if (invalidate) { + if (cwip_on) + EIO_CACHE_STATE_ON(dmc, abio->eb_index, + QUEUED); + else { + EIO_CACHE_STATE_SET(dmc, abio->eb_index, + INVALID); + atomic64_dec_if_positive(&dmc-> + eio_stats. + cached_blocks); + } + } else { + if (cwip_on) + EIO_CACHE_STATE_OFF(dmc, abio->eb_index, + DISKWRITEINPROG); + else { + if (EIO_CACHE_STATE_GET + (dmc, abio->eb_index) & QUEUED) { + EIO_CACHE_STATE_SET(dmc, + abio-> + eb_index, + INVALID); + atomic64_dec_if_positive(&dmc-> + eio_stats. + cached_blocks); + } else { + EIO_CACHE_STATE_SET(dmc, + abio-> + eb_index, + VALID); + } + } + } + } else { + EIO_ASSERT(invalidated || invalidate); + if (invalidate) + eio_inval_block(dmc, abio->eb_sector); + } + spin_unlock_irqrestore(&dmc->cache_sets[abio->eb_cacheset]. + cs_lock, flags); + if (!cwip_on && (!dirty_on || callendio)) + eb_endio(abio, 0); + abio = nbio; + } +} + +static void eio_disk_io_callback(int error, void *context) +{ + struct kcached_job *job; + struct eio_bio *ebio; + struct cache_c *dmc; + unsigned long flags; + unsigned eb_cacheset; + + flags = 0; + job = (struct kcached_job *)context; + dmc = job->dmc; + ebio = job->ebio; + + EIO_ASSERT(ebio != NULL); + eb_cacheset = ebio->eb_cacheset; + + if (unlikely(error)) + dmc->eio_errors.disk_read_errors++; + + spin_lock_irqsave(&dmc->cache_sets[eb_cacheset].cs_lock, flags); + /* Invalidate the cache block */ + EIO_CACHE_STATE_SET(dmc, ebio->eb_index, INVALID); + atomic64_dec_if_positive(&dmc->eio_stats.cached_blocks); + spin_unlock_irqrestore(&dmc->cache_sets[eb_cacheset].cs_lock, flags); + + if (unlikely(error)) + pr_err("disk_io_callback: io error %d block %llu action %d", + error, + (unsigned long long)job->job_io_regions.disk.sector, + job->action); + + eb_endio(ebio, error); + ebio = NULL; + job->ebio = NULL; + eio_free_cache_job(job); + job = NULL; +} + +static void eio_uncached_read_done(struct kcached_job *job) +{ + struct eio_bio *ebio = job->ebio; + struct cache_c *dmc = job->dmc; + struct eio_bio *iebio; + struct eio_bio *nebio; + unsigned long flags = 0; + + if (ebio->eb_bc->bc_dir == UNCACHED_READ) { + EIO_ASSERT(ebio != NULL); + iebio = ebio->eb_next; + while (iebio != NULL) { + nebio = iebio->eb_next; + if (iebio->eb_index != -1) { + spin_lock_irqsave(&dmc-> + cache_sets[iebio-> + eb_cacheset]. + cs_lock, flags); + if (unlikely + (EIO_CACHE_STATE_GET(dmc, iebio->eb_index) & + QUEUED)) { + EIO_CACHE_STATE_SET(dmc, + iebio->eb_index, + INVALID); + atomic64_dec_if_positive(&dmc-> + eio_stats. + cached_blocks); + } else + if (EIO_CACHE_STATE_GET + (dmc, + iebio->eb_index) & CACHEREADINPROG) { + /*turn off the cache read in prog flag*/ + EIO_CACHE_STATE_OFF(dmc, + iebio->eb_index, + BLOCK_IO_INPROG); + } else + /*Should never reach here*/ + EIO_ASSERT(0); + spin_unlock_irqrestore(&dmc-> + cache_sets[iebio-> + eb_cacheset]. + cs_lock, flags); + } + eb_endio(iebio, 0); + iebio = nebio; + } + eb_endio(ebio, 0); + eio_free_cache_job(job); + } else if (ebio->eb_bc->bc_dir == UNCACHED_READ_AND_READFILL) { + /* + * Kick off the READFILL. It will also do a read + * from SSD, in case of ALREADY_DIRTY block + */ + job->action = READFILL; + eio_enqueue_readfill(dmc, job); + } else + /* Should never reach here for uncached read */ + EIO_ASSERT(0); +} + +static void eio_io_callback(int error, void *context) +{ + struct kcached_job *job = (struct kcached_job *)context; + struct cache_c *dmc = job->dmc; + + job->error = error; + INIT_WORK(&job->work, eio_post_io_callback); + queue_work(dmc->callback_q, &job->work); + return; +} + +static void eio_post_io_callback(struct work_struct *work) +{ + struct kcached_job *job; + struct cache_c *dmc; + struct eio_bio *ebio; + unsigned long flags = 0; + index_t index; + unsigned eb_cacheset; + u_int8_t cstate; + int callendio = 0; + int error; + + job = container_of(work, struct kcached_job, work); + dmc = job->dmc; + index = job->index; + error = job->error; + + EIO_ASSERT(index != -1 || job->action == WRITEDISK + || job->action == READDISK); + ebio = job->ebio; + EIO_ASSERT(ebio != NULL); + EIO_ASSERT(ebio->eb_bc); + + eb_cacheset = ebio->eb_cacheset; + if (error) + pr_err("io_callback: io error %d block %llu action %d", + error, + (unsigned long long)job->job_io_regions.disk.sector, + job->action); + + switch (job->action) { + case WRITEDISK: + + atomic64_inc(&dmc->eio_stats.writedisk); + if (unlikely(error)) + dmc->eio_errors.disk_write_errors++; + if (unlikely(error) || (ebio->eb_iotype & EB_INVAL)) + eio_inval_range(dmc, ebio->eb_sector, ebio->eb_size); + if (ebio->eb_next) + eio_flag_abios(dmc, ebio->eb_next, + error || (ebio->eb_iotype & EB_INVAL)); + eb_endio(ebio, error); + job->ebio = NULL; + eio_free_cache_job(job); + return; + + case READDISK: + + if (unlikely(error) || unlikely(ebio->eb_iotype & EB_INVAL) + || CACHE_DEGRADED_IS_SET(dmc)) { + if (error) + dmc->eio_errors.disk_read_errors++; + eio_inval_range(dmc, ebio->eb_sector, ebio->eb_size); + eio_flag_abios(dmc, ebio->eb_next, 1); + } else if (ebio->eb_next) { + eio_uncached_read_done(job); + return; + } + eb_endio(ebio, error); + job->ebio = NULL; + eio_free_cache_job(job); + return; + + case READCACHE: + + /*atomic64_inc(&dmc->eio_stats.readcache);*/ + /*SECTOR_STATS(dmc->eio_stats.ssd_reads, ebio->eb_size);*/ + EIO_ASSERT(EIO_DBN_GET(dmc, index) == + EIO_ROUND_SECTOR(dmc, ebio->eb_sector)); + cstate = EIO_CACHE_STATE_GET(dmc, index); + /* We shouldn't reach here for DIRTY_INPROG blocks. */ + EIO_ASSERT(cstate != DIRTY_INPROG); + if (unlikely(error)) { + dmc->eio_errors.ssd_read_errors++; + /* Retry read from HDD for non-DIRTY blocks. */ + if (cstate != ALREADY_DIRTY) { + spin_lock_irqsave(&dmc->cache_sets[eb_cacheset]. + cs_lock, flags); + EIO_CACHE_STATE_OFF(dmc, ebio->eb_index, + CACHEREADINPROG); + EIO_CACHE_STATE_ON(dmc, ebio->eb_index, + DISKREADINPROG); + spin_unlock_irqrestore(&dmc-> + cache_sets[eb_cacheset]. + cs_lock, flags); + + eio_push_ssdread_failures(job); + schedule_work(&_kcached_wq); + + return; + } + } + callendio = 1; + break; + + case READFILL: + + /*atomic64_inc(&dmc->eio_stats.readfill);*/ + /*SECTOR_STATS(dmc->eio_stats.ssd_writes, ebio->eb_size);*/ + EIO_ASSERT(EIO_DBN_GET(dmc, index) == ebio->eb_sector); + if (unlikely(error)) + dmc->eio_errors.ssd_write_errors++; + if (!(EIO_CACHE_STATE_GET(dmc, index) & CACHEWRITEINPROG)) { + pr_debug("DISKWRITEINPROG absent in READFILL \ + sector %llu io size %u\n", + (unsigned long long)ebio->eb_sector, + ebio->eb_size); + } + callendio = 1; + break; + + case WRITECACHE: + + /*SECTOR_STATS(dmc->eio_stats.ssd_writes, ebio->eb_size);*/ + /*atomic64_inc(&dmc->eio_stats.writecache);*/ + cstate = EIO_CACHE_STATE_GET(dmc, index); + EIO_ASSERT(EIO_DBN_GET(dmc, index) == + EIO_ROUND_SECTOR(dmc, ebio->eb_sector)); + /* CWIP is a must for WRITECACHE, except when it is DIRTY */ + EIO_ASSERT(cstate & (CACHEWRITEINPROG | DIRTY)); + if (likely(error == 0)) { + /* If it is a DIRTY inprog block, proceed for metadata update */ + if (cstate == DIRTY_INPROG) { + eio_md_write(job); + return; + } + } else { + /* TODO: ask if this if condition is required */ + if (dmc->mode == CACHE_MODE_WT) + dmc->eio_errors.disk_write_errors++; + dmc->eio_errors.ssd_write_errors++; + } + job->ebio = NULL; + break; + + default: + pr_err("io_callback: invalid action %d", job->action); + return; + } + + spin_lock_irqsave(&dmc->cache_sets[eb_cacheset].cs_lock, flags); + + cstate = EIO_CACHE_STATE_GET(dmc, index); + EIO_ASSERT(!(cstate & INVALID)); + + if (unlikely + ((job->action == WRITECACHE) && !(cstate & DISKWRITEINPROG))) { + /* + * Can reach here in 2 cases: + * 1. Uncached write case, where WRITEDISK has finished first + * 2. Cached write case + * + * For DIRTY or DIRTY inprog cases, use eb holdcount to determine + * if end ebio can be called. This is because, we don't set DWIP etc + * flags on those and we have to avoid double end ebio call + */ + EIO_ASSERT((cstate != DIRTY_INPROG) || error); + callendio = 1; + if ((cstate & DIRTY) + && !atomic_dec_and_test(&ebio->eb_holdcount)) + callendio = 0; + } + + if (cstate & DISKWRITEINPROG) { + /* uncached write and WRITEDISK is not yet finished */ + EIO_ASSERT(!(cstate & DIRTY)); /* For dirty blocks, we can't have DWIP flag */ + if (error) + EIO_CACHE_STATE_ON(dmc, index, QUEUED); + EIO_CACHE_STATE_OFF(dmc, index, CACHEWRITEINPROG); + } else if (unlikely(error || (cstate & QUEUED))) { + /* Error or QUEUED is set: mark block as INVALID for non-DIRTY blocks */ + if (cstate != ALREADY_DIRTY) { + EIO_CACHE_STATE_SET(dmc, index, INVALID); + atomic64_dec_if_positive(&dmc->eio_stats.cached_blocks); + } + } else if (cstate & VALID) { + EIO_CACHE_STATE_OFF(dmc, index, BLOCK_IO_INPROG); + /* + * If we have NO_SSD_IO_INPROG flag set, then this block needs to be + * invalidated. There are three things that can happen -- (i) error, + * (ii) IOs are queued on this block, and (iii) success. + * + * If there was an error or if the QUEUED bit was set, then the logic + * in the if part will take care of setting the block to INVALID. + * Therefore, this is the success path where we invalidate if need be. + */ + + /* + * TBD + * NO_SSD_IO_INPROG need to be differently handled, in case block is DIRTY + */ + if ((cstate & NO_SSD_IO_INPROG) == NO_SSD_IO_INPROG) + EIO_CACHE_STATE_OFF(dmc, index, VALID); + } + + spin_unlock_irqrestore(&dmc->cache_sets[eb_cacheset].cs_lock, flags); + + if (callendio) + eb_endio(ebio, error); + + eio_free_cache_job(job); + job = NULL; + +} + +/* + * This function processes the kcached_job that + * needs to be scheduled on disk after ssd read failures. + */ +void eio_ssderror_diskread(struct kcached_job *job) +{ + struct cache_c *dmc; + struct eio_bio *ebio; + index_t index; + int error; + unsigned long flags = 0; + + dmc = job->dmc; + error = 0; + + /* + * 1. Extract the ebio which needs to be scheduled on disk. + * 2. Verify cache block state is VALID + * 3. Make sure that the cache state in not IOINPROG + */ + /* Reset the ssd read error in the job. */ + job->error = 0; + ebio = job->ebio; + index = ebio->eb_index; + + EIO_ASSERT(index != -1); + + spin_lock_irqsave(&dmc->cache_sets[index / dmc->assoc].cs_lock, flags); + EIO_ASSERT(EIO_CACHE_STATE_GET(dmc, index) & DISKREADINPROG); + spin_unlock_irqrestore(&dmc->cache_sets[index / dmc->assoc].cs_lock, + flags); + + EIO_ASSERT(ebio->eb_dir == READ); + + atomic64_inc(&dmc->eio_stats.readdisk); + SECTOR_STATS(dmc->eio_stats.disk_reads, ebio->eb_size); + job->action = READDISK; + + error = eio_io_async_bvec(dmc, &job->job_io_regions.disk, ebio->eb_dir, + ebio->eb_bv, ebio->eb_nbvec, + eio_disk_io_callback, job, 1); + + /* + * In case of disk i/o submission error clear ebio and kcached_job. + * This would return the actual read that was issued on ssd. + */ + if (error) + goto out; + + return; + +out: + /* We failed to submit the I/O to dm layer. The corresponding + * block should be marked as INVALID by turning off already set + * flags. + */ + spin_lock_irqsave(&dmc->cache_sets[index / dmc->assoc].cs_lock, flags); + EIO_CACHE_STATE_SET(dmc, ebio->eb_index, INVALID); + spin_unlock_irqrestore(&dmc->cache_sets[index / dmc->assoc].cs_lock, + flags); + + atomic64_dec_if_positive(&dmc->eio_stats.cached_blocks); + + eb_endio(ebio, error); + ebio = NULL; + job->ebio = NULL; + eio_free_cache_job(job); +} + +/* Adds clean set request to clean queue. */ +static void eio_addto_cleanq(struct cache_c *dmc, index_t set, int whole) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&dmc->cache_sets[set].cs_lock, flags); + + if (dmc->cache_sets[set].flags & SETFLAG_CLEAN_INPROG) { + /* Clean already in progress, just add to clean pendings */ + spin_unlock_irqrestore(&dmc->cache_sets[set].cs_lock, flags); + return; + } + + dmc->cache_sets[set].flags |= SETFLAG_CLEAN_INPROG; + if (whole) + dmc->cache_sets[set].flags |= SETFLAG_CLEAN_WHOLE; + + spin_unlock_irqrestore(&dmc->cache_sets[set].cs_lock, flags); + + spin_lock_irqsave(&dmc->clean_sl, flags); + list_add_tail(&dmc->cache_sets[set].list, &dmc->cleanq); + atomic64_inc(&dmc->clean_pendings); + EIO_SET_EVENT_AND_UNLOCK(&dmc->clean_event, &dmc->clean_sl, flags); + return; +} + +/* + * Clean thread loops forever in this, waiting for + * new clean set requests in the clean queue. + */ +int eio_clean_thread_proc(void *context) +{ + struct cache_c *dmc = (struct cache_c *)context; + unsigned long flags = 0; + u_int64_t systime; + index_t index; + + /* Sync makes sense only for writeback cache */ + EIO_ASSERT(dmc->mode == CACHE_MODE_WB); + + dmc->clean_thread_running = 1; + + /* + * Using sysctl_fast_remove to stop the clean thread + * works for now. Should have another flag specifically + * for such notification. + */ + for (; !dmc->sysctl_active.fast_remove; ) { + LIST_HEAD(setlist); + struct cache_set *set; + + eio_comply_dirty_thresholds(dmc, -1); + + if (dmc->sysctl_active.do_clean) { + /* pause the periodic clean */ + cancel_delayed_work_sync(&dmc->clean_aged_sets_work); + + /* clean all the sets */ + eio_clean_all(dmc); + + /* resume the periodic clean */ + spin_lock_irqsave(&dmc->dirty_set_lru_lock, flags); + dmc->is_clean_aged_sets_sched = 0; + if (dmc->sysctl_active.time_based_clean_interval + && atomic64_read(&dmc->nr_dirty)) { + /* there is a potential race here, If a sysctl changes + the time_based_clean_interval to 0. However a strong + synchronisation is not necessary here + */ + schedule_delayed_work(&dmc-> + clean_aged_sets_work, + dmc->sysctl_active. + time_based_clean_interval + * 60 * HZ); + dmc->is_clean_aged_sets_sched = 1; + } + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, flags); + } + + if (dmc->sysctl_active.fast_remove) + break; + + spin_lock_irqsave(&dmc->clean_sl, flags); + + while (! + ((!list_empty(&dmc->cleanq)) + || dmc->sysctl_active.fast_remove + || dmc->sysctl_active.do_clean)) + EIO_WAIT_EVENT(&dmc->clean_event, &dmc->clean_sl, + flags); + + /* + * Move cleanq elements to a private list for processing. + */ + + list_add(&setlist, &dmc->cleanq); + list_del(&dmc->cleanq); + INIT_LIST_HEAD(&dmc->cleanq); + + spin_unlock_irqrestore(&dmc->clean_sl, flags); + + systime = jiffies; + while (!list_empty(&setlist)) { + set = + list_entry((&setlist)->next, struct cache_set, + list); + list_del(&set->list); + index = set - dmc->cache_sets; + if (!(dmc->sysctl_active.fast_remove)) { + eio_clean_set(dmc, index, + set->flags & SETFLAG_CLEAN_WHOLE, + 0); + } else { + + /* + * Since we are not cleaning the set, we should + * put the set back in the lru list so that + * it is picked up at a later point. + * We also need to clear the clean inprog flag + * otherwise this set would never be cleaned. + */ + + spin_lock_irqsave(&dmc->cache_sets[index]. + cs_lock, flags); + dmc->cache_sets[index].flags &= + ~(SETFLAG_CLEAN_INPROG | + SETFLAG_CLEAN_WHOLE); + spin_unlock_irqrestore(&dmc->cache_sets[index]. + cs_lock, flags); + spin_lock_irqsave(&dmc->dirty_set_lru_lock, + flags); + lru_touch(dmc->dirty_set_lru, index, systime); + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, + flags); + } + atomic64_dec(&dmc->clean_pendings); + } + } + + /* notifier for cache delete that the clean thread has stopped running */ + dmc->clean_thread_running = 0; + + eio_thread_exit(0); + + /*Should never reach here*/ + return 0; +} + +/* + * Cache miss support. We read the data from disk, write it to the ssd. + * To avoid doing 1 IO at a time to the ssd, when the IO is kicked off, + * we enqueue it to a "readfill" queue in the cache in cache sector order. + * The worker thread can then issue all of these IOs and do 1 unplug to + * start them all. + * + */ +static void eio_enqueue_readfill(struct cache_c *dmc, struct kcached_job *job) +{ + unsigned long flags = 0; + struct kcached_job **j1, *next; + int do_schedule = 0; + + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + /* Insert job in sorted order of cache sector */ + j1 = &dmc->readfill_queue; + while (*j1 != NULL && (*j1)->job_io_regions.cache.sector < + job->job_io_regions.cache.sector) + j1 = &(*j1)->next; + next = *j1; + *j1 = job; + job->next = next; + do_schedule = (dmc->readfill_in_prog == 0); + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + if (do_schedule) + schedule_work(&dmc->readfill_wq); +} + +void eio_do_readfill(struct work_struct *work) +{ + struct kcached_job *job, *joblist; + struct eio_bio *ebio; + unsigned long flags = 0; + struct kcached_job *nextjob = NULL; + struct cache_c *dmc = container_of(work, struct cache_c, readfill_wq); + + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + if (dmc->readfill_in_prog) + goto out; + dmc->readfill_in_prog = 1; + while (dmc->readfill_queue != NULL) { + joblist = dmc->readfill_queue; + dmc->readfill_queue = NULL; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + for (job = joblist; job != NULL; job = nextjob) { + struct eio_bio *iebio; + struct eio_bio *next; + + nextjob = job->next; /* save for later because 'job' will be freed */ + EIO_ASSERT(job->action == READFILL); + /* Write to cache device */ + ebio = job->ebio; + iebio = ebio->eb_next; + EIO_ASSERT(iebio); + /* other iebios are anchored on this bio. Create + * jobs for them and then issue ios + */ + do { + struct kcached_job *job; + int err; + unsigned long flags; + index_t index; + next = iebio->eb_next; + index = iebio->eb_index; + if (index == -1) { + CTRACE("eio_do_readfill:1\n"); + /* Any INPROG(including DIRTY_INPROG) case would fall here */ + eb_endio(iebio, 0); + iebio = NULL; + } else { + spin_lock_irqsave(&dmc-> + cache_sets[iebio-> + eb_cacheset]. + cs_lock, flags); + /* If this block was already valid, we don't need to write it */ + if (unlikely + (EIO_CACHE_STATE_GET(dmc, index) & + QUEUED)) { + /*An invalidation request is queued. Can't do anything*/ + CTRACE("eio_do_readfill:2\n"); + EIO_CACHE_STATE_SET(dmc, index, + INVALID); + spin_unlock_irqrestore(&dmc-> + cache_sets + [iebio-> + eb_cacheset]. + cs_lock, + flags); + atomic64_dec_if_positive(&dmc-> + eio_stats. + cached_blocks); + eb_endio(iebio, 0); + iebio = NULL; + } else + if ((EIO_CACHE_STATE_GET(dmc, index) + & (VALID | DISKREADINPROG)) + == (VALID | DISKREADINPROG)) { + /* Do readfill. */ + EIO_CACHE_STATE_SET(dmc, index, + VALID | + CACHEWRITEINPROG); + EIO_ASSERT(EIO_DBN_GET(dmc, index) + == iebio->eb_sector); + spin_unlock_irqrestore(&dmc-> + cache_sets + [iebio-> + eb_cacheset]. + cs_lock, + flags); + job = + eio_new_job(dmc, iebio, + iebio-> + eb_index); + if (unlikely(job == NULL)) + err = -ENOMEM; + else { + err = 0; + job->action = READFILL; + atomic_inc(&dmc-> + nr_jobs); + SECTOR_STATS(dmc-> + eio_stats. + ssd_readfills, + iebio-> + eb_size); + SECTOR_STATS(dmc-> + eio_stats. + ssd_writes, + iebio-> + eb_size); + atomic64_inc(&dmc-> + eio_stats. + readfill); + atomic64_inc(&dmc-> + eio_stats. + writecache); + err = + eio_io_async_bvec + (dmc, + &job-> + job_io_regions. + cache, WRITE, + iebio->eb_bv, + iebio->eb_nbvec, + eio_io_callback, + job, 0); + } + if (err) { + pr_err + ("eio_do_readfill: IO submission failed, block %llu", + EIO_DBN_GET(dmc, + index)); + spin_lock_irqsave(&dmc-> + cache_sets + [iebio-> + eb_cacheset]. + cs_lock, + flags); + EIO_CACHE_STATE_SET(dmc, + iebio-> + eb_index, + INVALID); + spin_unlock_irqrestore + (&dmc-> + cache_sets[iebio-> + eb_cacheset]. + cs_lock, flags); + atomic64_dec_if_positive + (&dmc->eio_stats. + cached_blocks); + eb_endio(iebio, err); + + if (job) { + eio_free_cache_job + (job); + job = NULL; + } + } + } else + if (EIO_CACHE_STATE_GET(dmc, index) + == ALREADY_DIRTY) { + + spin_unlock_irqrestore(&dmc-> + cache_sets + [iebio-> + eb_cacheset]. + cs_lock, + flags); + + /* + * DIRTY block handling: + * Read the dirty data from the cache block to update + * the data buffer already read from the disk + */ + job = + eio_new_job(dmc, iebio, + iebio-> + eb_index); + if (unlikely(job == NULL)) + err = -ENOMEM; + else { + job->action = READCACHE; + SECTOR_STATS(dmc-> + eio_stats. + ssd_reads, + iebio-> + eb_size); + atomic64_inc(&dmc-> + eio_stats. + readcache); + err = + eio_io_async_bvec + (dmc, + &job-> + job_io_regions. + cache, READ, + iebio->eb_bv, + iebio->eb_nbvec, + eio_io_callback, + job, 0); + } + + if (err) { + pr_err + ("eio_do_readfill: dirty block read IO submission failed, block %llu", + EIO_DBN_GET(dmc, + index)); + /* can't invalidate the DIRTY block, just return error */ + eb_endio(iebio, err); + if (job) { + eio_free_cache_job + (job); + job = NULL; + } + } + } else + if ((EIO_CACHE_STATE_GET(dmc, index) + & (VALID | CACHEREADINPROG)) + == (VALID | CACHEREADINPROG)) { + /*turn off the cache read in prog flag + don't need to write the cache block*/ + CTRACE("eio_do_readfill:3\n"); + EIO_CACHE_STATE_OFF(dmc, index, + BLOCK_IO_INPROG); + spin_unlock_irqrestore(&dmc-> + cache_sets + [iebio-> + eb_cacheset]. + cs_lock, + flags); + eb_endio(iebio, 0); + iebio = NULL; + } else { + panic("Unknown condition"); + spin_unlock_irqrestore(&dmc-> + cache_sets + [iebio-> + eb_cacheset]. + cs_lock, + flags); + } + } + iebio = next; + } while (iebio); + eb_endio(ebio, 0); + ebio = NULL; + eio_free_cache_job(job); + } + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + } + dmc->readfill_in_prog = 0; +out: + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + atomic64_inc(&dmc->eio_stats.ssd_readfill_unplugs); + eio_unplug_cache_device(dmc); +} + +/* + * Map a block from the source device to a block in the cache device. + */ +static u_int32_t hash_block(struct cache_c *dmc, sector_t dbn) +{ + u_int32_t set_number; + + set_number = eio_hash_block(dmc, dbn); + return set_number; +} + +static void +find_valid_dbn(struct cache_c *dmc, sector_t dbn, + index_t start_index, index_t *index) +{ + index_t i; + index_t end_index = start_index + dmc->assoc; + + for (i = start_index; i < end_index; i++) { + if ((EIO_CACHE_STATE_GET(dmc, i) & VALID) + && EIO_DBN_GET(dmc, i) == dbn) { + *index = i; + if ((EIO_CACHE_STATE_GET(dmc, i) & BLOCK_IO_INPROG) == + 0) + eio_policy_reclaim_lru_movetail(dmc, i, + dmc-> + policy_ops); + return; + } + } + *index = -1; +} + +static index_t find_invalid_dbn(struct cache_c *dmc, index_t start_index) +{ + index_t i; + index_t end_index = start_index + dmc->assoc; + + /* Find INVALID slot that we can reuse */ + for (i = start_index; i < end_index; i++) { + if (EIO_CACHE_STATE_GET(dmc, i) == INVALID) { + eio_policy_reclaim_lru_movetail(dmc, i, + dmc->policy_ops); + return i; + } + } + return -1; +} + +/* Search for a slot that we can reclaim */ +static void +find_reclaim_dbn(struct cache_c *dmc, index_t start_index, index_t *index) +{ + eio_find_reclaim_dbn(dmc->policy_ops, start_index, index); +} + +void eio_set_warm_boot(void) +{ + eio_force_warm_boot = 1; + return; +} + +/* + * dbn is the starting sector. + */ +static int +eio_lookup(struct cache_c *dmc, struct eio_bio *ebio, index_t *index) +{ + sector_t dbn = EIO_ROUND_SECTOR(dmc, ebio->eb_sector); + u_int32_t set_number; + index_t invalid, oldest_clean = -1; + index_t start_index; + + /*ASK it is assumed that the lookup is being done for a single block*/ + set_number = hash_block(dmc, dbn); + start_index = dmc->assoc * set_number; + find_valid_dbn(dmc, dbn, start_index, index); + if (*index >= 0) + /* We found the exact range of blocks we are looking for */ + return VALID; + + invalid = find_invalid_dbn(dmc, start_index); + if (invalid == -1) + /* We didn't find an invalid entry, search for oldest valid entry */ + find_reclaim_dbn(dmc, start_index, &oldest_clean); + /* + * Cache miss : + * We can't choose an entry marked INPROG, but choose the oldest + * INVALID or the oldest VALID entry. + */ + *index = start_index + dmc->assoc; + if (invalid != -1) { + *index = invalid; + return INVALID; + } else if (oldest_clean != -1) { + *index = oldest_clean; + return VALID; + } + return -1; +} + +/* Do metadata update for a set */ +static void eio_do_mdupdate(struct work_struct *work) +{ + struct mdupdate_request *mdreq; + struct cache_set *set; + struct cache_c *dmc; + unsigned long flags; + index_t i; + index_t start_index; + index_t end_index; + index_t min_index; + index_t max_index; + struct flash_cacheblock *md_blocks; + struct eio_bio *ebio; + u_int8_t cstate; + struct eio_io_region region; + unsigned pindex; + int error, j; + index_t blk_index; + int k; + void *pg_virt_addr[2] = { NULL }; + u_int8_t sector_bits[2] = { 0 }; + int startbit, endbit; + int rw_flags = 0; + + mdreq = container_of(work, struct mdupdate_request, work); + dmc = mdreq->dmc; + set = &dmc->cache_sets[mdreq->set]; + + mdreq->error = 0; + EIO_ASSERT(mdreq->mdblk_bvecs); + + /* + * md_size = dmc->assoc * sizeof(struct flash_cacheblock); + * Currently, md_size is 8192 bytes, mdpage_count is 2 pages maximum. + */ + + EIO_ASSERT(mdreq->mdbvec_count && mdreq->mdbvec_count <= 2); + EIO_ASSERT((dmc->assoc == 512) || mdreq->mdbvec_count == 1); + for (k = 0; k < (int)mdreq->mdbvec_count; k++) + pg_virt_addr[k] = kmap(mdreq->mdblk_bvecs[k].bv_page); + + spin_lock_irqsave(&set->cs_lock, flags); + + start_index = mdreq->set * dmc->assoc; + end_index = start_index + dmc->assoc; + + pindex = 0; + md_blocks = (struct flash_cacheblock *)pg_virt_addr[pindex]; + j = MD_BLOCKS_PER_PAGE; + + /* initialize the md blocks to write */ + for (i = start_index; i < end_index; i++) { + cstate = EIO_CACHE_STATE_GET(dmc, i); + md_blocks->dbn = cpu_to_le64(EIO_DBN_GET(dmc, i)); + if (cstate == ALREADY_DIRTY) + md_blocks->cache_state = cpu_to_le64((VALID | DIRTY)); + else + md_blocks->cache_state = cpu_to_le64(INVALID); + md_blocks++; + j--; + + if ((j == 0) && (++pindex < mdreq->mdbvec_count)) { + md_blocks = + (struct flash_cacheblock *)pg_virt_addr[pindex]; + j = MD_BLOCKS_PER_PAGE; + } + + } + + /* Update the md blocks with the pending mdlist */ + min_index = start_index; + max_index = start_index; + + pindex = 0; + md_blocks = (struct flash_cacheblock *)pg_virt_addr[pindex]; + + ebio = mdreq->pending_mdlist; + while (ebio) { + EIO_ASSERT(EIO_CACHE_STATE_GET(dmc, ebio->eb_index) == + DIRTY_INPROG); + + blk_index = ebio->eb_index - start_index; + pindex = INDEX_TO_MD_PAGE(blk_index); + blk_index = INDEX_TO_MD_PAGE_OFFSET(blk_index); + sector_bits[pindex] |= (1 << INDEX_TO_MD_SECTOR(blk_index)); + + md_blocks = (struct flash_cacheblock *)pg_virt_addr[pindex]; + md_blocks[blk_index].cache_state = (VALID | DIRTY); + + if (min_index > ebio->eb_index) + min_index = ebio->eb_index; + + if (max_index < ebio->eb_index) + max_index = ebio->eb_index; + + ebio = ebio->eb_next; + } + + /* + * Below code may be required when selective pages need to be + * submitted for metadata update. Currently avoiding the optimization + * for correctness validation. + */ + + /* + min_cboff = (min_index - start_index) / MD_BLOCKS_PER_CBLOCK(dmc); + max_cboff = (max_index - start_index) / MD_BLOCKS_PER_CBLOCK(dmc); + write_size = ((uint32_t)(max_cboff - min_cboff + 1)) << dmc->block_shift; + EIO_ASSERT(write_size && (write_size <= eio_to_sector(mdreq->md_size))); + */ + + /* Move the pending mdlist to inprog list */ + mdreq->inprog_mdlist = mdreq->pending_mdlist; + mdreq->pending_mdlist = NULL; + + spin_unlock_irqrestore(&set->cs_lock, flags); + + for (k = 0; k < (int)mdreq->mdbvec_count; k++) + kunmap(mdreq->mdblk_bvecs[k].bv_page); + + /* + * Initiate the I/O to SSD for on-disk md update. + * TBD. Optimize to write only the affected blocks + */ + + region.bdev = dmc->cache_dev->bdev; + /*region.sector = dmc->md_start_sect + INDEX_TO_MD_SECTOR(start_index) + + (min_cboff << dmc->block_shift); */ + + atomic_set(&mdreq->holdcount, 1); + for (i = 0; i < mdreq->mdbvec_count; i++) { + if (!sector_bits[i]) + continue; + startbit = -1; + j = 0; + while (startbit == -1) { + if (sector_bits[i] & (1 << j)) + startbit = j; + j++; + } + endbit = -1; + j = 7; + while (endbit == -1) { + if (sector_bits[i] & (1 << j)) + endbit = j; + j--; + } + EIO_ASSERT(startbit <= endbit && startbit >= 0 && startbit <= 7 && + endbit >= 0 && endbit <= 7); + EIO_ASSERT(dmc->assoc != 128 || endbit <= 3); + region.sector = + dmc->md_start_sect + INDEX_TO_MD_SECTOR(start_index) + + i * SECTORS_PER_PAGE + startbit; + region.count = endbit - startbit + 1; + mdreq->mdblk_bvecs[i].bv_offset = to_bytes(startbit); + mdreq->mdblk_bvecs[i].bv_len = to_bytes(region.count); + + EIO_ASSERT(region.sector <= + (dmc->md_start_sect + INDEX_TO_MD_SECTOR(end_index))); + atomic64_inc(&dmc->eio_stats.md_ssd_writes); + SECTOR_STATS(dmc->eio_stats.ssd_writes, to_bytes(region.count)); + atomic_inc(&mdreq->holdcount); + + /* + * Set SYNC for making metadata + * writes as high priority. + */ + rw_flags = WRITE | REQ_SYNC; + error = eio_io_async_bvec(dmc, ®ion, rw_flags, + &mdreq->mdblk_bvecs[i], 1, + eio_mdupdate_callback, work, 0); + if (error && !(mdreq->error)) + mdreq->error = error; + } + if (atomic_dec_and_test(&mdreq->holdcount)) { + INIT_WORK(&mdreq->work, eio_post_mdupdate); + queue_work(dmc->mdupdate_q, &mdreq->work); + } +} + +/* Callback function for ondisk metadata update */ +static void eio_mdupdate_callback(int error, void *context) +{ + struct work_struct *work = (struct work_struct *)context; + struct mdupdate_request *mdreq; + + mdreq = container_of(work, struct mdupdate_request, work); + if (error && !(mdreq->error)) + mdreq->error = error; + if (!atomic_dec_and_test(&mdreq->holdcount)) + return; + INIT_WORK(&mdreq->work, eio_post_mdupdate); + queue_work(mdreq->dmc->mdupdate_q, &mdreq->work); +} + +static void eio_post_mdupdate(struct work_struct *work) +{ + struct mdupdate_request *mdreq; + struct cache_set *set; + struct cache_c *dmc; + unsigned long flags; + struct eio_bio *ebio; + struct eio_bio *nebio; + int more_pending_mdupdates = 0; + int error; + index_t set_index; + + mdreq = container_of(work, struct mdupdate_request, work); + + dmc = mdreq->dmc; + EIO_ASSERT(dmc); + set_index = mdreq->set; + set = &dmc->cache_sets[set_index]; + error = mdreq->error; + + /* Update in-core cache metadata */ + + spin_lock_irqsave(&set->cs_lock, flags); + + /* + * Update dirty inprog blocks. + * On error, convert them to INVALID + * On success, convert them to ALREADY_DIRTY + */ + ebio = mdreq->inprog_mdlist; + while (ebio) { + EIO_ASSERT(EIO_CACHE_STATE_GET(dmc, ebio->eb_index) == + DIRTY_INPROG); + if (unlikely(error)) { + EIO_CACHE_STATE_SET(dmc, ebio->eb_index, INVALID); + atomic64_dec_if_positive(&dmc->eio_stats.cached_blocks); + } else { + EIO_CACHE_STATE_SET(dmc, ebio->eb_index, ALREADY_DIRTY); + set->nr_dirty++; + atomic64_inc(&dmc->nr_dirty); + atomic64_inc(&dmc->eio_stats.md_write_dirty); + } + ebio = ebio->eb_next; + } + + /* + * If there are more pending requests for md update, + * need to pick up those using the current mdreq. + */ + if (mdreq->pending_mdlist) + more_pending_mdupdates = 1; + else + /* No request pending, we can free the mdreq */ + set->mdreq = NULL; + + /* + * After we unlock the set, we need to end the I/Os, + * which were processed as part of this md update + */ + + ebio = mdreq->inprog_mdlist; + mdreq->inprog_mdlist = NULL; + + spin_unlock_irqrestore(&set->cs_lock, flags); + + /* End the processed I/Os */ + while (ebio) { + nebio = ebio->eb_next; + eb_endio(ebio, error); + ebio = nebio; + } + + /* + * if dirty block was added + * 1. update the cache set lru list + * 2. check and initiate cleaning if thresholds are crossed + */ + if (!error) { + eio_touch_set_lru(dmc, set_index); + eio_comply_dirty_thresholds(dmc, set_index); + } + + if (more_pending_mdupdates) { + /* + * Schedule work to process the new + * pending mdupdate requests + */ + INIT_WORK(&mdreq->work, eio_do_mdupdate); + queue_work(dmc->mdupdate_q, &mdreq->work); + } else { + /* + * No more pending mdupdates. + * Free the mdreq. + */ + if (mdreq->mdblk_bvecs) { + eio_free_wb_bvecs(mdreq->mdblk_bvecs, + mdreq->mdbvec_count, + SECTORS_PER_PAGE); + kfree(mdreq->mdblk_bvecs); + } + + kfree(mdreq); + } +} + +/* Enqueue metadata update for marking dirty blocks on-disk/in-core */ +static void eio_enq_mdupdate(struct bio_container *bc) +{ + unsigned long flags = 0; + index_t set_index; + struct eio_bio *ebio; + struct cache_c *dmc = bc->bc_dmc; + struct cache_set *set = NULL; + struct mdupdate_request *mdreq; + int do_schedule; + + ebio = bc->bc_mdlist; + set_index = -1; + do_schedule = 0; + while (ebio) { + if (ebio->eb_cacheset != set_index) { + set_index = ebio->eb_cacheset; + set = &dmc->cache_sets[set_index]; + spin_lock_irqsave(&set->cs_lock, flags); + } + EIO_ASSERT(ebio->eb_cacheset == set_index); + + bc->bc_mdlist = ebio->eb_next; + + if (!set->mdreq) { + /* Pick up one mdreq from bc */ + mdreq = bc->mdreqs; + EIO_ASSERT(mdreq != NULL); + bc->mdreqs = bc->mdreqs->next; + mdreq->next = NULL; + mdreq->pending_mdlist = ebio; + mdreq->dmc = dmc; + mdreq->set = set_index; + set->mdreq = mdreq; + ebio->eb_next = NULL; + do_schedule = 1; + } else { + mdreq = set->mdreq; + EIO_ASSERT(mdreq != NULL); + ebio->eb_next = mdreq->pending_mdlist; + mdreq->pending_mdlist = ebio; + } + + ebio = bc->bc_mdlist; + if (!ebio || ebio->eb_cacheset != set_index) { + spin_unlock_irqrestore(&set->cs_lock, flags); + if (do_schedule) { + INIT_WORK(&mdreq->work, eio_do_mdupdate); + queue_work(dmc->mdupdate_q, &mdreq->work); + do_schedule = 0; + } + } + } + + EIO_ASSERT(bc->bc_mdlist == NULL); +} + +/* Kick-off a cache metadata update for marking the blocks dirty */ +void eio_md_write(struct kcached_job *job) +{ + struct eio_bio *ebio = job->ebio; + struct eio_bio *nebio; + struct eio_bio *pebio; + struct bio_container *bc = ebio->eb_bc; + unsigned long flags; + int enqueue = 0; + + /* + * ebios are stored in ascending order of cache sets. + */ + + spin_lock_irqsave(&bc->bc_lock, flags); + EIO_ASSERT(bc->bc_mdwait > 0); + nebio = bc->bc_mdlist; + pebio = NULL; + while (nebio) { + if (nebio->eb_cacheset > ebio->eb_cacheset) + break; + pebio = nebio; + nebio = nebio->eb_next; + } + ebio->eb_next = nebio; + if (!pebio) + bc->bc_mdlist = ebio; + else + pebio->eb_next = ebio; + bc->bc_mdwait--; + if (bc->bc_mdwait == 0) + enqueue = 1; + spin_unlock_irqrestore(&bc->bc_lock, flags); + + eio_free_cache_job(job); + + if (enqueue) + eio_enq_mdupdate(bc); +} + +/* Ensure cache level dirty thresholds compliance. If required, trigger cache-wide clean */ +static void eio_check_dirty_cache_thresholds(struct cache_c *dmc) +{ + if (DIRTY_CACHE_THRESHOLD_CROSSED(dmc)) { + int64_t required_cleans; + int64_t enqueued_cleans; + u_int64_t set_time; + index_t set_index; + unsigned long flags; + + spin_lock_irqsave(&dmc->clean_sl, flags); + if (atomic64_read(&dmc->clean_pendings) + || dmc->clean_excess_dirty) { + /* Already excess dirty block cleaning is in progress */ + spin_unlock_irqrestore(&dmc->clean_sl, flags); + return; + } + dmc->clean_excess_dirty = 1; + spin_unlock_irqrestore(&dmc->clean_sl, flags); + + /* Clean needs to be triggered on the cache */ + required_cleans = atomic64_read(&dmc->nr_dirty) - + (EIO_DIV((dmc->sysctl_active.dirty_low_threshold * dmc->size), + 100)); + enqueued_cleans = 0; + + spin_lock_irqsave(&dmc->dirty_set_lru_lock, flags); + do { + lru_rem_head(dmc->dirty_set_lru, &set_index, &set_time); + if (set_index == LRU_NULL) + break; + + enqueued_cleans += dmc->cache_sets[set_index].nr_dirty; + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, flags); + eio_addto_cleanq(dmc, set_index, 1); + spin_lock_irqsave(&dmc->dirty_set_lru_lock, flags); + } while (enqueued_cleans <= required_cleans); + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, flags); + spin_lock_irqsave(&dmc->clean_sl, flags); + dmc->clean_excess_dirty = 0; + spin_unlock_irqrestore(&dmc->clean_sl, flags); + } +} + +/* Ensure set level dirty thresholds compliance. If required, trigger set clean */ +static void eio_check_dirty_set_thresholds(struct cache_c *dmc, index_t set) +{ + if (DIRTY_SET_THRESHOLD_CROSSED(dmc, set)) { + eio_addto_cleanq(dmc, set, 0); + return; + } +} + +/* Ensure various cache thresholds compliance. If required trigger clean */ +void eio_comply_dirty_thresholds(struct cache_c *dmc, index_t set) +{ + /* + * 1. Don't trigger new cleanings if + * - cache is not wb + * - autoclean threshold is crossed + * - fast remove in progress is set + * - cache is in failed mode. + * 2. Initiate set-wide clean, if set level dirty threshold is crossed + * 3. Initiate cache-wide clean, if cache level dirty threshold is crossed + */ + + if (unlikely(CACHE_FAILED_IS_SET(dmc))) { + pr_debug + ("eio_comply_dirty_thresholds: Cache %s is in failed mode.\n", + dmc->cache_name); + return; + } + + if (AUTOCLEAN_THRESHOLD_CROSSED(dmc) || (dmc->mode != CACHE_MODE_WB)) + return; + + if (set != -1) + eio_check_dirty_set_thresholds(dmc, set); + eio_check_dirty_cache_thresholds(dmc); +} + +/* Do read from cache */ +static void +eio_cached_read(struct cache_c *dmc, struct eio_bio *ebio, int rw_flags) +{ + struct kcached_job *job; + index_t index = ebio->eb_index; + int err = 0; + + job = eio_new_job(dmc, ebio, index); + + if (unlikely(job == NULL)) + err = -ENOMEM; + else { + job->action = READCACHE; /* Fetch data from cache */ + atomic_inc(&dmc->nr_jobs); + + SECTOR_STATS(dmc->eio_stats.read_hits, ebio->eb_size); + SECTOR_STATS(dmc->eio_stats.ssd_reads, ebio->eb_size); + atomic64_inc(&dmc->eio_stats.readcache); + err = + eio_io_async_bvec(dmc, &job->job_io_regions.cache, rw_flags, + ebio->eb_bv, ebio->eb_nbvec, + eio_io_callback, job, 0); + + } + if (err) { + unsigned long flags; + pr_err("eio_cached_read: IO submission failed, block %llu", + EIO_DBN_GET(dmc, index)); + spin_lock_irqsave(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, + flags); + /* + * For already DIRTY block, invalidation is too costly, skip it. + * For others, mark the block as INVALID and return error. + */ + if (EIO_CACHE_STATE_GET(dmc, ebio->eb_index) != ALREADY_DIRTY) { + EIO_CACHE_STATE_SET(dmc, ebio->eb_index, INVALID); + atomic64_dec_if_positive(&dmc->eio_stats.cached_blocks); + } + spin_unlock_irqrestore(&dmc->cache_sets[ebio->eb_cacheset]. + cs_lock, flags); + eb_endio(ebio, err); + ebio = NULL; + if (job) { + job->ebio = NULL; + eio_free_cache_job(job); + job = NULL; + } + } +} + +/* + * Invalidate any colliding blocks if they are !BUSY and !DIRTY. In BUSY case, + * we need to wait until the underlying IO is finished, and then proceed with + * the invalidation, so a QUEUED flag is added. + */ +static int +eio_inval_block_set_range(struct cache_c *dmc, int set, sector_t iosector, + unsigned iosize, int multiblk) +{ + int start_index, end_index, i; + sector_t endsector = iosector + eio_to_sector(iosize); + + start_index = dmc->assoc * set; + end_index = start_index + dmc->assoc; + for (i = start_index; i < end_index; i++) { + sector_t start_dbn; + sector_t end_dbn; + + if (EIO_CACHE_STATE_GET(dmc, i) & INVALID) + continue; + start_dbn = EIO_DBN_GET(dmc, i); + end_dbn = start_dbn + dmc->block_size; + + if (!(endsector <= start_dbn || iosector >= end_dbn)) { + + if (! + (EIO_CACHE_STATE_GET(dmc, i) & + (BLOCK_IO_INPROG | DIRTY | QUEUED))) { + EIO_CACHE_STATE_SET(dmc, i, INVALID); + atomic64_dec_if_positive(&dmc->eio_stats. + cached_blocks); + if (multiblk) + continue; + return 0; + } + + /* Skip queued flag for DIRTY(inprog or otherwise) blocks. */ + if (!(EIO_CACHE_STATE_GET(dmc, i) & (DIRTY | QUEUED))) + /* BLOCK_IO_INPROG is set. Set QUEUED flag */ + EIO_CACHE_STATE_ON(dmc, i, QUEUED); + + if (!multiblk) + return 1; + } + } + return 0; +} + +int +eio_invalidate_sanity_check(struct cache_c *dmc, u_int64_t iosector, + u_int64_t *num_sectors) +{ + u_int64_t disk_size; + + /* + * Sanity check the arguements + */ + if (unlikely(*num_sectors == 0)) { + pr_info + ("invaldate_sector_range: nothing to do because number of sectors specified is zero"); + return -EINVAL; + } + + disk_size = eio_to_sector(eio_get_device_size(dmc->disk_dev)); + if (iosector >= disk_size) { + pr_err + ("eio_inval_range: nothing to do because starting sector is past last sector (%lu > %lu)", + (long unsigned int)iosector, (long unsigned int)disk_size); + return -EINVAL; + } + + if ((iosector + (*num_sectors)) > disk_size) { + pr_info + ("eio_inval_range: trimming range because there are less sectors to invalidate than requested. (%lu < %lu)", + (long unsigned int)(disk_size - iosector), + (long unsigned int)*num_sectors); + *num_sectors = (disk_size - iosector); + } + + return 0; +} + +void eio_inval_range(struct cache_c *dmc, sector_t iosector, unsigned iosize) +{ + u_int32_t bset; + sector_t snum; + sector_t snext; + unsigned ioinset; + unsigned long flags; + int totalsshift = dmc->block_shift + dmc->consecutive_shift; + + snum = iosector; + while (iosize) { + bset = hash_block(dmc, snum); + snext = ((snum >> totalsshift) + 1) << totalsshift; + ioinset = (unsigned)to_bytes(snext - snum); + if (ioinset > iosize) + ioinset = iosize; + spin_lock_irqsave(&dmc->cache_sets[bset].cs_lock, flags); + eio_inval_block_set_range(dmc, bset, snum, ioinset, 1); + spin_unlock_irqrestore(&dmc->cache_sets[bset].cs_lock, flags); + snum = snext; + iosize -= ioinset; + } +} + +/* + * Invalidates all cached blocks without waiting for them to complete + * Should be called with incoming IO suspended + */ +int eio_invalidate_cache(struct cache_c *dmc) +{ + u_int64_t i = 0; + unsigned long flags = 0; + sector_t disk_dev_size = to_bytes(eio_get_device_size(dmc->disk_dev)); + + /* invalidate the whole cache */ + for (i = 0; i < (dmc->size >> dmc->consecutive_shift); i++) { + spin_lock_irqsave(&dmc->cache_sets[i].cs_lock, flags); + /* TBD. Apply proper fix for the cast to disk_dev_size */ + (void)eio_inval_block_set_range(dmc, (int)i, 0, + (unsigned)disk_dev_size, 0); + spin_unlock_irqrestore(&dmc->cache_sets[i].cs_lock, flags); + } /* end - for all cachesets (i) */ + + return 0; /* i suspect we may need to return different statuses in the future */ +} /* eio_invalidate_cache */ + +static int eio_inval_block(struct cache_c *dmc, sector_t iosector) +{ + u_int32_t bset; + int queued; + + /*Chop lower bits of iosector*/ + iosector = EIO_ROUND_SECTOR(dmc, iosector); + bset = hash_block(dmc, iosector); + queued = eio_inval_block_set_range(dmc, bset, iosector, + (unsigned)to_bytes(dmc->block_size), + 0); + + return queued; +} + +/* Serving write I/Os, that involves both SSD and HDD */ +static int eio_uncached_write(struct cache_c *dmc, struct eio_bio *ebio) +{ + struct kcached_job *job; + int err = 0; + index_t index = ebio->eb_index; + unsigned long flags = 0; + u_int8_t cstate; + + if (index == -1) { + /* + * No work, if block is not allocated. + * Ensure, invalidation of the block at the end + */ + ebio->eb_iotype |= EB_INVAL; + return 0; + } + + spin_lock_irqsave(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, flags); + cstate = EIO_CACHE_STATE_GET(dmc, index); + EIO_ASSERT(cstate & (DIRTY | CACHEWRITEINPROG)); + if (cstate == ALREADY_DIRTY) { + /* + * Treat the dirty block cache write failure as + * I/O failure for the entire I/O + * TBD + * Can we live without this restriction + */ + ebio->eb_iotype = EB_MAIN_IO; + + /* + * We don't set inprog flag on dirty block. + * In lieu of the inprog flag, we are using the + * eb_holdcount for dirty block, so that the + * endio can be called, only when the write to disk + * and the write to cache both complete for the ebio + */ + atomic_inc(&ebio->eb_holdcount); + } else + /* ensure DISKWRITEINPROG for uncached write on non-DIRTY blocks */ + EIO_CACHE_STATE_ON(dmc, index, DISKWRITEINPROG); + + spin_unlock_irqrestore(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, + flags); + + job = eio_new_job(dmc, ebio, index); + if (unlikely(job == NULL)) + err = -ENOMEM; + else { + job->action = WRITECACHE; + SECTOR_STATS(dmc->eio_stats.ssd_writes, ebio->eb_size); + atomic64_inc(&dmc->eio_stats.writecache); + err = eio_io_async_bvec(dmc, &job->job_io_regions.cache, WRITE, + ebio->eb_bv, ebio->eb_nbvec, + eio_io_callback, job, 0); + } + + if (err) { + pr_err("eio_uncached_write: IO submission failed, block %llu", + EIO_DBN_GET(dmc, index)); + spin_lock_irqsave(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, + flags); + if (EIO_CACHE_STATE_GET(dmc, ebio->eb_index) == ALREADY_DIRTY) + /* + * Treat I/O failure on a DIRTY block as failure of entire I/O. + * TBD + * Can do better error handling by invalidation of the dirty + * block, if the cache block write failed, but disk write succeeded + */ + ebio->eb_bc->bc_error = err; + else { + /* Mark the block as INVALID for non-DIRTY block. */ + EIO_CACHE_STATE_SET(dmc, ebio->eb_index, INVALID); + atomic64_dec_if_positive(&dmc->eio_stats.cached_blocks); + /* Set the INVAL flag to ensure block is marked invalid at the end */ + ebio->eb_iotype |= EB_INVAL; + ebio->eb_index = -1; + } + spin_unlock_irqrestore(&dmc->cache_sets[ebio->eb_cacheset]. + cs_lock, flags); + if (job) { + job->ebio = NULL; + eio_free_cache_job(job); + job = NULL; + } + } + + return err; +} + +/* Serving write I/Os that can be fulfilled just by SSD */ +static int +eio_cached_write(struct cache_c *dmc, struct eio_bio *ebio, int rw_flags) +{ + struct kcached_job *job; + int err = 0; + index_t index = ebio->eb_index; + unsigned long flags = 0; + u_int8_t cstate; + + /* + * WRITE (I->DV) + * WRITE (V->DV) + * WRITE (V1->DV2) + * WRITE (DV->DV) + */ + + /* Possible only in writeback caching mode */ + EIO_ASSERT(dmc->mode == CACHE_MODE_WB); + + /* + * TBD + * Possibly don't need the spinlock-unlock here + */ + spin_lock_irqsave(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, flags); + cstate = EIO_CACHE_STATE_GET(dmc, index); + if (!(cstate & DIRTY)) { + EIO_ASSERT(cstate & CACHEWRITEINPROG); + /* make sure the block is marked DIRTY inprogress */ + EIO_CACHE_STATE_SET(dmc, index, DIRTY_INPROG); + } + spin_unlock_irqrestore(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, + flags); + + job = eio_new_job(dmc, ebio, index); + if (unlikely(job == NULL)) + err = -ENOMEM; + else { + job->action = WRITECACHE; + + SECTOR_STATS(dmc->eio_stats.ssd_writes, ebio->eb_size); + atomic64_inc(&dmc->eio_stats.writecache); + EIO_ASSERT((rw_flags & 1) == WRITE); + err = + eio_io_async_bvec(dmc, &job->job_io_regions.cache, rw_flags, + ebio->eb_bv, ebio->eb_nbvec, + eio_io_callback, job, 0); + + } + + if (err) { + pr_err("eio_cached_write: IO submission failed, block %llu", + EIO_DBN_GET(dmc, index)); + spin_lock_irqsave(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, + flags); + cstate = EIO_CACHE_STATE_GET(dmc, index); + if (cstate == DIRTY_INPROG) { + /* A DIRTY(inprog) block should be invalidated on error */ + EIO_CACHE_STATE_SET(dmc, ebio->eb_index, INVALID); + atomic64_dec_if_positive(&dmc->eio_stats.cached_blocks); + } else + /* An already DIRTY block don't have an option but just return error. */ + EIO_ASSERT(cstate == ALREADY_DIRTY); + spin_unlock_irqrestore(&dmc->cache_sets[ebio->eb_cacheset]. + cs_lock, flags); + eb_endio(ebio, err); + ebio = NULL; + if (job) { + job->ebio = NULL; + eio_free_cache_job(job); + job = NULL; + } + } + + return err; +} + +static struct eio_bio *eio_new_ebio(struct cache_c *dmc, struct bio *bio, + unsigned *presidual_biovec, sector_t snum, + int iosize, struct bio_container *bc, + int iotype) +{ + struct eio_bio *ebio; + int residual_biovec = *presidual_biovec; + int numbvecs = 0; + int ios; + + if (residual_biovec) { + int bvecindex = bio->bi_idx; + int rbvindex; + + /* Calculate the number of bvecs required */ + ios = iosize; + while (ios > 0) { + int len; + + if (ios == iosize) + len = + bio->bi_io_vec[bvecindex].bv_len - + residual_biovec; + else + len = bio->bi_io_vec[bvecindex].bv_len; + + numbvecs++; + if (len > ios) + len = ios; + ios -= len; + bvecindex++; + } + ebio = + kmalloc(sizeof(struct eio_bio) + + numbvecs * sizeof(struct bio_vec), GFP_NOWAIT); + + if (!ebio) + return ERR_PTR(-ENOMEM); + + rbvindex = 0; + ios = iosize; + while (ios > 0) { + ebio->eb_rbv[rbvindex].bv_page = + bio->bi_io_vec[bio->bi_idx].bv_page; + ebio->eb_rbv[rbvindex].bv_offset = + bio->bi_io_vec[bio->bi_idx].bv_offset + + residual_biovec; + ebio->eb_rbv[rbvindex].bv_len = + bio->bi_io_vec[bio->bi_idx].bv_len - + residual_biovec; + if (ebio->eb_rbv[rbvindex].bv_len > (unsigned)ios) { + residual_biovec += ios; + ebio->eb_rbv[rbvindex].bv_len = ios; + } else { + residual_biovec = 0; + bio->bi_idx++; + } + ios -= ebio->eb_rbv[rbvindex].bv_len; + rbvindex++; + } + EIO_ASSERT(rbvindex == numbvecs); + ebio->eb_bv = ebio->eb_rbv; + } else { + ebio = kmalloc(sizeof(struct eio_bio), GFP_NOWAIT); + + if (!ebio) + return ERR_PTR(-ENOMEM); + ebio->eb_bv = bio->bi_io_vec + bio->bi_idx; + ios = iosize; + while (ios > 0) { + numbvecs++; + if ((unsigned)ios < bio->bi_io_vec[bio->bi_idx].bv_len) { + residual_biovec = ios; + ios = 0; + } else { + ios -= bio->bi_io_vec[bio->bi_idx].bv_len; + bio->bi_idx++; + } + } + } + EIO_ASSERT(ios == 0); + EIO_ASSERT(numbvecs != 0); + *presidual_biovec = residual_biovec; + + ebio->eb_sector = snum; + ebio->eb_cacheset = hash_block(dmc, snum); + ebio->eb_size = iosize; + ebio->eb_dir = bio_data_dir(bio); + ebio->eb_next = NULL; + ebio->eb_index = -1; + ebio->eb_iotype = iotype; + ebio->eb_nbvec = numbvecs; + + bc_addfb(bc, ebio); + + /* Always set the holdcount for eb to 1, to begin with. */ + atomic_set(&ebio->eb_holdcount, 1); + + return ebio; +} + +/* Issues HDD I/O */ +static void +eio_disk_io(struct cache_c *dmc, struct bio *bio, + struct eio_bio *anchored_bios, struct bio_container *bc, + int force_inval) +{ + struct eio_bio *ebio; + struct kcached_job *job; + int residual_biovec = 0; + int error = 0; + + /*disk io happens on whole bio. Reset bi_idx*/ + bio->bi_idx = 0; + ebio = + eio_new_ebio(dmc, bio, &residual_biovec, bio->bi_sector, + bio->bi_size, bc, EB_MAIN_IO); + + if (unlikely(IS_ERR(ebio))) { + bc->bc_error = error = PTR_ERR(ebio); + ebio = NULL; + goto errout; + } + + if (force_inval) + ebio->eb_iotype |= EB_INVAL; + ebio->eb_next = anchored_bios; /*Anchor the ebio list to this super bio*/ + job = eio_new_job(dmc, ebio, -1); + + if (unlikely(job == NULL)) { + error = -ENOMEM; + goto errout; + } + atomic_inc(&dmc->nr_jobs); + if (ebio->eb_dir == READ) { + job->action = READDISK; + SECTOR_STATS(dmc->eio_stats.disk_reads, bio->bi_size); + atomic64_inc(&dmc->eio_stats.readdisk); + } else { + job->action = WRITEDISK; + SECTOR_STATS(dmc->eio_stats.disk_writes, bio->bi_size); + atomic64_inc(&dmc->eio_stats.writedisk); + } + + /* + * Pass the original bio flags as is, while doing + * read / write to HDD. + */ + VERIFY_BIO_FLAGS(ebio); + error = eio_io_async_bvec(dmc, &job->job_io_regions.disk, + GET_BIO_FLAGS(ebio), + ebio->eb_bv, ebio->eb_nbvec, + eio_io_callback, job, 1); + + if (error) { + job->ebio = NULL; + eio_free_cache_job(job); + goto errout; + } + return; + +errout: + eio_inval_range(dmc, bio->bi_sector, bio->bi_size); + eio_flag_abios(dmc, anchored_bios, error); + + if (ebio) + eb_endio(ebio, error); + return; +} + +/*Given a sector number and biosize, returns cache io size*/ +static unsigned int +eio_get_iosize(struct cache_c *dmc, sector_t snum, unsigned int biosize) +{ + unsigned int iosize; + unsigned int swithinblock = snum & (dmc->block_size - 1); + + /*Check whether io starts at a cache block boundary*/ + if (swithinblock) + iosize = (unsigned int)to_bytes(dmc->block_size - swithinblock); + else + iosize = (unsigned int)to_bytes(dmc->block_size); + if (iosize > biosize) + iosize = biosize; + return iosize; +} + +/* Insert a new set sequence in sorted order to existing set sequence list */ +static int +insert_set_seq(struct set_seq **seq_list, index_t first_set, index_t last_set) +{ + struct set_seq *cur_seq = NULL; + struct set_seq *prev_seq = NULL; + struct set_seq *new_seq = NULL; + + EIO_ASSERT((first_set != -1) && (last_set != -1) + && (last_set >= first_set)); + + for (cur_seq = *seq_list; cur_seq; + prev_seq = cur_seq, cur_seq = cur_seq->next) { + if (first_set > cur_seq->last_set) + /* go for the next seq in the sorted seq list */ + continue; + + if (last_set < cur_seq->first_set) + /* break here to insert the new seq to seq list at this point */ + break; + + /* + * There is an overlap of the new seq with the current seq. + * Adjust the first_set field of the current seq to consume + * the overlap. + */ + if (first_set < cur_seq->first_set) + cur_seq->first_set = first_set; + + if (last_set <= cur_seq->last_set) + /* The current seq now fully encompasses the first and last sets */ + return 0; + + /* Increment the first set so as to start from, where the current seq left */ + first_set = cur_seq->last_set + 1; + } + + new_seq = kmalloc(sizeof(struct set_seq), GFP_NOWAIT); + if (new_seq == NULL) + return -ENOMEM; + new_seq->first_set = first_set; + new_seq->last_set = last_set; + if (prev_seq) { + new_seq->next = prev_seq->next; + prev_seq->next = new_seq; + } else { + new_seq->next = *seq_list; + *seq_list = new_seq; + } + + return 0; +} + +/* Acquire read/shared lock for the sets covering the entire I/O range */ +static int eio_acquire_set_locks(struct cache_c *dmc, struct bio_container *bc) +{ + struct bio *bio = bc->bc_bio; + sector_t round_sector; + sector_t end_sector; + sector_t set_size; + index_t cur_set; + index_t first_set; + index_t last_set; + index_t i; + struct set_seq *cur_seq; + struct set_seq *next_seq; + int error; + + /* + * Find first set using start offset of the I/O and lock it. + * Find next sets by adding the set offsets to the previous set + * Identify all the sequences of set numbers that need locking. + * Keep the sequences in sorted list. + * For each set in each sequence + * - acquire read lock on the set. + */ + + round_sector = EIO_ROUND_SET_SECTOR(dmc, bio->bi_sector); + set_size = dmc->block_size * dmc->assoc; + end_sector = bio->bi_sector + eio_to_sector(bio->bi_size); + first_set = -1; + last_set = -1; + cur_set = -1; + bc->bc_setspan = NULL; + + while (round_sector < end_sector) { + cur_set = hash_block(dmc, round_sector); + if (first_set == -1) { + first_set = cur_set; + last_set = cur_set; + } else if (cur_set == (last_set + 1)) + last_set = cur_set; + else { + /* + * Add the seq of start, end set to sorted (first, last) seq list + * and reinit the first and last set + */ + error = + insert_set_seq(&bc->bc_setspan, first_set, + last_set); + if (error) + goto err_out; + first_set = cur_set; + last_set = cur_set; + } + + round_sector += set_size; + } + + /* Add the remaining first, last set sequence */ + + EIO_ASSERT((first_set != -1) && (last_set == cur_set)); + + if (bc->bc_setspan == NULL) { + /* No sequence was added, can use singlespan */ + cur_seq = &bc->bc_singlesspan; + cur_seq->first_set = first_set; + cur_seq->last_set = last_set; + cur_seq->next = NULL; + bc->bc_setspan = cur_seq; + } else { + error = insert_set_seq(&bc->bc_setspan, first_set, last_set); + if (error) + goto err_out; + } + + /* Acquire read locks on the sets in the set span */ + for (cur_seq = bc->bc_setspan; cur_seq; cur_seq = cur_seq->next) + for (i = cur_seq->first_set; i <= cur_seq->last_set; i++) + down_read(&dmc->cache_sets[i].rw_lock); + + return 0; + +err_out: + + /* Free the seqs in the seq list, unless it is just the local seq */ + if (bc->bc_setspan != &bc->bc_singlesspan) { + for (cur_seq = bc->bc_setspan; cur_seq; cur_seq = next_seq) { + next_seq = cur_seq->next; + kfree(cur_seq); + } + } + return error; +} + +/* + * Allocate mdreq and md_blocks for each set. + */ +static int eio_alloc_mdreqs(struct cache_c *dmc, struct bio_container *bc) +{ + index_t i; + struct mdupdate_request *mdreq; + int nr_bvecs, ret; + struct set_seq *cur_seq; + + bc->mdreqs = NULL; + + for (cur_seq = bc->bc_setspan; cur_seq; cur_seq = cur_seq->next) { + for (i = cur_seq->first_set; i <= cur_seq->last_set; i++) { + mdreq = kzalloc(sizeof(*mdreq), GFP_NOWAIT); + if (mdreq) { + mdreq->md_size = + dmc->assoc * + sizeof(struct flash_cacheblock); + nr_bvecs = + IO_BVEC_COUNT(mdreq->md_size, + SECTORS_PER_PAGE); + + mdreq->mdblk_bvecs = + (struct bio_vec *) + kmalloc(sizeof(struct bio_vec) * nr_bvecs, + GFP_KERNEL); + if (mdreq->mdblk_bvecs) { + + ret = + eio_alloc_wb_bvecs(mdreq-> + mdblk_bvecs, + nr_bvecs, + SECTORS_PER_PAGE); + if (ret) { + pr_err + ("eio_alloc_mdreqs: failed to allocated pages\n"); + kfree(mdreq->mdblk_bvecs); + mdreq->mdblk_bvecs = NULL; + } + mdreq->mdbvec_count = nr_bvecs; + } + } + + if (unlikely + ((mdreq == NULL) || (mdreq->mdblk_bvecs == NULL))) { + struct mdupdate_request *nmdreq; + + mdreq = bc->mdreqs; + while (mdreq) { + nmdreq = mdreq->next; + if (mdreq->mdblk_bvecs) { + eio_free_wb_bvecs(mdreq-> + mdblk_bvecs, + mdreq-> + mdbvec_count, + SECTORS_PER_PAGE); + kfree(mdreq->mdblk_bvecs); + } + kfree(mdreq); + mdreq = nmdreq; + } + bc->mdreqs = NULL; + return -ENOMEM; + } else { + mdreq->next = bc->mdreqs; + bc->mdreqs = mdreq; + } + } + } + + return 0; + +} + +/* + * Release: + * 1. the set locks covering the entire I/O range + * 2. any previously allocated memory for md update + */ +static int +eio_release_io_resources(struct cache_c *dmc, struct bio_container *bc) +{ + index_t i; + struct mdupdate_request *mdreq; + struct mdupdate_request *nmdreq; + struct set_seq *cur_seq; + struct set_seq *next_seq; + + /* Release read locks on the sets in the set span */ + for (cur_seq = bc->bc_setspan; cur_seq; cur_seq = cur_seq->next) + for (i = cur_seq->first_set; i <= cur_seq->last_set; i++) + up_read(&dmc->cache_sets[i].rw_lock); + + /* Free the seqs in the set span, unless it is single span */ + if (bc->bc_setspan != &bc->bc_singlesspan) { + for (cur_seq = bc->bc_setspan; cur_seq; cur_seq = next_seq) { + next_seq = cur_seq->next; + kfree(cur_seq); + } + } + + mdreq = bc->mdreqs; + while (mdreq) { + nmdreq = mdreq->next; + if (mdreq->mdblk_bvecs) { + eio_free_wb_bvecs(mdreq->mdblk_bvecs, + mdreq->mdbvec_count, + SECTORS_PER_PAGE); + kfree(mdreq->mdblk_bvecs); + } + kfree(mdreq); + mdreq = nmdreq; + } + bc->mdreqs = NULL; + + return 0; +} + +/* + * Decide the mapping and perform necessary cache operations for a bio request. + */ +int eio_map(struct cache_c *dmc, struct request_queue *rq, struct bio *bio) +{ + sector_t sectors = eio_to_sector(bio->bi_size); + struct eio_bio *ebio = NULL; + struct bio_container *bc; + sector_t snum; + unsigned int iosize; + unsigned int totalio; + unsigned int biosize; + unsigned int residual_biovec; + unsigned int force_uncached = 0; + int data_dir = bio_data_dir(bio); + + /*bio list*/ + struct eio_bio *ebegin = NULL; + struct eio_bio *eend = NULL; + struct eio_bio *enext = NULL; + + EIO_ASSERT(bio->bi_idx == 0); + + pr_debug("this needs to be removed immediately\n"); + + if (bio_rw_flagged(bio, REQ_DISCARD)) { + pr_debug + ("eio_map: Discard IO received. Invalidate incore start=%lu totalsectors=%d.\n", + (unsigned long)bio->bi_sector, + (int)eio_to_sector(bio->bi_size)); + bio_endio(bio, 0); + pr_err + ("eio_map: I/O with Discard flag received. Discard flag is not supported.\n"); + return 0; + } + + if (unlikely(dmc->cache_rdonly)) { + if (data_dir != READ) { + bio_endio(bio, -EPERM); + pr_debug + ("eio_map: cache is read only, write not permitted\n"); + return 0; + } + } + + if (sectors < SIZE_HIST) + atomic64_inc(&dmc->size_hist[sectors]); + + if (data_dir == READ) { + SECTOR_STATS(dmc->eio_stats.reads, bio->bi_size); + atomic64_inc(&dmc->eio_stats.readcount); + } else { + SECTOR_STATS(dmc->eio_stats.writes, bio->bi_size); + atomic64_inc(&dmc->eio_stats.writecount); + } + + /* + * Cache FAILED mode is like Hard failure. + * Dont allow I/Os to go through. + */ + if (unlikely(CACHE_FAILED_IS_SET(dmc))) { + /*ASK confirm that once failed is set, it's never reset*/ + /* Source device is not available. */ + CTRACE + ("eio_map:2 source device is not present. Cache is in Failed state\n"); + bio_endio(bio, -ENODEV); + bio = NULL; + return DM_MAPIO_SUBMITTED; + } + + /* WB cache will never be in degraded mode. */ + if (unlikely(CACHE_DEGRADED_IS_SET(dmc))) { + EIO_ASSERT(dmc->mode != CACHE_MODE_WB); + force_uncached = 1; + } + + /* + * Process zero sized bios by passing original bio flags + * to both HDD and SSD. + */ + if (bio->bi_size == 0) { + eio_process_zero_size_bio(dmc, bio); + return DM_MAPIO_SUBMITTED; + } + + /* Create a bio container */ + + bc = kzalloc(sizeof(struct bio_container), GFP_NOWAIT); + if (!bc) { + bio_endio(bio, -ENOMEM); + return DM_MAPIO_SUBMITTED; + } + bc->bc_iotime = jiffies; + bc->bc_bio = bio; + bc->bc_dmc = dmc; + spin_lock_init(&bc->bc_lock); + atomic_set(&bc->bc_holdcount, 1); + bc->bc_error = 0; + + snum = bio->bi_sector; + totalio = bio->bi_size; + biosize = bio->bi_size; + residual_biovec = 0; + + if (dmc->mode == CACHE_MODE_WB) { + int ret; + /* + * For writeback, the app I/O and the clean I/Os + * need to be exclusive for a cache set. Acquire shared + * lock on the cache set for app I/Os and exclusive + * lock on the cache set for clean I/Os. + */ + ret = eio_acquire_set_locks(dmc, bc); + if (ret) { + bio_endio(bio, ret); + kfree(bc); + return DM_MAPIO_SUBMITTED; + } + } + + atomic64_inc(&dmc->nr_ios); + + /* + * Prepare for I/O processing. + * - Allocate ebios. + * - For reads, identify if we need to do uncached read + * - If force uncached I/O is set, invalidate the cache blocks for the I/O + */ + + if (force_uncached) + eio_inval_range(dmc, snum, totalio); + else { + while (biosize) { + iosize = eio_get_iosize(dmc, snum, biosize); + ebio = eio_new_ebio(dmc, bio, &residual_biovec, snum, + iosize, bc, EB_SUBORDINATE_IO); + if (IS_ERR(ebio)) { + bc->bc_error = -ENOMEM; + break; + } + + /* Anchor this ebio on ebio list. Preserve the order */ + if (ebegin) + eend->eb_next = ebio; + else + ebegin = ebio; + eend = ebio; + + biosize -= iosize; + snum += eio_to_sector(iosize); + } + } + + if (bc->bc_error) { + /* Error. Do ebio and bc cleanup. */ + ebio = ebegin; + while (ebio) { + enext = ebio->eb_next; + eb_endio(ebio, bc->bc_error); + ebio = enext; + } + + /* By now, the bc_holdcount must be 1 */ + EIO_ASSERT(atomic_read(&bc->bc_holdcount) == 1); + + /* Goto out to cleanup the bc(in bc_put()) */ + goto out; + } + + /* + * Start processing of the ebios. + * + * Note: don't return error from this point on. + * Error handling would be done as part of + * the processing of the ebios internally. + */ + if (force_uncached) { + EIO_ASSERT(dmc->mode != CACHE_MODE_WB); + if (data_dir == READ) + atomic64_inc(&dmc->eio_stats.uncached_reads); + else + atomic64_inc(&dmc->eio_stats.uncached_writes); + eio_disk_io(dmc, bio, ebegin, bc, 1); + } else if (data_dir == READ) { + + /* read io processing */ + eio_read(dmc, bc, ebegin); + } else + /* write io processing */ + eio_write(dmc, bc, ebegin); + +out: + + if (bc) + bc_put(bc, 0); + + return DM_MAPIO_SUBMITTED; +} + +/* + * Checks the cache block state, for deciding cached/uncached read. + * Also reserves/allocates the cache block, wherever necessary. + * + * Return values + * 1: cache hit + * 0: cache miss + */ +static int eio_read_peek(struct cache_c *dmc, struct eio_bio *ebio) +{ + index_t index; + int res; + int retval = 0; + unsigned long flags; + u_int8_t cstate; + + spin_lock_irqsave(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, flags); + + res = eio_lookup(dmc, ebio, &index); + ebio->eb_index = -1; + + if (res < 0) { + atomic64_inc(&dmc->eio_stats.noroom); + goto out; + } + + cstate = EIO_CACHE_STATE_GET(dmc, index); + + if (cstate & (BLOCK_IO_INPROG | QUEUED)) + /* + * We found a valid or invalid block but an io is on, so we can't + * proceed. Don't invalidate it. This implies that we'll + * have to read from disk. + * Read on a DIRTY | INPROG block (block which is going to be DIRTY) + * is also redirected to read from disk. + */ + goto out; + + if (res == VALID) { + EIO_ASSERT(cstate & VALID); + if ((EIO_DBN_GET(dmc, index) == + EIO_ROUND_SECTOR(dmc, ebio->eb_sector))) { + /* + * Read/write should be done on already DIRTY block + * without any inprog flag. + * Ensure that a failure of DIRTY block read is propagated to app. + * non-DIRTY valid blocks should have inprog flag. + */ + if (cstate == ALREADY_DIRTY) { + ebio->eb_iotype = EB_MAIN_IO; + /* + * Set to uncached read and readfill for now. + * It may change to CACHED_READ later, if all + * the blocks are found to be cached + */ + ebio->eb_bc->bc_dir = + UNCACHED_READ_AND_READFILL; + } else + EIO_CACHE_STATE_ON(dmc, index, CACHEREADINPROG); + retval = 1; + ebio->eb_index = index; + goto out; + } + + /* cache is marked readonly. Do not allow READFILL on SSD */ + if (unlikely(dmc->cache_rdonly)) + goto out; + + /* + * Found a block to be recycled. + * Its guranteed that it will be a non-DIRTY block + */ + EIO_ASSERT(!(cstate & DIRTY)); + if (eio_to_sector(ebio->eb_size) == dmc->block_size) { + /*We can recycle and then READFILL only if iosize is block size*/ + atomic64_inc(&dmc->eio_stats.rd_replace); + EIO_CACHE_STATE_SET(dmc, index, VALID | DISKREADINPROG); + EIO_DBN_SET(dmc, index, (sector_t)ebio->eb_sector); + ebio->eb_index = index; + ebio->eb_bc->bc_dir = UNCACHED_READ_AND_READFILL; + } + goto out; + } + EIO_ASSERT(res == INVALID); + + /* cache is marked readonly. Do not allow READFILL on SSD */ + if (unlikely(dmc->cache_rdonly)) + goto out; + /* + * Found an invalid block to be used. + * Can recycle only if iosize is block size + */ + if (eio_to_sector(ebio->eb_size) == dmc->block_size) { + EIO_ASSERT(cstate & INVALID); + EIO_CACHE_STATE_SET(dmc, index, VALID | DISKREADINPROG); + atomic64_inc(&dmc->eio_stats.cached_blocks); + EIO_DBN_SET(dmc, index, (sector_t)ebio->eb_sector); + ebio->eb_index = index; + ebio->eb_bc->bc_dir = UNCACHED_READ_AND_READFILL; + } + +out: + + spin_unlock_irqrestore(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, + flags); + + /* + * Enqueue clean set if there is no room in the set + * TBD + * Ensure, a force clean + */ + if (res < 0) + eio_comply_dirty_thresholds(dmc, ebio->eb_cacheset); + + return retval; +} + +/* + * Checks the cache block state, for deciding cached/uncached write. + * Also reserves/allocates the cache block, wherever necessary. + * + * Return values + * 1: cache block is available or newly allocated + * 0: cache block could not be got for the ebio + */ +static int eio_write_peek(struct cache_c *dmc, struct eio_bio *ebio) +{ + index_t index; + int res; + int retval; + u_int8_t cstate; + unsigned long flags; + + spin_lock_irqsave(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, flags); + + res = eio_lookup(dmc, ebio, &index); + ebio->eb_index = -1; + retval = 0; + + if (res < 0) { + /* cache block not found and new block couldn't be allocated */ + atomic64_inc(&dmc->eio_stats.noroom); + ebio->eb_iotype |= EB_INVAL; + goto out; + } + + cstate = EIO_CACHE_STATE_GET(dmc, index); + + if (cstate & (BLOCK_IO_INPROG | QUEUED)) { + ebio->eb_iotype |= EB_INVAL; + /* treat as if cache block is not available */ + goto out; + } + + if ((res == VALID) && (EIO_DBN_GET(dmc, index) == + EIO_ROUND_SECTOR(dmc, ebio->eb_sector))) { + /* + * Cache hit. + * All except an already DIRTY block should have an INPROG flag. + * If it is a cached write, a DIRTY flag would be added later. + */ + SECTOR_STATS(dmc->eio_stats.write_hits, ebio->eb_size); + if (cstate != ALREADY_DIRTY) + EIO_CACHE_STATE_ON(dmc, index, CACHEWRITEINPROG); + else + atomic64_inc(&dmc->eio_stats.dirty_write_hits); + ebio->eb_index = index; + /* + * A VALID block should get upgraded to DIRTY, only when we + * are updating the entire cache block(not partially). + * Otherwise, 2 sequential partial writes can lead to missing + * data when one write upgrades the cache block to DIRTY, while + * the other just writes to HDD. Subsequent read would be + * served from the cache block, which won't have the data from + * 2nd write. + */ + if ((cstate == ALREADY_DIRTY) || + (eio_to_sector(ebio->eb_size) == dmc->block_size)) + retval = 1; + else + retval = 0; + goto out; + + } + + /* + * cache miss with a new block allocated for recycle. + * Set INPROG flag, if the ebio size is equal to cache block size + */ + EIO_ASSERT(!(EIO_CACHE_STATE_GET(dmc, index) & DIRTY)); + if (eio_to_sector(ebio->eb_size) == dmc->block_size) { + if (res == VALID) + atomic64_inc(&dmc->eio_stats.wr_replace); + else + atomic64_inc(&dmc->eio_stats.cached_blocks); + EIO_CACHE_STATE_SET(dmc, index, VALID | CACHEWRITEINPROG); + EIO_DBN_SET(dmc, index, (sector_t)ebio->eb_sector); + ebio->eb_index = index; + retval = 1; + } else { + /* + * eb iosize smaller than cache block size shouldn't + * do cache write on a cache miss + */ + retval = 0; + ebio->eb_iotype |= EB_INVAL; + } + +out: + if ((retval == 1) && (dmc->mode == CACHE_MODE_WB) && + (cstate != ALREADY_DIRTY)) + ebio->eb_bc->bc_mdwait++; + + spin_unlock_irqrestore(&dmc->cache_sets[ebio->eb_cacheset].cs_lock, + flags); + + /* + * Enqueue clean set if there is no room in the set + * TBD + * Ensure, a force clean + */ + if (res < 0) + eio_comply_dirty_thresholds(dmc, ebio->eb_cacheset); + + return retval; +} + +/* Top level read function, called from eio_map */ +static void +eio_read(struct cache_c *dmc, struct bio_container *bc, struct eio_bio *ebegin) +{ + int ucread = 0; + struct eio_bio *ebio; + struct eio_bio *enext; + + bc->bc_dir = UNCACHED_READ; + ebio = ebegin; + while (ebio) { + enext = ebio->eb_next; + if (eio_read_peek(dmc, ebio) == 0) + ucread = 1; + ebio = enext; + } + + if (ucread) { + /* + * Uncached read. + * Start HDD I/O. Once that is finished + * readfill or dirty block re-read would start + */ + atomic64_inc(&dmc->eio_stats.uncached_reads); + eio_disk_io(dmc, bc->bc_bio, ebegin, bc, 0); + } else { + /* Cached read. Serve the read from SSD */ + + /* + * Pass all orig bio flags except UNPLUG. + * Unplug in the end if flagged. + */ + int rw_flags; + + rw_flags = 0; + + bc->bc_dir = CACHED_READ; + ebio = ebegin; + + VERIFY_BIO_FLAGS(ebio); + + EIO_ASSERT((rw_flags & 1) == READ); + while (ebio) { + enext = ebio->eb_next; + ebio->eb_iotype = EB_MAIN_IO; + + eio_cached_read(dmc, ebio, rw_flags); + ebio = enext; + } + } +} + +/* Top level write function called from eio_map */ +static void +eio_write(struct cache_c *dmc, struct bio_container *bc, struct eio_bio *ebegin) +{ + int ucwrite = 0; + int error = 0; + struct eio_bio *ebio; + struct eio_bio *enext; + + if ((dmc->mode != CACHE_MODE_WB) || + (dmc->sysctl_active.do_clean & EIO_CLEAN_KEEP)) + ucwrite = 1; + + ebio = ebegin; + while (ebio) { + enext = ebio->eb_next; + if (eio_write_peek(dmc, ebio) == 0) + ucwrite = 1; + ebio = enext; + } + + if (ucwrite) { + /* + * Uncached write. + * Start both SSD and HDD writes + */ + atomic64_inc(&dmc->eio_stats.uncached_writes); + bc->bc_mdwait = 0; + bc->bc_dir = UNCACHED_WRITE; + ebio = ebegin; + while (ebio) { + enext = ebio->eb_next; + eio_uncached_write(dmc, ebio); + ebio = enext; + } + + eio_disk_io(dmc, bc->bc_bio, ebegin, bc, 0); + } else { + /* Cached write. Start writes to SSD blocks */ + + int rw_flags; + rw_flags = 0; + + bc->bc_dir = CACHED_WRITE; + if (bc->bc_mdwait) { + + /* + * mdreqs are required only if the write would cause a metadata + * update. + */ + + error = eio_alloc_mdreqs(dmc, bc); + } + + /* + * Pass all orig bio flags except UNPLUG. + * UNPLUG in the end if flagged. + */ + ebio = ebegin; + VERIFY_BIO_FLAGS(ebio); + + while (ebio) { + enext = ebio->eb_next; + ebio->eb_iotype = EB_MAIN_IO; + + if (!error) { + + eio_cached_write(dmc, ebio, WRITE | rw_flags); + + } else { + unsigned long flags; + u_int8_t cstate; + + pr_err + ("eio_write: IO submission failed, block %llu", + EIO_DBN_GET(dmc, ebio->eb_index)); + spin_lock_irqsave(&dmc-> + cache_sets[ebio->eb_cacheset]. + cs_lock, flags); + cstate = + EIO_CACHE_STATE_GET(dmc, ebio->eb_index); + if (cstate != ALREADY_DIRTY) { + + /* + * A DIRTY(inprog) block should be invalidated on error. + */ + + EIO_CACHE_STATE_SET(dmc, ebio->eb_index, + INVALID); + atomic64_dec_if_positive(&dmc-> + eio_stats. + cached_blocks); + } + spin_unlock_irqrestore(&dmc-> + cache_sets[ebio-> + eb_cacheset]. + cs_lock, flags); + eb_endio(ebio, error); + } + ebio = enext; + } + } +} + +/* + * Synchronous clean of all the cache sets. Callers of this function needs + * to handle the situation that clean operation was aborted midway. + */ + +void eio_clean_all(struct cache_c *dmc) +{ + unsigned long flags = 0; + + EIO_ASSERT(dmc->mode == CACHE_MODE_WB); + for (atomic_set(&dmc->clean_index, 0); + (atomic_read(&dmc->clean_index) < + (s32)(dmc->size >> dmc->consecutive_shift)) + && (dmc->sysctl_active.do_clean & EIO_CLEAN_START) + && (atomic64_read(&dmc->nr_dirty) > 0) + && (!(dmc->cache_flags & CACHE_FLAGS_SHUTDOWN_INPROG) + && !dmc->sysctl_active.fast_remove); + atomic_inc(&dmc->clean_index)) { + + if (unlikely(CACHE_FAILED_IS_SET(dmc))) { + pr_err("clean_all: CACHE \"%s\" is in FAILED state.", + dmc->cache_name); + break; + } + + eio_clean_set(dmc, (index_t)(atomic_read(&dmc->clean_index)), + /* whole */ 1, /* force */ 1); + } + + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.do_clean &= ~EIO_CLEAN_START; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); +} + +/* + * Do unconditional clean of a cache. + * Useful for a cold enabled writeback cache. + */ +void eio_clean_for_reboot(struct cache_c *dmc) +{ + index_t i; + + for (i = 0; i < (index_t)(dmc->size >> dmc->consecutive_shift); i++) + eio_clean_set(dmc, i, /* whole */ 1, /* force */ 1); +} + +/* + * Used during the partial cache set clean. + * Uses reclaim policy(LRU/FIFO) information to + * identify the cache blocks that needs cleaning. + * The number of such cache blocks is determined + * by the high and low thresholds set. + */ +static void +eio_get_setblks_to_clean(struct cache_c *dmc, index_t set, int *ncleans) +{ + int i = 0; + int max_clean; + index_t start_index; + int nr_writes = 0; + + *ncleans = 0; + + max_clean = dmc->cache_sets[set].nr_dirty - + ((dmc->sysctl_active.dirty_set_low_threshold * dmc->assoc) / 100); + if (max_clean <= 0) + /* Nothing to clean */ + return; + + start_index = set * dmc->assoc; + + /* + * Spinlock is not required here, as we assume that we have + * taken a write lock on the cache set, when we reach here + */ + if (dmc->policy_ops == NULL) { + /* Scan sequentially in the set and pick blocks to clean */ + while ((i < (int)dmc->assoc) && (nr_writes < max_clean)) { + if ((EIO_CACHE_STATE_GET(dmc, start_index + i) & + (DIRTY | BLOCK_IO_INPROG)) == DIRTY) { + EIO_CACHE_STATE_ON(dmc, start_index + i, + DISKWRITEINPROG); + nr_writes++; + } + i++; + } + } else + nr_writes = + eio_policy_clean_set(dmc->policy_ops, set, max_clean); + + *ncleans = nr_writes; +} + +/* Callback function, when synchronous I/O completes */ +static void eio_sync_io_callback(int error, void *context) +{ + struct sync_io_context *sioc = (struct sync_io_context *)context; + + if (error) + sioc->sio_error = error; + up_read(&sioc->sio_lock); +} + +/* + * Setup biovecs for preallocated biovecs per cache set. + */ + +struct bio_vec *setup_bio_vecs(struct bio_vec *bvec, index_t block_index, + unsigned block_size, unsigned total, + unsigned *num_bvecs) +{ + struct bio_vec *data = NULL; + index_t iovec_index; + + switch (block_size) { + case BLKSIZE_2K: + *num_bvecs = total; + iovec_index = block_index; + data = &bvec[iovec_index]; + break; + + case BLKSIZE_4K: + *num_bvecs = total; + iovec_index = block_index; + data = &bvec[iovec_index]; + break; + + case BLKSIZE_8K: + /* + * For 8k data block size, we need 2 bio_vecs + * per data block. + */ + *num_bvecs = total * 2; + iovec_index = block_index * 2; + data = &bvec[iovec_index]; + break; + } + + return data; +} + +/* Cleans a given cache set */ +static void +eio_clean_set(struct cache_c *dmc, index_t set, int whole, int force) +{ + struct eio_io_region where; + int error; + index_t i; + index_t j; + index_t start_index; + index_t end_index; + struct sync_io_context sioc; + int ncleans = 0; + int alloc_size; + struct flash_cacheblock *md_blocks = NULL; + unsigned long flags; + + int pindex, k; + index_t blkindex; + struct bio_vec *bvecs; + unsigned nr_bvecs = 0, total; + void *pg_virt_addr[2] = { NULL }; + + /* Cache is failed mode, do nothing. */ + if (unlikely(CACHE_FAILED_IS_SET(dmc))) { + pr_debug("clean_set: CACHE \"%s\" is in FAILED state.", + dmc->cache_name); + goto err_out1; + } + + /* Nothing to clean, if there are no dirty blocks */ + if (dmc->cache_sets[set].nr_dirty == 0) + goto err_out1; + + /* If this is not the suitable time to clean, postpone it */ + if ((!force) && AUTOCLEAN_THRESHOLD_CROSSED(dmc)) { + eio_touch_set_lru(dmc, set); + goto err_out1; + } + + /* + * 1. Take exclusive lock on the cache set + * 2. Verify that there are dirty blocks to clean + * 3. Identify the cache blocks to clean + * 4. Read the cache blocks data from ssd + * 5. Write the cache blocks data to hdd + * 6. Update on-disk cache metadata + * 7. Update in-core cache metadata + */ + + start_index = set * dmc->assoc; + end_index = start_index + dmc->assoc; + + /* 1. exclusive lock. Let the ongoing writes to finish. Pause new writes */ + down_write(&dmc->cache_sets[set].rw_lock); + + /* 2. Return if there are no dirty blocks to clean */ + if (dmc->cache_sets[set].nr_dirty == 0) + goto err_out2; + + /* 3. identify and mark cache blocks to clean */ + if (!whole) + eio_get_setblks_to_clean(dmc, set, &ncleans); + else { + for (i = start_index; i < end_index; i++) { + if (EIO_CACHE_STATE_GET(dmc, i) == ALREADY_DIRTY) { + EIO_CACHE_STATE_SET(dmc, i, CLEAN_INPROG); + ncleans++; + } + } + } + + /* If nothing to clean, return */ + if (!ncleans) + goto err_out2; + + /* + * From this point onwards, make sure to reset + * the clean inflag on cache blocks before returning + */ + + /* 4. read cache set data */ + + init_rwsem(&sioc.sio_lock); + sioc.sio_error = 0; + + for (i = start_index; i < end_index; i++) { + if (EIO_CACHE_STATE_GET(dmc, i) == CLEAN_INPROG) { + + for (j = i; ((j < end_index) && + (EIO_CACHE_STATE_GET(dmc, j) == CLEAN_INPROG)); + j++); + + blkindex = (i - start_index); + total = (j - i); + + /* + * Get the correct index and number of bvecs + * setup from dmc->clean_dbvecs before issuing i/o. + */ + bvecs = + setup_bio_vecs(dmc->clean_dbvecs, blkindex, + dmc->block_size, total, &nr_bvecs); + EIO_ASSERT(bvecs != NULL); + EIO_ASSERT(nr_bvecs > 0); + + where.bdev = dmc->cache_dev->bdev; + where.sector = + (i << dmc->block_shift) + dmc->md_sectors; + where.count = total * dmc->block_size; + + SECTOR_STATS(dmc->eio_stats.ssd_reads, + to_bytes(where.count)); + down_read(&sioc.sio_lock); + error = + eio_io_async_bvec(dmc, &where, READ, bvecs, + nr_bvecs, eio_sync_io_callback, + &sioc, 0); + if (error) { + sioc.sio_error = error; + up_read(&sioc.sio_lock); + } + + bvecs = NULL; + i = j; + } + } + /* + * In above for loop, submit all READ I/Os to SSD + * and unplug the device for immediate submission to + * underlying device driver. + */ + eio_unplug_cache_device(dmc); + + /* wait for all I/Os to complete and release sync lock */ + down_write(&sioc.sio_lock); + up_write(&sioc.sio_lock); + + error = sioc.sio_error; + if (error) + goto err_out3; + + /* 5. write to hdd */ + /* + * While writing the data to HDD, explicitly enable + * BIO_RW_SYNC flag to hint higher priority for these + * I/Os. + */ + for (i = start_index; i < end_index; i++) { + if (EIO_CACHE_STATE_GET(dmc, i) == CLEAN_INPROG) { + + blkindex = (i - start_index); + total = 1; + + bvecs = + setup_bio_vecs(dmc->clean_dbvecs, blkindex, + dmc->block_size, total, &nr_bvecs); + EIO_ASSERT(bvecs != NULL); + EIO_ASSERT(nr_bvecs > 0); + + where.bdev = dmc->disk_dev->bdev; + where.sector = EIO_DBN_GET(dmc, i); + where.count = dmc->block_size; + + SECTOR_STATS(dmc->eio_stats.disk_writes, + to_bytes(where.count)); + down_read(&sioc.sio_lock); + error = eio_io_async_bvec(dmc, &where, WRITE | REQ_SYNC, + bvecs, nr_bvecs, + eio_sync_io_callback, &sioc, + 1); + + if (error) { + sioc.sio_error = error; + up_read(&sioc.sio_lock); + } + bvecs = NULL; + } + } + + /* wait for all I/Os to complete and release sync lock */ + down_write(&sioc.sio_lock); + up_write(&sioc.sio_lock); + + error = sioc.sio_error; + if (error) + goto err_out3; + + /* 6. update on-disk cache metadata */ + + /* TBD. Do we have to consider sector alignment here ? */ + + /* + * md_size = dmc->assoc * sizeof(struct flash_cacheblock); + * Currently, md_size is 8192 bytes, mdpage_count is 2 pages maximum. + */ + + EIO_ASSERT(dmc->mdpage_count <= 2); + for (k = 0; k < dmc->mdpage_count; k++) + pg_virt_addr[k] = kmap(dmc->clean_mdpages[k]); + + alloc_size = dmc->assoc * sizeof(struct flash_cacheblock); + pindex = 0; + md_blocks = (struct flash_cacheblock *)pg_virt_addr[pindex]; + k = MD_BLOCKS_PER_PAGE; + + for (i = start_index; i < end_index; i++) { + + md_blocks->dbn = cpu_to_le64(EIO_DBN_GET(dmc, i)); + + if (EIO_CACHE_STATE_GET(dmc, i) == CLEAN_INPROG) + md_blocks->cache_state = cpu_to_le64(INVALID); + else if (EIO_CACHE_STATE_GET(dmc, i) == ALREADY_DIRTY) + md_blocks->cache_state = cpu_to_le64((VALID | DIRTY)); + else + md_blocks->cache_state = cpu_to_le64(INVALID); + + /* This was missing earlier. */ + md_blocks++; + k--; + + if (k == 0) { + md_blocks = + (struct flash_cacheblock *)pg_virt_addr[++pindex]; + k = MD_BLOCKS_PER_PAGE; + } + } + + for (k = 0; k < dmc->mdpage_count; k++) + kunmap(dmc->clean_mdpages[k]); + + where.bdev = dmc->cache_dev->bdev; + where.sector = dmc->md_start_sect + INDEX_TO_MD_SECTOR(start_index); + where.count = eio_to_sector(alloc_size); + error = + eio_io_sync_pages(dmc, &where, WRITE, dmc->clean_mdpages, + dmc->mdpage_count); + + if (error) + goto err_out3; + +err_out3: + + /* + * 7. update in-core cache metadata for clean_inprog blocks. + * If there was an error, set them back to ALREADY_DIRTY + * If no error, set them to VALID + */ + for (i = start_index; i < end_index; i++) { + if (EIO_CACHE_STATE_GET(dmc, i) == CLEAN_INPROG) { + if (error) + EIO_CACHE_STATE_SET(dmc, i, ALREADY_DIRTY); + else { + EIO_CACHE_STATE_SET(dmc, i, VALID); + EIO_ASSERT(dmc->cache_sets[set].nr_dirty > 0); + dmc->cache_sets[set].nr_dirty--; + atomic64_dec(&dmc->nr_dirty); + } + } + } + +err_out2: + + up_write(&dmc->cache_sets[set].rw_lock); + +err_out1: + + /* Reset clean flags on the set */ + + if (!force) { + spin_lock_irqsave(&dmc->cache_sets[set].cs_lock, flags); + dmc->cache_sets[set].flags &= + ~(SETFLAG_CLEAN_INPROG | SETFLAG_CLEAN_WHOLE); + spin_unlock_irqrestore(&dmc->cache_sets[set].cs_lock, flags); + } + + if (dmc->cache_sets[set].nr_dirty) + /* + * Lru touch the set, so that it can be picked + * up for whole set clean by clean thread later + */ + eio_touch_set_lru(dmc, set); + + return; +} + +/* + * Enqueues the dirty sets for clean, which had got dirtied long + * time back(aged). User tunable values to determine if a set has aged + */ +void eio_clean_aged_sets(struct work_struct *work) +{ + struct cache_c *dmc; + unsigned long flags = 0; + index_t set_index; + u_int64_t set_time; + u_int64_t cur_time; + + dmc = container_of(work, struct cache_c, clean_aged_sets_work.work); + + /* + * In FAILED state, dont schedule cleaning of sets. + */ + if (unlikely(CACHE_FAILED_IS_SET(dmc))) { + pr_debug("clean_aged_sets: Cache \"%s\" is in failed mode.\n", + dmc->cache_name); + /* + * This is to make sure that this thread is rescheduled + * once CACHE is ACTIVE again. + */ + spin_lock_irqsave(&dmc->dirty_set_lru_lock, flags); + dmc->is_clean_aged_sets_sched = 0; + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, flags); + + return; + } + + cur_time = jiffies; + + /* Use the set LRU list to pick up the most aged sets. */ + spin_lock_irqsave(&dmc->dirty_set_lru_lock, flags); + do { + lru_read_head(dmc->dirty_set_lru, &set_index, &set_time); + if (set_index == LRU_NULL) + break; + + if ((EIO_DIV((cur_time - set_time), HZ)) < + (dmc->sysctl_active.time_based_clean_interval * 60)) + break; + lru_rem(dmc->dirty_set_lru, set_index); + + if (dmc->cache_sets[set_index].nr_dirty > 0) { + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, flags); + eio_addto_cleanq(dmc, set_index, 1); + spin_lock_irqsave(&dmc->dirty_set_lru_lock, flags); + } + } while (1); + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, flags); + + /* Re-schedule the aged set clean, unless the clean has to stop now */ + + if (dmc->sysctl_active.time_based_clean_interval == 0) + goto out; + + schedule_delayed_work(&dmc->clean_aged_sets_work, + dmc->sysctl_active.time_based_clean_interval * + 60 * HZ); +out: + return; +} + +/* Move the given set at the head of the set LRU list */ +void eio_touch_set_lru(struct cache_c *dmc, index_t set) +{ + u_int64_t systime; + unsigned long flags; + + systime = jiffies; + spin_lock_irqsave(&dmc->dirty_set_lru_lock, flags); + lru_touch(dmc->dirty_set_lru, set, systime); + + if ((dmc->sysctl_active.time_based_clean_interval > 0) && + (dmc->is_clean_aged_sets_sched == 0)) { + schedule_delayed_work(&dmc->clean_aged_sets_work, + dmc->sysctl_active. + time_based_clean_interval * 60 * HZ); + dmc->is_clean_aged_sets_sched = 1; + } + + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, flags); +} diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_mem.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_mem.c --- linux-3.10.30/drivers/block/enhanceio/eio_mem.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_mem.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,236 @@ +/* + * eio_mem.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "eio.h" + +#define SECTORS_PER_SET (dmc->assoc * dmc->block_size) +#define SECTORS_PER_SET_SHIFT (dmc->consecutive_shift + dmc->block_shift) +#define SECTORS_PER_SET_MASK (SECTORS_PER_SET - 1) + +#define EIO_DBN_TO_SET(dmc, dbn, set_number, wrapped) do { \ + u_int64_t value; \ + u_int64_t mid_i; \ + value = (dbn) >> SECTORS_PER_SET_SHIFT; \ + mid_i = (value) & (dmc)->num_sets_mask; \ + if (mid_i >= (dmc)->num_sets) { \ + (wrapped) = 1; \ + (set_number) = mid_i - (dmc)->num_sets; \ + } else { \ + (wrapped) = 0; \ + (set_number) = mid_i; \ + } \ +} while (0) + +/* + * eio_mem_init + */ +int eio_mem_init(struct cache_c *dmc) +{ + u_int32_t lsb_bits; + u_int32_t msb_bits_24; /* most significant bits in shrunk dbn */ + u_int64_t max_dbn; + u_int64_t num_sets_64; + + /* + * Sanity check the number of sets. + */ + num_sets_64 = EIO_DIV(dmc->size, dmc->assoc); + if (num_sets_64 > UINT_MAX) { + pr_err("Number of cache sets (%lu) greater than maximum" \ + "allowed (%u)", + (long unsigned int)num_sets_64, UINT_MAX); + return -1; + } + + /* + * Find the number of bits required to encode the set number and + * its corresponding mask value. + */ + dmc->num_sets = (u_int32_t)num_sets_64; + for (dmc->num_sets_bits = 0; (dmc->num_sets >> dmc->num_sets_bits); + dmc->num_sets_bits++); + + dmc->num_sets_mask = ULLONG_MAX >> (64 - dmc->num_sets_bits); + + /* + * If we don't have at least 16 bits to save, + * we can't use small metadata. + */ + if (dmc->num_sets_bits < 16) { + dmc->cache_flags |= CACHE_FLAGS_MD8; + pr_info("Not enough sets to use small metadata"); + return 1; + } + + /* + * Now compute the largest sector number that we can shrink; then see + * if the source volume is smaller. + */ + lsb_bits = dmc->consecutive_shift + dmc->block_shift; + msb_bits_24 = 24 - 1 - lsb_bits; /* 1 for wrapped bit */ + max_dbn = + ((u_int64_t)1) << (msb_bits_24 + dmc->num_sets_bits + lsb_bits); + if (eio_to_sector(eio_get_device_size(dmc->disk_dev)) > max_dbn) { + dmc->cache_flags |= CACHE_FLAGS_MD8; + pr_info("Source volume too big to use small metadata"); + return 1; + } + + return 0; +} + +/* + * eio_hash_block + */ +u_int32_t eio_hash_block(struct cache_c *dmc, sector_t dbn) +{ + int wrapped; + u_int64_t set_number; + + EIO_DBN_TO_SET(dmc, dbn, set_number, wrapped); + EIO_ASSERT(set_number < dmc->num_sets); + + return (u_int32_t)set_number; +} + +/* + * eio_shrink_dbn + * + * Shrink a 5-byte "dbn" into a 3-byte "dbn" by eliminating 16 lower bits + * of the set number this "dbn" belongs to. + */ +unsigned int eio_shrink_dbn(struct cache_c *dmc, sector_t dbn) +{ + u_int32_t dbn_24; + sector_t lsb; + sector_t wrapped; + sector_t msb; + sector_t set_number; + + EIO_ASSERT(!EIO_MD8(dmc)); + if (unlikely(dbn == 0)) + return 0; + + lsb = dbn & SECTORS_PER_SET_MASK; + EIO_DBN_TO_SET(dmc, dbn, set_number, wrapped); + msb = dbn >> (dmc->num_sets_bits + SECTORS_PER_SET_SHIFT); + dbn_24 = + (unsigned int)(lsb | (wrapped << SECTORS_PER_SET_SHIFT) | + (msb << (SECTORS_PER_SET_SHIFT + 1))); + + return dbn_24; +} + +/* + * eio_expand_dbn + * + * Expand a 3-byte "dbn" into a 5-byte "dbn" by adding 16 lower bits + * of the set number this "dbn" belongs to. + */ +sector_t eio_expand_dbn(struct cache_c *dmc, u_int64_t index) +{ + u_int32_t dbn_24; + u_int64_t set_number; + sector_t lsb; + sector_t msb; + sector_t dbn_40; + + EIO_ASSERT(!EIO_MD8(dmc)); + /* + * Expanding "dbn" zero? + */ + if (index == dmc->index_zero && + dmc->index_zero < (u_int64_t)dmc->assoc) + return 0; + + dbn_24 = dmc->cache[index].md4_u.u_i_md4 & EIO_MD4_DBN_MASK; + if (dbn_24 == 0 && EIO_CACHE_STATE_GET(dmc, index) == INVALID) + return (sector_t)0; + + set_number = EIO_DIV(index, dmc->assoc); + lsb = dbn_24 & SECTORS_PER_SET_MASK; + msb = dbn_24 >> (SECTORS_PER_SET_SHIFT + 1); /* 1 for wrapped */ + /* had we wrapped? */ + if ((dbn_24 & SECTORS_PER_SET) != 0) { + dbn_40 = msb << (dmc->num_sets_bits + SECTORS_PER_SET_SHIFT); + dbn_40 |= (set_number + dmc->num_sets) << SECTORS_PER_SET_SHIFT; + dbn_40 |= lsb; + } else { + dbn_40 = msb << (dmc->num_sets_bits + SECTORS_PER_SET_SHIFT); + dbn_40 |= set_number << SECTORS_PER_SET_SHIFT; + dbn_40 |= lsb; + } + EIO_ASSERT(unlikely(dbn_40 < EIO_MAX_SECTOR)); + + return (sector_t)dbn_40; +} +EXPORT_SYMBOL(eio_expand_dbn); + +/* + * eio_invalidate_md + */ +void eio_invalidate_md(struct cache_c *dmc, u_int64_t index) +{ + + if (EIO_MD8(dmc)) + dmc->cache_md8[index].md8_u.u_i_md8 = EIO_MD8_INVALID; + else + dmc->cache[index].md4_u.u_i_md4 = EIO_MD4_INVALID; +} + +/* + * eio_md4_dbn_set + */ +void eio_md4_dbn_set(struct cache_c *dmc, u_int64_t index, u_int32_t dbn_24) +{ + + EIO_ASSERT((dbn_24 & ~EIO_MD4_DBN_MASK) == 0); + + /* retain "cache_state" */ + dmc->cache[index].md4_u.u_i_md4 &= ~EIO_MD4_DBN_MASK; + dmc->cache[index].md4_u.u_i_md4 |= dbn_24; + + /* XXX excessive debugging */ + if (dmc->index_zero < (u_int64_t)dmc->assoc && /* sector 0 cached */ + index == dmc->index_zero && /* we're accessing sector 0 */ + dbn_24 != 0) /* we're replacing sector 0 */ + dmc->index_zero = dmc->assoc; +} + +/* + * eio_md8_dbn_set + */ +void eio_md8_dbn_set(struct cache_c *dmc, u_int64_t index, sector_t dbn) +{ + + EIO_ASSERT((dbn & ~EIO_MD8_DBN_MASK) == 0); + + /* retain "cache_state" */ + dmc->cache_md8[index].md8_u.u_i_md8 &= ~EIO_MD8_DBN_MASK; + dmc->cache_md8[index].md8_u.u_i_md8 |= dbn; + + /* XXX excessive debugging */ + if (dmc->index_zero < (u_int64_t)dmc->assoc && /* sector 0 cached */ + index == dmc->index_zero && /* we're accessing sector 0 */ + dbn != 0) /* we're replacing sector 0 */ + dmc->index_zero = dmc->assoc; +} diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_policy.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_policy.c --- linux-3.10.30/drivers/block/enhanceio/eio_policy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_policy.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,146 @@ +/* + * eio_policy.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "eio.h" + +LIST_HEAD(eio_policy_list); + +int eio_register_policy(struct eio_policy_header *new_policy) +{ + struct list_head *ptr; + struct eio_policy_header *curr; + + list_for_each(ptr, &eio_policy_list) { + curr = list_entry(ptr, struct eio_policy_header, sph_list); + if (curr->sph_name == new_policy->sph_name) + return 1; + } + list_add_tail(&new_policy->sph_list, &eio_policy_list); + + pr_info("register_policy: policy %d added", new_policy->sph_name); + + return 0; +} +EXPORT_SYMBOL(eio_register_policy); + +int eio_unregister_policy(struct eio_policy_header *p_ops) +{ + struct list_head *ptr; + struct eio_policy_header *curr; + + list_for_each(ptr, &eio_policy_list) { + curr = list_entry(ptr, struct eio_policy_header, sph_list); + if (curr->sph_name == p_ops->sph_name) { + list_del(&curr->sph_list); + pr_info("unregister_policy: policy %d removed", + (int)p_ops->sph_name); + return 0; + } + } + + return 1; +} +EXPORT_SYMBOL(eio_unregister_policy); + +struct eio_policy *eio_get_policy(int policy) +{ + struct list_head *ptr; + struct eio_policy_header *curr; + + list_for_each(ptr, &eio_policy_list) { + curr = list_entry(ptr, struct eio_policy_header, sph_list); + if (curr->sph_name == policy) { + pr_info("get_policy: policy %d found", policy); + return curr->sph_instance_init(); + } + } + pr_info("get_policy: cannot find policy %d", policy); + + return NULL; +} + +/* + * Decrement the reference count of the policy specific module + * and any other cleanup that is required when an instance of a + * policy is no longer required. + */ +void eio_put_policy(struct eio_policy *p_ops) +{ + + if (p_ops == NULL) { + pr_err("put_policy: Cannot decrement reference" \ + "count of NULL policy"); + return; + } + p_ops->sp_repl_exit(); +} + +/* + * Wrappers for policy specific functions. These default to nothing if the + * default policy is being used. + */ +int eio_repl_sets_init(struct eio_policy *p_ops) +{ + + return (p_ops && + p_ops->sp_repl_sets_init) ? p_ops->sp_repl_sets_init(p_ops) : 0; +} + +int eio_repl_blk_init(struct eio_policy *p_ops) +{ + + return (p_ops && + p_ops->sp_repl_blk_init) ? p_ops->sp_repl_blk_init(p_ops) : 0; +} + +void +eio_find_reclaim_dbn(struct eio_policy *p_ops, + index_t start_index, index_t *index) +{ + + p_ops->sp_find_reclaim_dbn(p_ops, start_index, index); +} + +int eio_policy_clean_set(struct eio_policy *p_ops, index_t set, int to_clean) +{ + + return p_ops->sp_clean_set(p_ops, set, to_clean); +} + +/* + * LRU Specific functions + */ +void eio_policy_lru_pushblks(struct eio_policy *p_ops) +{ + + if (p_ops && p_ops->sp_name == CACHE_REPL_LRU) + p_ops->sp_policy.lru->sl_lru_pushblks(p_ops); +} + +void +eio_policy_reclaim_lru_movetail(struct cache_c *dmc, index_t i, + struct eio_policy *p_ops) +{ + + if (p_ops && p_ops->sp_name == CACHE_REPL_LRU) + p_ops->sp_policy.lru->sl_reclaim_lru_movetail(dmc, i, p_ops); +} diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_policy.h linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_policy.h --- linux-3.10.30/drivers/block/enhanceio/eio_policy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_policy.h 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,105 @@ +/* + * eio_policy.h + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef EIO_POLICY_H +#define EIO_POLICY_H + +#include +#include + +/* + * Defines for policy types (EIO_REPL_XXX are in eio.h + * so that user space utilties can use those definitions. + */ + +/* + * The LRU pointers are maintained as set-relative offsets, instead of + * pointers. This enables us to store the LRU pointers per cacheblock + * using 4 bytes instead of 16 bytes. The upshot of this is that we + * are required to clamp the associativity at an 8K max. + * + * XXX - The above comment is from the original code. Looks like an error, + * maximum associativity should be 32K (2^15) and not 8K. + */ +#define EIO_MAX_ASSOC 8192 +#define EIO_LRU_NULL 0xFFFF + +/* Declerations to keep the compiler happy */ +struct cache_c; +struct eio_policy; +struct eio_lru; + +/* LRU specific data structures and functions */ +struct eio_lru { + void (*sl_lru_pushblks)(struct eio_policy *); + void (*sl_reclaim_lru_movetail)(struct cache_c *, index_t, + struct eio_policy *); +}; + +/* Function prototypes for LRU wrappers in eio_policy.c */ +void eio_policy_lru_pushblks(struct eio_policy *); +void eio_policy_reclaim_lru_movetail(struct cache_c *, index_t, + struct eio_policy *); + +/* + * Context that captures the cache block replacement policy. + * There is one instance of this struct per dmc (cache) + */ +struct eio_policy { + int sp_name; + union { + struct eio_lru *lru; + } sp_policy; + int (*sp_repl_init)(struct cache_c *); + void (*sp_repl_exit)(void); + int (*sp_repl_sets_init)(struct eio_policy *); + int (*sp_repl_blk_init)(struct eio_policy *); + void (*sp_find_reclaim_dbn)(struct eio_policy *, + index_t start_index, index_t *index); + int (*sp_clean_set)(struct eio_policy *, index_t set, int); + struct cache_c *sp_dmc; +}; + +/* + * List of registered policies. There is one instance + * of this structure per policy type. + */ +struct eio_policy_header { + int sph_name; + struct eio_policy *(*sph_instance_init)(void); + struct list_head sph_list; +}; + +/* Prototypes of generic functions in eio_policy */ +int *eio_repl_init(struct cache_c *); +int eio_repl_sets_init(struct eio_policy *); +int eio_repl_blk_init(struct eio_policy *); +void eio_find_reclaim_dbn(struct eio_policy *, index_t start_index, + index_t *index); +int eio_policy_clean_set(struct eio_policy *, index_t, int); + +int eio_register_policy(struct eio_policy_header *); +int eio_unregister_policy(struct eio_policy_header *); +struct eio_policy *eio_get_policy(int); +void eio_put_policy(struct eio_policy *); + +#endif /* EIO_POLICY_H */ diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_procfs.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_procfs.c --- linux-3.10.30/drivers/block/enhanceio/eio_procfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_procfs.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,1932 @@ +/* + * eio_procfs.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "eio.h" +#define EIO_RELEASE "ENHANCEIO" + +#ifndef ENHANCEIO_GIT_COMMIT_HASH +#define ENHANCEIO_GIT_COMMIT_HASH "unknown-git-version" +#endif /* !ENHANCEIO_GIT_COMMIT_HASH */ + +int eio_version_query(size_t buf_sz, char *bufp) +{ + if (unlikely(buf_sz == 0) || unlikely(bufp == NULL)) + return -EINVAL; + snprintf(bufp, buf_sz, "EnhanceIO Version: %s %s (checksum disabled)", + EIO_RELEASE, ENHANCEIO_GIT_COMMIT_HASH); + + bufp[buf_sz - 1] = '\0'; + + return 0; +} + +static struct sysctl_table_dir *sysctl_handle_dir; + +/* + * eio_zerostats_sysctl + */ +static int +eio_zerostats_sysctl(ctl_table *table, int write, void __user *buffer, + size_t *length, loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + long long cached_blocks; + unsigned long flags = 0; + + /* fetch the new tunable value or post the existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.zerostats = dmc->sysctl_active.zerostats; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + /* do sanity check */ + + if ((dmc->sysctl_pending.zerostats != 0) && + (dmc->sysctl_pending.zerostats != 1)) { + pr_err + ("0 or 1 are the only valid values for zerostats"); + return -EINVAL; + } + + if (dmc->sysctl_pending.zerostats == + dmc->sysctl_active.zerostats) + /* same value. Nothing to work */ + return 0; + + /* Copy to active */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.zerostats = dmc->sysctl_pending.zerostats; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + if (dmc->sysctl_active.zerostats) { + /* + * The number of cached blocks should not be zero'd since + * these blocks are already on cache dev. Making this zero + * may lead to -ve count during block invalidate, and also, + * incorrectly indicating how much data is cached. + * + * TODO - should have used an spinlock, but existing spinlocks + * are inadequate to fully protect this + */ + + cached_blocks = + atomic64_read(&dmc->eio_stats.cached_blocks); + memset(&dmc->eio_stats, 0, sizeof(struct eio_stats)); + atomic64_set(&dmc->eio_stats.cached_blocks, + cached_blocks); + } + } + + return 0; +} + +/* + * eio_mem_limit_pct_sysctl + * - sets the eio sysctl mem_limit_pct value + */ +static int +eio_mem_limit_pct_sysctl(ctl_table *table, int write, void __user *buffer, + size_t *length, loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value or post the existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.mem_limit_pct = + dmc->sysctl_active.mem_limit_pct; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + /* do sanity check */ + if ((dmc->sysctl_pending.mem_limit_pct < 0) || + (dmc->sysctl_pending.mem_limit_pct > 100)) { + pr_err + ("only valid percents are [0 - 100] for mem_limit_pct"); + return -EINVAL; + } + + if (dmc->sysctl_pending.mem_limit_pct == + dmc->sysctl_active.mem_limit_pct) + /* same value. Nothing more to do */ + return 0; + + /* Copy to active */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.mem_limit_pct = + dmc->sysctl_pending.mem_limit_pct; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + return 0; +} + +/* + * eio_clean_sysctl + */ +static int +eio_clean_sysctl(ctl_table *table, int write, void __user *buffer, + size_t *length, loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value or post the existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.do_clean = dmc->sysctl_active.do_clean; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + /* Do sanity check */ + + if (dmc->mode != CACHE_MODE_WB) { + /* do_clean is only valid for writeback cache */ + pr_err("do_clean is only valid for writeback cache"); + return -EINVAL; + } + + if (dmc->sysctl_pending. + do_clean & ~(EIO_CLEAN_START | EIO_CLEAN_KEEP)) { + pr_err + ("do_clean should be either clean start/clean keep"); + return -EINVAL; + } + + if (dmc->sysctl_pending.do_clean == dmc->sysctl_active.do_clean) + /* New and old values are same. No work required */ + return 0; + + /* Copy to active and apply the new tunable value */ + + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + + if (dmc->cache_flags & CACHE_FLAGS_MOD_INPROG) { + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + pr_err + ("do_clean called while cache modification in progress"); + return -EBUSY; + } else { + dmc->sysctl_active.do_clean = + dmc->sysctl_pending.do_clean; + + if (dmc->sysctl_active.do_clean) { + atomic_set(&dmc->clean_index, 0); + dmc->sysctl_active.do_clean |= EIO_CLEAN_START; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + flags); + + /* + * Wake up the clean thread. + * Sync thread will do the clean and once complete + * will reset the clean_start flag. + * The clean_keep flag will remain set(unless reset + * by user) and will prevent new I/Os from making + * the blocks dirty. + */ + + spin_lock_irqsave(&dmc->clean_sl, flags); + EIO_SET_EVENT_AND_UNLOCK(&dmc->clean_event, + &dmc->clean_sl, flags); + } else + spin_unlock_irqrestore(&dmc->cache_spin_lock, + flags); + } + } + + return 0; +} + +/* + * eio_dirty_high_threshold_sysctl + */ +static int +eio_dirty_high_threshold_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *length, + loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value or post the existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.dirty_high_threshold = + dmc->sysctl_active.dirty_high_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + int error; + uint32_t old_value; + + /* do sanity check */ + + if (dmc->mode != CACHE_MODE_WB) { + pr_err + ("dirty_high_threshold is only valid for writeback cache"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_high_threshold > 100) { + pr_err + ("dirty_high_threshold percentage should be [0 - 100]"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_high_threshold < + dmc->sysctl_active.dirty_low_threshold) { + pr_err + ("dirty high shouldn't be less than dirty low threshold"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_high_threshold == + dmc->sysctl_active.dirty_high_threshold) + /* new is same as old value. No need to take any action */ + return 0; + + /* update the active value with the new tunable value */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + old_value = dmc->sysctl_active.dirty_high_threshold; + dmc->sysctl_active.dirty_high_threshold = + dmc->sysctl_pending.dirty_high_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + /* Store the change persistently */ + error = eio_sb_store(dmc); + if (error) { + /* restore back the old value and return error */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.dirty_high_threshold = old_value; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + return error; + } + + /* if we reduced the high threshold, check if we require cache cleaning */ + if (old_value > dmc->sysctl_active.dirty_high_threshold) + eio_comply_dirty_thresholds(dmc, -1); + } + + return 0; +} + +/* + * eio_dirty_low_threshold_sysctl + */ +static int +eio_dirty_low_threshold_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *length, + loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value or post the existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.dirty_low_threshold = + dmc->sysctl_active.dirty_low_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + int error; + uint32_t old_value; + + /* do sanity check */ + + if (dmc->mode != CACHE_MODE_WB) { + pr_err + ("dirty_low_threshold is valid for only writeback cache"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_low_threshold > 100) { + pr_err + ("dirty_low_threshold percentage should be [0 - 100]"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_low_threshold > + dmc->sysctl_active.dirty_high_threshold) { + pr_err + ("dirty low shouldn't be more than dirty high threshold"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_low_threshold == + dmc->sysctl_active.dirty_low_threshold) + /* new is same as old value. No need to take any action */ + return 0; + + /* update the active value with the new tunable value */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + old_value = dmc->sysctl_active.dirty_low_threshold; + dmc->sysctl_active.dirty_low_threshold = + dmc->sysctl_pending.dirty_low_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + /* Store the change persistently */ + error = eio_sb_store(dmc); + if (error) { + /* restore back the old value and return error */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.dirty_low_threshold = old_value; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + return error; + } + + if (old_value > dmc->sysctl_active.dirty_low_threshold) + /* + * Although the low threshold set shouldn't trigger new cleans, + * but because we set the tunables one at a time from user mode, + * it is possible that the high threshold value triggering clean + * did not happen and should get triggered now that the low value + * has been changed, so we are calling the comply function here + */ + eio_comply_dirty_thresholds(dmc, -1); + } + + return 0; +} + +/* + * eio_dirty_set_high_threshold_sysctl + */ +static int +eio_dirty_set_high_threshold_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *length, + loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value or post the existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.dirty_set_high_threshold = + dmc->sysctl_active.dirty_set_high_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + int error; + uint32_t old_value; + u_int64_t i; + + /* do sanity check */ + + if (dmc->mode != CACHE_MODE_WB) { + pr_err + ("dirty_set_high_threshold is valid only for writeback cache"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_set_high_threshold > 100) { + pr_err + ("dirty_set_high_threshold percentage should be [0 - 100]"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_set_high_threshold < + dmc->sysctl_active.dirty_set_low_threshold) { + pr_err + ("dirty_set_high_threshold shouldn't be less than dirty low threshold"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_set_high_threshold == + dmc->sysctl_active.dirty_set_high_threshold) + /* new is same as old value. No need to take any action */ + return 0; + + /* update the active value with the new tunable value */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + old_value = dmc->sysctl_active.dirty_set_high_threshold; + dmc->sysctl_active.dirty_set_high_threshold = + dmc->sysctl_pending.dirty_set_high_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + /* Store the change persistently */ + error = eio_sb_store(dmc); + if (error) { + /* restore back the old value and return error */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.dirty_set_high_threshold = old_value; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + return error; + } + + if (old_value > dmc->sysctl_active.dirty_set_high_threshold) { + /* Check each set for dirty blocks cleaning */ + for (i = 0; i < (dmc->size >> dmc->consecutive_shift); + i++) + eio_comply_dirty_thresholds(dmc, i); + } + } + + return 0; +} + +/* + * eio_dirty_set_low_threshold_sysctl + */ +static int +eio_dirty_set_low_threshold_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *length, + loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value or post the existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.dirty_set_low_threshold = + dmc->sysctl_active.dirty_set_low_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + int error; + uint32_t old_value; + u_int64_t i; + + /* do sanity check */ + + if (dmc->mode != CACHE_MODE_WB) { + pr_err + ("dirty_set_low_threshold is valid only for writeback cache"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_set_low_threshold > 100) { + pr_err + ("dirty_set_low_threshold percentage should be [0 - 100]"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_set_low_threshold > + dmc->sysctl_active.dirty_set_high_threshold) { + pr_err + ("dirty_set_low_threshold shouldn't be more than dirty_set_high_threshold"); + return -EINVAL; + } + + if (dmc->sysctl_pending.dirty_set_low_threshold == + dmc->sysctl_active.dirty_set_low_threshold) + /* new is same as old value. No need to take any action */ + return 0; + + /* update the active value with the new tunable value */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + old_value = dmc->sysctl_active.dirty_set_low_threshold; + dmc->sysctl_active.dirty_set_low_threshold = + dmc->sysctl_pending.dirty_set_low_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + /* Store the change persistently */ + error = eio_sb_store(dmc); + if (error) { + /* restore back the old value and return error */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.dirty_set_low_threshold = old_value; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + return error; + } + + /* + * Although the low threshold value shouldn't trigger new cleans, + * but because we set the tunables one at a time from user mode, + * it is possible that the high threshold value triggering clean + * did not happen and should get triggered now that the low value + * has been changed, so we are calling the comply function again + */ + if (old_value > dmc->sysctl_active.dirty_set_low_threshold) { + /* Check each set for dirty blocks cleaning */ + for (i = 0; i < (dmc->size >> dmc->consecutive_shift); + i++) + eio_comply_dirty_thresholds(dmc, i); + } + } + + return 0; +} + +/* + * eio_autoclean_threshold_sysctl + */ +static int +eio_autoclean_threshold_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *length, + loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value or post existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.autoclean_threshold = + dmc->sysctl_active.autoclean_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + int error; + int old_value; + + /* do sanity check */ + + if (dmc->mode != CACHE_MODE_WB) { + pr_err + ("autoclean_threshold is valid only for writeback cache"); + return -EINVAL; + } + + if ((dmc->sysctl_pending.autoclean_threshold < 0) || + (dmc->sysctl_pending.autoclean_threshold > + AUTOCLEAN_THRESH_MAX)) { + pr_err("autoclean_threshold is valid range is 0 to %d", + AUTOCLEAN_THRESH_MAX); + return -EINVAL; + } + + if (dmc->sysctl_pending.autoclean_threshold == + dmc->sysctl_active.autoclean_threshold) + /* new is same as old value. No need to take any action */ + return 0; + + /* update the active value with the new tunable value */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + old_value = dmc->sysctl_active.autoclean_threshold; + dmc->sysctl_active.autoclean_threshold = + dmc->sysctl_pending.autoclean_threshold; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + /* Store the change persistently */ + error = eio_sb_store(dmc); + if (error) { + /* restore back the old value and return error */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.autoclean_threshold = old_value; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + return error; + } + + /* Ensure new thresholds are being complied */ + eio_comply_dirty_thresholds(dmc, -1); + } + + return 0; +} + +/* + * eio_time_based_clean_interval_sysctl + */ +static int +eio_time_based_clean_interval_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *length, + loff_t *ppos) +{ + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value or post existing value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.time_based_clean_interval = + dmc->sysctl_active.time_based_clean_interval; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + int error; + uint32_t old_value; + + /* do sanity check */ + + if (dmc->mode != CACHE_MODE_WB) { + pr_err + ("time_based_clean_interval is valid only for writeback cache"); + return -EINVAL; + } + + if (dmc->sysctl_pending.time_based_clean_interval > + TIME_BASED_CLEAN_INTERVAL_MAX) { + /* valid values are 0 to TIME_BASED_CLEAN_INTERVAL_MAX */ + pr_err + ("time_based_clean_interval valid range is 0 to %u", + TIME_BASED_CLEAN_INTERVAL_MAX); + return -EINVAL; + } + + if (dmc->sysctl_pending.time_based_clean_interval == + dmc->sysctl_active.time_based_clean_interval) + /* new is same as old value */ + return 0; + + /* update the active value with the new tunable value */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + old_value = dmc->sysctl_active.time_based_clean_interval; + dmc->sysctl_active.time_based_clean_interval = + dmc->sysctl_pending.time_based_clean_interval; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + /* Store the change persistently */ + error = eio_sb_store(dmc); + if (error) { + /* restore back the old value and return error */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.time_based_clean_interval = + old_value; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + return error; + } + + /* Reschedule the time based clean, based on new interval */ + cancel_delayed_work_sync(&dmc->clean_aged_sets_work); + spin_lock_irqsave(&dmc->dirty_set_lru_lock, flags); + dmc->is_clean_aged_sets_sched = 0; + if (dmc->sysctl_active.time_based_clean_interval + && atomic64_read(&dmc->nr_dirty)) { + schedule_delayed_work(&dmc->clean_aged_sets_work, + dmc->sysctl_active. + time_based_clean_interval * 60 * + HZ); + dmc->is_clean_aged_sets_sched = 1; + } + spin_unlock_irqrestore(&dmc->dirty_set_lru_lock, flags); + } + + return 0; +} + +static void eio_sysctl_register_writeback(struct cache_c *dmc); +static void eio_sysctl_unregister_writeback(struct cache_c *dmc); +static void eio_sysctl_register_invalidate(struct cache_c *dmc); +static void eio_sysctl_unregister_invalidate(struct cache_c *dmc); + +/* + * eio_control_sysctl + */ +int +eio_control_sysctl(ctl_table *table, int write, void __user *buffer, + size_t *length, loff_t *ppos) +{ + int rv = 0; + struct cache_c *dmc = (struct cache_c *)table->extra1; + unsigned long flags = 0; + + /* fetch the new tunable value */ + + if (!write) { + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_pending.control = dmc->sysctl_active.control; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + } + + proc_dointvec(table, write, buffer, length, ppos); + + /* do write processing */ + + if (write) { + /* do sanity check */ + + if (dmc->sysctl_pending.control > CACHE_CONTROL_FLAG_MAX || + dmc->sysctl_pending.control < 0) { + /* valid values are from 0 till CACHE_CONTROL_FLAG_MAX */ + pr_err("control valid values are from 0 till %d", + CACHE_CONTROL_FLAG_MAX); + return -EINVAL; + } + + if (dmc->sysctl_pending.control == dmc->sysctl_active.control) + /* new is same as old value. No work required */ + return 0; + + /* update the active value with the new tunable value */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.control = dmc->sysctl_pending.control; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + switch (dmc->sysctl_active.control) { + case CACHE_VERBOSE_OFF: + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_VERBOSE; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + pr_info("Turning off verbose mode"); + break; + case CACHE_VERBOSE_ON: + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags |= CACHE_FLAGS_VERBOSE; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + pr_info("Turning on verbose mode"); + break; + case CACHE_WRITEBACK_ON: + if (dmc->sysctl_handle_writeback == NULL) + eio_sysctl_register_writeback(dmc); + break; + case CACHE_WRITEBACK_OFF: + if (dmc->sysctl_handle_writeback) + eio_sysctl_unregister_writeback(dmc); + break; + case CACHE_INVALIDATE_ON: + if (dmc->sysctl_handle_invalidate == NULL) { + eio_sysctl_register_invalidate(dmc); + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags |= CACHE_FLAGS_INVALIDATE; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc-> + cache_spin_lock_flags); + } else + pr_info("Invalidate API already registered"); + break; + case CACHE_INVALIDATE_OFF: + if (dmc->sysctl_handle_invalidate) { + eio_sysctl_unregister_invalidate(dmc); + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_INVALIDATE; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc-> + cache_spin_lock_flags); + } else + pr_info("Invalidate API not registered"); + break; + case CACHE_FAST_REMOVE_ON: + if (dmc->mode != CACHE_MODE_WB) { + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags |= CACHE_FLAGS_FAST_REMOVE; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc-> + cache_spin_lock_flags); + if (CACHE_VERBOSE_IS_SET(dmc)) + pr_info("Turning on fast remove"); + } else { +#ifdef EIO_DEBUG + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags |= CACHE_FLAGS_FAST_REMOVE; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc-> + cache_spin_lock_flags); + if (CACHE_VERBOSE_IS_SET(dmc)) + pr_info("Turning on fast remove"); +#else + pr_err("Invalid control value: 0x%x", + dmc->sysctl_active.control); + rv = -1; +#endif /* EIO_DEBUG */ + } + break; + case CACHE_FAST_REMOVE_OFF: + if (dmc->mode != CACHE_MODE_WB) { + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_FAST_REMOVE; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc-> + cache_spin_lock_flags); + if (CACHE_VERBOSE_IS_SET(dmc)) + pr_info("Turning off fast remove"); + } else { +#ifdef EIO_DEBUG + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_FAST_REMOVE; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc-> + cache_spin_lock_flags); + if (CACHE_VERBOSE_IS_SET(dmc)) + pr_info("Turning off fast remove"); +#else + pr_err("Invalid control value: 0x%x", + dmc->sysctl_active.control); + rv = -1; +#endif /* EIO_DEBUG */ + } + break; + default: + pr_err("Invalid control value: 0x%x", + dmc->sysctl_active.control); + rv = -1; + } + } + + return rv; +} + +#define PROC_STR "enhanceio" +#define PROC_VER_STR "enhanceio/version" +#define PROC_STATS "stats" +#define PROC_ERRORS "errors" +#define PROC_IOSZ_HIST "io_hist" +#define PROC_CONFIG "config" + +static int eio_invalidate_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *length, + loff_t *ppos); +static void *eio_find_sysctl_data(struct cache_c *dmc, ctl_table *vars); +static char *eio_cons_sysctl_devname(struct cache_c *dmc); +static char *eio_cons_procfs_cachename(struct cache_c *dmc, + char *path_component); +static void eio_sysctl_register_common(struct cache_c *dmc); +static void eio_sysctl_unregister_common(struct cache_c *dmc); +static void eio_sysctl_register_dir(void); +static void eio_sysctl_unregister_dir(void); +static int eio_stats_show(struct seq_file *seq, void *v); +static int eio_stats_open(struct inode *inode, struct file *file); +static int eio_errors_show(struct seq_file *seq, void *v); +static int eio_errors_open(struct inode *inode, struct file *file); +static int eio_iosize_hist_show(struct seq_file *seq, void *v); +static int eio_iosize_hist_open(struct inode *inode, struct file *file); +static int eio_version_show(struct seq_file *seq, void *v); +static int eio_version_open(struct inode *inode, struct file *file); +static int eio_config_show(struct seq_file *seq, void *v); +static int eio_config_open(struct inode *inode, struct file *file); + +static const struct file_operations eio_version_operations = { + .open = eio_version_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations eio_stats_operations = { + .open = eio_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations eio_errors_operations = { + .open = eio_errors_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations eio_iosize_hist_operations = { + .open = eio_iosize_hist_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations eio_config_operations = { + .open = eio_config_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* + * Each ctl_table array needs to be 1 more than the actual number of + * entries - zero padded at the end ! Therefore the NUM_*_SYSCTLS + * is 1 more than then number of sysctls. + */ + +#define PROC_SYS_ROOT_NAME "dev" +#define PROC_SYS_DIR_NAME "enhanceio" +#define PROC_SYS_CACHE_NAME "enhanceio-dev" + +/* + * The purpose of sysctl_table_dir is to create the "enhanceio" + * dir under /proc/sys/dev/. The creation is done during module + * load time and the dir is removed when module is removed. + * + * This was added because otherwise, the first cache instance + * falsely assumes that /proc/sys/kernel/ is its parent instead + * of /proc/sys/dev leading to an incorrect number of reference + * count. When you have multiple cache instances, removing the + * last one results in the kernel's reference count to be 0 + * leading to a kernel warning at runtime. Hopefully, this will + * be fixed in the kernel sometime. + */ +static struct sysctl_table_dir { + struct ctl_table_header *sysctl_header; + ctl_table vars[0 + 1]; + ctl_table dev[0 + 1]; + ctl_table dir[1 + 1]; + ctl_table root[1 + 1]; +} sysctl_template_dir = { + .vars = { + }, .dev = { + }, .dir = { + { + .procname = PROC_SYS_DIR_NAME, + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = sysctl_template_dir.dev, + }, + }, .root = { + { + .procname = PROC_SYS_ROOT_NAME, + .maxlen = 0, + .mode = 0555, + .child = sysctl_template_dir.dir, + }, + }, +}; + +#define NUM_COMMON_SYSCTLS 3 + +static struct sysctl_table_common { + struct ctl_table_header *sysctl_header; + ctl_table vars[NUM_COMMON_SYSCTLS + 1]; + ctl_table dev[1 + 1]; + ctl_table dir[1 + 1]; + ctl_table root[1 + 1]; +} sysctl_template_common = { + .vars = { + { /* 1 */ + .procname = "zero_stats", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &eio_zerostats_sysctl, + }, { /* 2 */ + .procname = "mem_limit_pct", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &eio_mem_limit_pct_sysctl, + }, { /* 3 */ + .procname = "control", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &eio_control_sysctl, + }, + }, .dev = { + { + .procname = PROC_SYS_CACHE_NAME, + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = sysctl_template_common.vars, + }, + }, .dir = { + { + .procname = PROC_SYS_DIR_NAME, + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = sysctl_template_common.dev, + }, + }, .root = { + { + .procname = PROC_SYS_ROOT_NAME, + .maxlen = 0, + .mode = 0555, + .child = sysctl_template_common.dir, + }, + }, +}; + +#define NUM_WRITEBACK_SYSCTLS 7 + +static struct sysctl_table_writeback { + struct ctl_table_header *sysctl_header; + ctl_table vars[NUM_WRITEBACK_SYSCTLS + 1]; + ctl_table dev[1 + 1]; + ctl_table dir[1 + 1]; + ctl_table root[1 + 1]; +} sysctl_template_writeback = { + .vars = { + { /* 1 */ + .procname = "do_clean", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &eio_clean_sysctl, + }, { /* 2 */ + .procname = "time_based_clean_interval", + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &eio_time_based_clean_interval_sysctl, + }, { /* 3 */ + .procname = "autoclean_threshold", + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &eio_autoclean_threshold_sysctl, + }, { /* 4 */ + .procname = "dirty_high_threshold", + .maxlen = sizeof(uint32_t), + .mode = 0644, + .proc_handler = &eio_dirty_high_threshold_sysctl, + } + , { /* 5 */ + .procname = "dirty_low_threshold", + .maxlen = sizeof(uint32_t), + .mode = 0644, + .proc_handler = &eio_dirty_low_threshold_sysctl, + } + , { /* 6 */ + .procname = "dirty_set_high_threshold", + .maxlen = sizeof(uint32_t), + .mode = 0644, + .proc_handler = &eio_dirty_set_high_threshold_sysctl, + } + , { /* 7 */ + .procname = "dirty_set_low_threshold", + .maxlen = sizeof(uint32_t), + .mode = 0644, + .proc_handler = &eio_dirty_set_low_threshold_sysctl, + } + , + } + , .dev = { + { + .procname = PROC_SYS_CACHE_NAME, + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = sysctl_template_writeback.vars, + } + , + } + , .dir = { + { + .procname = PROC_SYS_DIR_NAME, + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = sysctl_template_writeback.dev, + } + , + } + , .root = { + { + .procname = PROC_SYS_ROOT_NAME, + .maxlen = 0, + .mode = 0555, + .child = sysctl_template_writeback.dir, + } + , + } + , +}; + +#define NUM_INVALIDATE_SYSCTLS (1) +static struct sysctl_table_invalidate { + struct ctl_table_header *sysctl_header; + ctl_table vars[NUM_INVALIDATE_SYSCTLS + 1]; + ctl_table dev[1 + 1]; + ctl_table dir[1 + 1]; + ctl_table root[1 + 1]; +} sysctl_template_invalidate = { + .vars = { + { /* 1 */ + .procname = "invalidate", + .maxlen = sizeof(u_int64_t), + .mode = 0644, + .proc_handler = &eio_invalidate_sysctl, + } + , + } + , .dev = { + { + .procname = PROC_SYS_CACHE_NAME, + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = sysctl_template_invalidate.vars, + } + , + } + , .dir = { + { + .procname = PROC_SYS_DIR_NAME, + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = sysctl_template_invalidate.dev, + } + , + } + , .root = { + { + .procname = PROC_SYS_ROOT_NAME, + .maxlen = 0, + .mode = 0555, + .child = sysctl_template_invalidate.dir, + } + , + } + , +}; + +/* + * eio_module_procfs_init -- called from "eio_init()" + */ +void eio_module_procfs_init(void) +{ + struct proc_dir_entry *entry; + + if (proc_mkdir(PROC_STR, NULL)) { + entry = proc_create_data(PROC_VER_STR, 0, NULL, + &eio_version_operations, NULL); + } + eio_sysctl_register_dir(); +} + +/* + * eio_module_procfs_exit -- called from "eio_exit()" + */ +void eio_module_procfs_exit(void) +{ + (void)remove_proc_entry(PROC_VER_STR, NULL); + (void)remove_proc_entry(PROC_STR, NULL); + + eio_sysctl_unregister_dir(); +} + +/* + * eio_procfs_ctr -- called from "eio_ctr()" + */ +void eio_procfs_ctr(struct cache_c *dmc) +{ + char *s; + struct proc_dir_entry *entry; + + s = eio_cons_procfs_cachename(dmc, ""); + entry = proc_mkdir(s, NULL); + kfree(s); + if (entry == NULL) { + pr_err("Failed to create /proc/%s", s); + return; + } + + s = eio_cons_procfs_cachename(dmc, PROC_STATS); + entry = proc_create_data(s, 0, NULL, &eio_stats_operations, dmc); + kfree(s); + + s = eio_cons_procfs_cachename(dmc, PROC_ERRORS); + entry = proc_create_data(s, 0, NULL, &eio_errors_operations, dmc); + kfree(s); + + s = eio_cons_procfs_cachename(dmc, PROC_IOSZ_HIST); + entry = proc_create_data(s, 0, NULL, &eio_iosize_hist_operations, dmc); + kfree(s); + + s = eio_cons_procfs_cachename(dmc, PROC_CONFIG); + entry = proc_create_data(s, 0, NULL, &eio_config_operations, dmc); + kfree(s); + + eio_sysctl_register_common(dmc); + if (dmc->mode == CACHE_MODE_WB) + eio_sysctl_register_writeback(dmc); + if (CACHE_INVALIDATE_IS_SET(dmc)) + eio_sysctl_register_invalidate(dmc); +} + +/* + * eio_procfs_dtr -- called from "eio_dtr()" + */ +void eio_procfs_dtr(struct cache_c *dmc) +{ + char *s; + + s = eio_cons_procfs_cachename(dmc, PROC_STATS); + remove_proc_entry(s, NULL); + kfree(s); + + s = eio_cons_procfs_cachename(dmc, PROC_ERRORS); + remove_proc_entry(s, NULL); + kfree(s); + + s = eio_cons_procfs_cachename(dmc, PROC_IOSZ_HIST); + remove_proc_entry(s, NULL); + kfree(s); + + s = eio_cons_procfs_cachename(dmc, PROC_CONFIG); + remove_proc_entry(s, NULL); + kfree(s); + + s = eio_cons_procfs_cachename(dmc, ""); + remove_proc_entry(s, NULL); + kfree(s); + + if (dmc->sysctl_handle_invalidate) + eio_sysctl_unregister_invalidate(dmc); + if (dmc->sysctl_handle_writeback) + eio_sysctl_unregister_writeback(dmc); + eio_sysctl_unregister_common(dmc); +} + +static spinlock_t invalidate_spin_lock; + +/* + * eio_invalidate_sysctl + */ +static int +eio_invalidate_sysctl(ctl_table *table, int write, void __user *buffer, + size_t *length, loff_t *ppos) +{ + static int have_sector; + static u_int64_t sector; + static u_int64_t num_sectors; + int rv; + unsigned long int flags; + struct cache_c *dmc; + + spin_lock_irqsave(&invalidate_spin_lock, flags); + + dmc = (struct cache_c *)table->extra1; + if (dmc == NULL) { + pr_err + ("Cannot invalidate due to unexpected NULL cache pointer"); + spin_unlock_irqrestore(&invalidate_spin_lock, flags); + return -EBUSY; + } + + table->extra1 = NULL; + proc_doulongvec_minmax(table, write, buffer, length, ppos); + table->extra1 = dmc; + + spin_unlock_irqrestore(&invalidate_spin_lock, flags); + + rv = 0; + + if (write) { + /* TBD. Need to put appropriate sanity checks */ + + /* update the active value with the new tunable value */ + spin_lock_irqsave(&dmc->cache_spin_lock, flags); + dmc->sysctl_active.invalidate = dmc->sysctl_pending.invalidate; + spin_unlock_irqrestore(&dmc->cache_spin_lock, flags); + + /* apply the new tunable value */ + + if (have_sector) { + num_sectors = dmc->sysctl_active.invalidate; + + rv = eio_invalidate_sanity_check(dmc, sector, + &num_sectors); + + /* Invalidate only if sanity passes and reset the return value. */ + if (rv == 0) + eio_inval_range(dmc, sector, + (unsigned) + to_bytes(num_sectors)); + + rv = 0; + have_sector = 0; + + } else { + sector = dmc->sysctl_active.invalidate; + have_sector = 1; + num_sectors = 0; + } + } + + if (CACHE_VERBOSE_IS_SET(dmc) && num_sectors) { + pr_info + ("eio_inval_range: Invalidated sector range from sector=%lu to sector=%lu", + (long unsigned int)sector, (long unsigned int)num_sectors); + } + + return rv; +} + +/* + * eio_find_sysctl_data + */ +static void *eio_find_sysctl_data(struct cache_c *dmc, ctl_table *vars) +{ + + if (strcmp(vars->procname, "do_clean") == 0) + return (void *)&dmc->sysctl_pending.do_clean; + if (strcmp(vars->procname, "time_based_clean_interval") == 0) + return (void *)&dmc->sysctl_pending.time_based_clean_interval; + if (strcmp(vars->procname, "dirty_high_threshold") == 0) + return (void *)&dmc->sysctl_pending.dirty_high_threshold; + if (strcmp(vars->procname, "dirty_low_threshold") == 0) + return (void *)&dmc->sysctl_pending.dirty_low_threshold; + if (strcmp(vars->procname, "dirty_set_high_threshold") == 0) + return (void *)&dmc->sysctl_pending.dirty_set_high_threshold; + if (strcmp(vars->procname, "dirty_set_low_threshold") == 0) + return (void *)&dmc->sysctl_pending.dirty_set_low_threshold; + if (strcmp(vars->procname, "autoclean_threshold") == 0) + return (void *)&dmc->sysctl_pending.autoclean_threshold; + if (strcmp(vars->procname, "zero_stats") == 0) + return (void *)&dmc->sysctl_pending.zerostats; + if (strcmp(vars->procname, "mem_limit_pct") == 0) + return (void *)&dmc->sysctl_pending.mem_limit_pct; + if (strcmp(vars->procname, "control") == 0) + return (void *)&dmc->sysctl_pending.control; + if (strcmp(vars->procname, "invalidate") == 0) + return (void *)&dmc->sysctl_pending.invalidate; + + pr_err("Cannot find sysctl data for %s", vars->procname); + return NULL; +} + +/* + * eio_cons_sysctl_devname + */ +static char *eio_cons_sysctl_devname(struct cache_c *dmc) +{ + char *pathname; + + if (dmc->cache_name[0]) { + pathname = kzalloc(strlen(dmc->cache_name) + 1, GFP_KERNEL); + if (pathname) + strcpy(pathname, dmc->cache_name); + else + pr_err("Failed to allocate memory"); + } else { + pr_err("Cache name is NULL"); + pathname = NULL; + } + + return pathname; +} + +/* + * eio_cons_procfs_cachename + */ +static char *eio_cons_procfs_cachename(struct cache_c *dmc, + char *path_component) +{ + char *pathname; + + if (dmc->cache_name[0]) { + pathname = + kzalloc(strlen(PROC_SYS_DIR_NAME) + 1 + + strlen(dmc->cache_name) + 1 + + strlen(path_component) + 1, GFP_KERNEL); + if (pathname) { + strcpy(pathname, PROC_SYS_DIR_NAME); + strcat(pathname, "/"); + strcat(pathname, dmc->cache_name); + if (strcmp(path_component, "") != 0) { + strcat(pathname, "/"); + strcat(pathname, path_component); + } + } else + pr_err("Failed to allocate memory"); + } else { + pr_err("Cache name is NULL"); + pathname = NULL; + } + + return pathname; +} + +static void eio_sysctl_register_dir(void) +{ + struct sysctl_table_dir *dir; + + dir = + kmemdup(&sysctl_template_dir, sizeof(sysctl_template_dir), + GFP_KERNEL); + if (unlikely(dir == NULL)) { + pr_err("Failed to allocate memory for dir sysctl"); + return; + } + + dir->dir[0].child = dir->dev; + dir->root[0].child = dir->dir; + dir->sysctl_header = register_sysctl_table(dir->root); + if (unlikely(dir->sysctl_header == NULL)) { + pr_err("Failed to register dir sysctl"); + goto out; + } + + sysctl_handle_dir = dir; + return; +out: + kfree(dir); +} + +static void eio_sysctl_unregister_dir(void) +{ + if (sysctl_handle_dir != NULL) { + unregister_sysctl_table(sysctl_handle_dir->sysctl_header); + kfree(sysctl_handle_dir); + sysctl_handle_dir = NULL; + } +} + +/* + * eio_sysctl_register_common + */ +static void eio_sysctl_register_common(struct cache_c *dmc) +{ + unsigned int i; + struct sysctl_table_common *common; + + common = + kmemdup(&sysctl_template_common, sizeof(sysctl_template_common), + GFP_KERNEL); + if (common == NULL) { + pr_err("Failed to allocate memory for common sysctl"); + return; + } + for (i = 0; i < ARRAY_SIZE(common->vars) - 1; i++) { + common->vars[i].data = + eio_find_sysctl_data(dmc, &common->vars[i]); + common->vars[i].extra1 = dmc; + } + + common->dev[0].procname = eio_cons_sysctl_devname(dmc); + common->dev[0].child = common->vars; + common->dir[0].child = common->dev; + common->root[0].child = common->dir; + common->sysctl_header = register_sysctl_table(common->root); + if (common->sysctl_header == NULL) { + pr_err("Failed to register common sysctl"); + goto out; + } + + dmc->sysctl_handle_common = common; + return; +out: + kfree(common->dev[0].procname); + kfree(common); +} + +/* + * eio_sysctl_unregister_common + */ +static void eio_sysctl_unregister_common(struct cache_c *dmc) +{ + struct sysctl_table_common *common; + + common = dmc->sysctl_handle_common; + if (common != NULL) { + dmc->sysctl_handle_common = NULL; + unregister_sysctl_table(common->sysctl_header); + kfree(common->dev[0].procname); + kfree(common); + } +} + +/* + * eio_sysctl_register_writeback + */ +static void eio_sysctl_register_writeback(struct cache_c *dmc) +{ + unsigned int i; + struct sysctl_table_writeback *writeback; + + writeback = + kmemdup(&sysctl_template_writeback, + sizeof(sysctl_template_writeback), GFP_KERNEL); + if (writeback == NULL) { + pr_err("Failed to allocate memory for writeback sysctl"); + return; + } + for (i = 0; i < ARRAY_SIZE(writeback->vars) - 1; i++) { + writeback->vars[i].data = + eio_find_sysctl_data(dmc, &writeback->vars[i]); + writeback->vars[i].extra1 = dmc; + } + + writeback->dev[0].procname = eio_cons_sysctl_devname(dmc); + writeback->dev[0].child = writeback->vars; + writeback->dir[0].child = writeback->dev; + writeback->root[0].child = writeback->dir; + writeback->sysctl_header = register_sysctl_table(writeback->root); + if (writeback->sysctl_header == NULL) { + pr_err("Failed to register writeback sysctl"); + goto out; + } + + dmc->sysctl_handle_writeback = writeback; + return; +out: + kfree(writeback->dev[0].procname); + kfree(writeback); +} + +/* + * eio_sysctl_unregister_writeback + */ +static void eio_sysctl_unregister_writeback(struct cache_c *dmc) +{ + struct sysctl_table_writeback *writeback; + + writeback = dmc->sysctl_handle_writeback; + if (writeback != NULL) { + dmc->sysctl_handle_writeback = NULL; + unregister_sysctl_table(writeback->sysctl_header); + kfree(writeback->dev[0].procname); + kfree(writeback); + } +} + +/* + * eio_sysctl_register_invalidate + */ +static void eio_sysctl_register_invalidate(struct cache_c *dmc) +{ + unsigned int i; + struct sysctl_table_invalidate *invalidate; + + invalidate = + kmemdup(&sysctl_template_invalidate, + sizeof(sysctl_template_invalidate), GFP_KERNEL); + if (invalidate == NULL) { + pr_err("Failed to allocate memory for invalidate sysctl"); + return; + } + for (i = 0; i < ARRAY_SIZE(invalidate->vars) - 1; i++) { + invalidate->vars[i].data = + eio_find_sysctl_data(dmc, &invalidate->vars[i]); + invalidate->vars[i].extra1 = dmc; + } + + invalidate->dev[0].procname = eio_cons_sysctl_devname(dmc); + invalidate->dev[0].child = invalidate->vars; + invalidate->dir[0].child = invalidate->dev; + invalidate->root[0].child = invalidate->dir; + invalidate->sysctl_header = register_sysctl_table(invalidate->root); + if (invalidate->sysctl_header == NULL) { + pr_err("Failed to register invalidate sysctl"); + goto out; + } + + dmc->sysctl_handle_invalidate = invalidate; + spin_lock_init(&invalidate_spin_lock); + return; +out: + kfree(invalidate->dev[0].procname); + kfree(invalidate); +} + +/* + * eio_sysctl_unregister_invalidate + */ +static void eio_sysctl_unregister_invalidate(struct cache_c *dmc) +{ + struct sysctl_table_invalidate *invalidate; + + invalidate = dmc->sysctl_handle_invalidate; + if (invalidate != NULL) { + dmc->sysctl_handle_invalidate = NULL; + unregister_sysctl_table(invalidate->sysctl_header); + kfree(invalidate->dev[0].procname); + kfree(invalidate); + } +} + +/* + * eio_stats_show + */ +static int eio_stats_show(struct seq_file *seq, void *v) +{ + struct cache_c *dmc = seq->private; + struct eio_stats *stats = &dmc->eio_stats; + int read_hit_pct, write_hit_pct, dirty_write_hit_pct; + + if (atomic64_read(&stats->reads) > 0) + read_hit_pct = + EIO_CALCULATE_PERCENTAGE( + atomic64_read(&stats->read_hits), + atomic64_read(&stats->reads)); + else + read_hit_pct = 0; + + if (atomic64_read(&stats->writes) > 0) { + write_hit_pct = + EIO_CALCULATE_PERCENTAGE( + atomic64_read(&stats->write_hits), + atomic64_read(&stats->writes)); + dirty_write_hit_pct = + EIO_CALCULATE_PERCENTAGE( + atomic64_read(&stats->dirty_write_hits), + atomic64_read(&stats->writes)); + } else { + write_hit_pct = 0; + dirty_write_hit_pct = 0; + } + + seq_printf(seq, "%-26s %12lld\n", "reads", + (int64_t)atomic64_read(&stats->reads)); + seq_printf(seq, "%-26s %12lld\n", "writes", + (int64_t)atomic64_read(&stats->writes)); + + seq_printf(seq, "%-26s %12lld\n", "read_hits", + (int64_t)atomic64_read(&stats->read_hits)); + seq_printf(seq, "%-26s %12d\n", "read_hit_pct", read_hit_pct); + + seq_printf(seq, "%-26s %12lld\n", "write_hits", + (int64_t)atomic64_read(&stats->write_hits)); + seq_printf(seq, "%-26s %12u\n", "write_hit_pct", write_hit_pct); + + seq_printf(seq, "%-26s %12lld\n", "dirty_write_hits", + (int64_t)atomic64_read(&stats->dirty_write_hits)); + seq_printf(seq, "%-26s %12d\n", "dirty_write_hit_pct", + dirty_write_hit_pct); + + if ((int64_t)(atomic64_read(&stats->cached_blocks)) < 0) + atomic64_set(&stats->cached_blocks, 0); + seq_printf(seq, "%-26s %12lld\n", "cached_blocks", + (int64_t)atomic64_read(&stats->cached_blocks)); + + seq_printf(seq, "%-26s %12lld\n", "rd_replace", + (int64_t)atomic64_read(&stats->rd_replace)); + seq_printf(seq, "%-26s %12lld\n", "wr_replace", + (int64_t)atomic64_read(&stats->wr_replace)); + + seq_printf(seq, "%-26s %12lld\n", "noroom", + (int64_t)atomic64_read(&stats->noroom)); + + seq_printf(seq, "%-26s %12lld\n", "cleanings", + (int64_t)atomic64_read(&stats->cleanings)); + seq_printf(seq, "%-26s %12lld\n", "md_write_dirty", + (int64_t)atomic64_read(&stats->md_write_dirty)); + seq_printf(seq, "%-26s %12lld\n", "md_write_clean", + (int64_t)atomic64_read(&stats->md_write_clean)); + seq_printf(seq, "%-26s %12lld\n", "md_ssd_writes", + (int64_t)atomic64_read(&stats->md_ssd_writes)); + seq_printf(seq, "%-26s %12d\n", "do_clean", + dmc->sysctl_active.do_clean); + seq_printf(seq, "%-26s %12lld\n", "nr_blocks", dmc->size); + seq_printf(seq, "%-26s %12lld\n", "nr_dirty", + (int64_t)atomic64_read(&dmc->nr_dirty)); + seq_printf(seq, "%-26s %12u\n", "nr_sets", (uint32_t)dmc->num_sets); + seq_printf(seq, "%-26s %12d\n", "clean_index", + (uint32_t)atomic_read(&dmc->clean_index)); + + seq_printf(seq, "%-26s %12lld\n", "uncached_reads", + (int64_t)atomic64_read(&stats->uncached_reads)); + seq_printf(seq, "%-26s %12lld\n", "uncached_writes", + (int64_t)atomic64_read(&stats->uncached_writes)); + seq_printf(seq, "%-26s %12lld\n", "uncached_map_size", + (int64_t)atomic64_read(&stats->uncached_map_size)); + seq_printf(seq, "%-26s %12lld\n", "uncached_map_uncacheable", + (int64_t)atomic64_read(&stats->uncached_map_uncacheable)); + + seq_printf(seq, "%-26s %12lld\n", "disk_reads", + (int64_t)atomic64_read(&stats->disk_reads)); + seq_printf(seq, "%-26s %12lld\n", "disk_writes", + (int64_t)atomic64_read(&stats->disk_writes)); + seq_printf(seq, "%-26s %12lld\n", "ssd_reads", + (int64_t)atomic64_read(&stats->ssd_reads)); + seq_printf(seq, "%-26s %12lld\n", "ssd_writes", + (int64_t)atomic64_read(&stats->ssd_writes)); + seq_printf(seq, "%-26s %12lld\n", "ssd_readfills", + (int64_t)atomic64_read(&stats->ssd_readfills)); + seq_printf(seq, "%-26s %12lld\n", "ssd_readfill_unplugs", + (int64_t)atomic64_read(&stats->ssd_readfill_unplugs)); + + seq_printf(seq, "%-26s %12lld\n", "readdisk", + (int64_t)atomic64_read(&stats->readdisk)); + seq_printf(seq, "%-26s %12lld\n", "writedisk", + (int64_t)atomic64_read(&stats->readdisk)); + seq_printf(seq, "%-26s %12lld\n", "readcache", + (int64_t)atomic64_read(&stats->readcache)); + seq_printf(seq, "%-26s %12lld\n", "readfill", + (int64_t)atomic64_read(&stats->readfill)); + seq_printf(seq, "%-26s %12lld\n", "writecache", + (int64_t)atomic64_read(&stats->writecache)); + + seq_printf(seq, "%-26s %12lld\n", "readcount", + (int64_t)atomic64_read(&stats->readcount)); + seq_printf(seq, "%-26s %12lld\n", "writecount", + (int64_t)atomic64_read(&stats->writecount)); + seq_printf(seq, "%-26s %12lld\n", "kb_reads", + (int64_t)atomic64_read(&stats->reads) / 2); + seq_printf(seq, "%-26s %12lld\n", "kb_writes", + (int64_t)atomic64_read(&stats->writes) / 2); + seq_printf(seq, "%-26s %12lld\n", "rdtime_ms", + (int64_t)atomic64_read(&stats->rdtime_ms)); + seq_printf(seq, "%-26s %12lld\n", "wrtime_ms", + (int64_t)atomic64_read(&stats->wrtime_ms)); + return 0; +} + +/* + * eio_stats_open + */ +static int eio_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, &eio_stats_show, PDE_DATA(inode)); +} + +/* + * eio_errors_show + */ +static int eio_errors_show(struct seq_file *seq, void *v) +{ + struct cache_c *dmc = seq->private; + + seq_printf(seq, "disk_read_errors %4u\n", + dmc->eio_errors.disk_read_errors); + seq_printf(seq, "disk_write_errors %4u\n", + dmc->eio_errors.disk_write_errors); + seq_printf(seq, "ssd_read_errors %4u\n", + dmc->eio_errors.ssd_read_errors); + seq_printf(seq, "ssd_write_errors %4u\n", + dmc->eio_errors.ssd_write_errors); + seq_printf(seq, "memory_alloc_errors %4u\n", + dmc->eio_errors.memory_alloc_errors); + seq_printf(seq, "no_cache_dev %4u\n", + dmc->eio_errors.no_cache_dev); + seq_printf(seq, "no_source_dev %4u\n", + dmc->eio_errors.no_source_dev); + + return 0; +} + +/* + * eio_errors_open + */ +static int eio_errors_open(struct inode *inode, struct file *file) +{ + return single_open(file, &eio_errors_show, PDE_DATA(inode)); +} + +/* + * eio_iosize_hist_show + */ +static int eio_iosize_hist_show(struct seq_file *seq, void *v) +{ + int i; + struct cache_c *dmc = seq->private; + + for (i = 1; i <= SIZE_HIST - 1; i++) { + if (atomic64_read(&dmc->size_hist[i]) == 0) + continue; + + if (i == 1) + seq_printf(seq, "%u %12lld\n", i * 512, + (int64_t)atomic64_read(&dmc->size_hist[i])); + else if (i < 20) + seq_printf(seq, "%u %12lld\n", i * 512, + (int64_t)atomic64_read(&dmc->size_hist[i])); + else + seq_printf(seq, "%u %12lld\n", i * 512, + (int64_t)atomic64_read(&dmc->size_hist[i])); + } + + return 0; +} + +/* + * eio_iosize_hist_open + */ +static int eio_iosize_hist_open(struct inode *inode, struct file *file) +{ + + return single_open(file, &eio_iosize_hist_show, PDE_DATA(inode)); +} + +/* + * eio_version_show + */ +static int eio_version_show(struct seq_file *seq, void *v) +{ + char buf[128]; + + memset(buf, 0, sizeof(buf)); + eio_version_query(sizeof(buf), buf); + seq_printf(seq, "%s\n", buf); + + return 0; +} + +/* + * eio_version_open + */ +static int eio_version_open(struct inode *inode, struct file *file) +{ + return single_open(file, &eio_version_show, PDE_DATA(inode)); +} + +/* + * eio_config_show + */ +static int eio_config_show(struct seq_file *seq, void *v) +{ + struct cache_c *dmc = seq->private; + + seq_printf(seq, "src_name %s\n", dmc->disk_devname); + seq_printf(seq, "ssd_name %s\n", dmc->cache_devname); + seq_printf(seq, "src_size %lu\n", (long unsigned int)dmc->disk_size); + seq_printf(seq, "ssd_size %lu\n", (long unsigned int)dmc->size); + + seq_printf(seq, "set_size %10u\n", dmc->assoc); + seq_printf(seq, "block_size %10u\n", (dmc->block_size) << SECTOR_SHIFT); + seq_printf(seq, "mode %10u\n", dmc->mode); + seq_printf(seq, "eviction %10u\n", dmc->req_policy); + seq_printf(seq, "num_sets %10u\n", dmc->num_sets); + seq_printf(seq, "num_blocks %10lu\n", (long unsigned int)dmc->size); + seq_printf(seq, "metadata %s\n", + CACHE_MD8_IS_SET(dmc) ? "large" : "small"); + seq_printf(seq, "state %s\n", + CACHE_DEGRADED_IS_SET(dmc) ? "degraded" + : (CACHE_FAILED_IS_SET(dmc) ? "failed" : "normal")); + seq_printf(seq, "flags 0x%08x\n", dmc->cache_flags); + + return 0; +} + +/* + * eio_config_open + */ +static int eio_config_open(struct inode *inode, struct file *file) +{ + + return single_open(file, &eio_config_show, PDE_DATA(inode)); +} diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_rand.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_rand.c --- linux-3.10.30/drivers/block/enhanceio/eio_rand.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_rand.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,201 @@ +/* + * eio_rand.c + * + * Copyright (C) 2013 ProfitBricks, GmbH. + * Jack Wang + * Dongsu Park + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "eio.h" +/* Generic policy functions prototypes */ +int eio_rand_init(struct cache_c *); +void eio_rand_exit(void); +int eio_rand_cache_sets_init(struct eio_policy *); +int eio_rand_cache_blk_init(struct eio_policy *); +void eio_rand_find_reclaim_dbn(struct eio_policy *, index_t, index_t *); +int eio_rand_clean_set(struct eio_policy *, index_t, int); + +/* Per policy instance initialization */ +struct eio_policy *eio_rand_instance_init(void); + + +/* + * Context that captures the rand replacement policy + */ +static struct eio_policy_header eio_rand_ops = { + .sph_name = CACHE_REPL_RANDOM, + .sph_instance_init = eio_rand_instance_init, +}; + +/* + * Initialize RAND policy. + */ +int eio_rand_init(struct cache_c *dmc) +{ + return 0; +} + +/* + * Initialize rand data structure called from ctr. + */ +int eio_rand_cache_sets_init(struct eio_policy *p_ops) +{ + return 0; +} + +/* + * The actual function that returns a victim block in index. + */ +void +eio_rand_find_reclaim_dbn(struct eio_policy *p_ops, index_t start_index, + index_t *index) +{ + int i = 0; + index_t idx; + + struct cache_c *dmc = p_ops->sp_dmc; + + /* + * "start_index" should already be the beginning index of the set. + * We're just being cautious here. + */ + start_index = (start_index / dmc->assoc) * dmc->assoc; + for (i = 0; i < (int)dmc->assoc; i++) { + idx = dmc->random++ % dmc->assoc; + if (EIO_CACHE_STATE_GET(dmc, start_index + idx) == VALID) { + *index = start_index + idx; + return; + } + } +} + +/* + * Go through the entire set and clean. + */ +int eio_rand_clean_set(struct eio_policy *p_ops, index_t set, int to_clean) +{ + int i = 0, nr_writes = 0; + index_t start_index; + + struct cache_c *dmc; + + dmc = p_ops->sp_dmc; + + start_index = set * dmc->assoc; + + /* Scan sequentially in the set and pick blocks to clean */ + while ((i < (int)dmc->assoc) && (nr_writes < to_clean)) { + if ((EIO_CACHE_STATE_GET(dmc, start_index + i) & + (DIRTY | BLOCK_IO_INPROG)) == DIRTY) { + EIO_CACHE_STATE_ON(dmc, start_index + i, + DISKWRITEINPROG); + nr_writes++; + } + i++; + } + + return nr_writes; +} + +/* + * rand is per set, so do nothing on a per block init. + */ +int eio_rand_cache_blk_init(struct eio_policy *p_ops) +{ + return 0; +} + +/* + * Allocate a new instance of eio_policy per dmc + */ +struct eio_policy *eio_rand_instance_init(void) +{ + struct eio_policy *new_instance; + + new_instance = vmalloc(sizeof(struct eio_policy)); + if (new_instance == NULL) { + pr_err("ssdscache_rand_instance_init: vmalloc failed"); + return NULL; + } + + /* Initialize the rand specific functions and variables */ + new_instance->sp_name = CACHE_REPL_RANDOM; + new_instance->sp_policy.lru = NULL; + new_instance->sp_repl_init = eio_rand_init; + new_instance->sp_repl_exit = eio_rand_exit; + new_instance->sp_repl_sets_init = eio_rand_cache_sets_init; + new_instance->sp_repl_blk_init = eio_rand_cache_blk_init; + new_instance->sp_find_reclaim_dbn = eio_rand_find_reclaim_dbn; + new_instance->sp_clean_set = eio_rand_clean_set; + new_instance->sp_dmc = NULL; + + try_module_get(THIS_MODULE); + + pr_info("eio_rand_instance_init: created new instance of RAND"); + + return new_instance; +} + +/* + * Cleanup an instance of eio_policy (called from dtr). + */ +void eio_rand_exit(void) +{ + module_put(THIS_MODULE); +} + +static +int __init rand_register(void) +{ + int ret; + + ret = eio_register_policy(&eio_rand_ops); + if (ret != 0) + pr_info("eio_rand already registered"); + + return ret; +} + +static +void __exit rand_unregister(void) +{ + int ret; + + ret = eio_unregister_policy(&eio_rand_ops); + if (ret != 0) + pr_err("eio_rand unregister failed"); +} + +module_init(rand_register); +module_exit(rand_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("RAND policy for EnhanceIO"); +MODULE_AUTHOR("STEC, Inc. based on code by Facebook"); diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_setlru.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_setlru.c --- linux-3.10.30/drivers/block/enhanceio/eio_setlru.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_setlru.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,170 @@ +/* + * eio_setlru.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Amit Kale + * Harish Pujari + * Generic lru implementation used mainly for cache sets. + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + */ + +#include "eio.h" + +/* Initialize the lru list */ +int lru_init(struct lru_ls **llist, index_t max) +{ + index_t i = 0; + + EIO_ASSERT(max > 0); + *llist = vmalloc((sizeof(struct lru_ls) + (max - 1) * sizeof(struct lru_elem))); + if (*llist == NULL) + return -ENOMEM; + + (*llist)->ll_head = LRU_NULL; + (*llist)->ll_tail = LRU_NULL; + (*llist)->ll_max = max; + (*llist)->ll_size = 0; + + for (i = 0; i < max; i++) { + (*llist)->ll_elem[i].le_next = LRU_NULL; + (*llist)->ll_elem[i].le_prev = LRU_NULL; + (*llist)->ll_elem[i].le_key = 0; + } + + return 0; +} + +/* Uninitialize the lru list */ +void lru_uninit(struct lru_ls *llist) +{ + if (llist) + vfree(llist); +} + +/* Add a new entry to lru list */ +int lru_add(struct lru_ls *llist, index_t index, u_int64_t key) +{ + if (!llist || (index >= llist->ll_max)) + return -EINVAL; + + llist->ll_elem[index].le_prev = llist->ll_tail; + llist->ll_elem[index].le_next = LRU_NULL; + llist->ll_elem[index].le_key = key; + + if (llist->ll_tail != LRU_NULL) + llist->ll_elem[llist->ll_tail].le_next = index; + else { + EIO_ASSERT(llist->ll_head == LRU_NULL); + llist->ll_head = index; + } + llist->ll_tail = index; + llist->ll_size++; + + return 0; +} + +/* Remove an entry from the lru list */ +int lru_rem(struct lru_ls *llist, index_t index) +{ + if (!llist || (index >= llist->ll_max) || (index == LRU_NULL)) + return -EINVAL; + + if (llist->ll_head == LRU_NULL && llist->ll_tail == LRU_NULL) + /* + * No element in the list. + */ + return -EINVAL; + + if (llist->ll_elem[index].le_prev == LRU_NULL && + llist->ll_elem[index].le_next == LRU_NULL && + llist->ll_head != index && llist->ll_tail != index) + /* + * Element not in list. + */ + return 0; + + if (llist->ll_elem[index].le_prev != LRU_NULL) + llist->ll_elem[llist->ll_elem[index].le_prev].le_next = + llist->ll_elem[index].le_next; + + if (llist->ll_elem[index].le_next != LRU_NULL) + llist->ll_elem[llist->ll_elem[index].le_next].le_prev = + llist->ll_elem[index].le_prev; + + if (llist->ll_head == index) + llist->ll_head = llist->ll_elem[index].le_next; + + if (llist->ll_tail == index) + llist->ll_tail = llist->ll_elem[index].le_prev; + + llist->ll_elem[index].le_prev = LRU_NULL; + llist->ll_elem[index].le_next = LRU_NULL; + EIO_ASSERT(llist->ll_size != 0); + llist->ll_size--; + + return 0; +} + +/* Move up the given lru element */ +int lru_touch(struct lru_ls *llist, index_t index, u_int64_t key) +{ + if (!llist || (index >= llist->ll_max)) + return -EINVAL; + + if (llist->ll_tail == index) + llist->ll_elem[index].le_key = key; + else { + lru_rem(llist, index); + lru_add(llist, index, key); + } + + return 0; +} + +/* Read the element at the head of the lru */ +int lru_read_head(struct lru_ls *llist, index_t *index, u_int64_t *key) +{ + if (!llist || !index || !key) + return -EINVAL; + + *index = llist->ll_head; + if (llist->ll_head == LRU_NULL) { + *index = LRU_NULL; + *key = 0; + } else { + *index = llist->ll_head; + *key = llist->ll_elem[*index].le_key; + } + + return 0; +} + +/* Remove the element at the head of the lru */ +int lru_rem_head(struct lru_ls *llist, index_t *index, u_int64_t *key) +{ + if (!llist || !index || !key) + return -EINVAL; + + *index = llist->ll_head; + if (llist->ll_head == LRU_NULL) { + *index = LRU_NULL; + *key = 0; + } else { + *index = llist->ll_head; + *key = llist->ll_elem[*index].le_key; + lru_rem(llist, *index); + } + + return 0; +} diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_setlru.h linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_setlru.h --- linux-3.10.30/drivers/block/enhanceio/eio_setlru.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_setlru.h 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,49 @@ +/* + * eio_setlru.h + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Amit Kale + * Harish Pujari + * Generic lru implementation used mainly for cache sets + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + */ + +#ifndef _EIO_SETLRU_H_ +#define _EIO_SETLRU_H_ + +#define LRU_NULL -1 + +struct lru_elem { + index_t le_next; + index_t le_prev; + u_int64_t le_key; +}; + +struct lru_ls { + index_t ll_head; + index_t ll_tail; + index_t ll_max; + u_int64_t ll_size; + struct lru_elem ll_elem[1]; +}; + +int lru_init(struct lru_ls **llist, index_t max); +void lru_uninit(struct lru_ls *llist); +int lru_add(struct lru_ls *llist, index_t index, u_int64_t key); +int lru_rem(struct lru_ls *llist, index_t index); +int lru_touch(struct lru_ls *llist, index_t index, u_int64_t key); +int lru_read_head(struct lru_ls *llist, index_t *index, u_int64_t *key); +int lru_rem_head(struct lru_ls *llist, index_t *index, u_int64_t *key); + +#endif /* _EIO_SETLRU_H_ */ diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_subr.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_subr.c --- linux-3.10.30/drivers/block/enhanceio/eio_subr.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_subr.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,444 @@ +/* + * eio_subr.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * Made EnhanceIO specific changes. + * Saied Kazemi + * Siddharth Choudhuri + * + * Copyright 2010 Facebook, Inc. + * Author: Mohan Srinivasan (mohan@facebook.com) + * + * Based on DM-Cache: + * Copyright (C) International Business Machines Corp., 2006 + * Author: Ming Zhao (mingzhao@ufl.edu) + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "eio.h" +#include "eio_ttc.h" + +static DEFINE_SPINLOCK(_job_lock); +static LIST_HEAD(_io_jobs); +static LIST_HEAD(_disk_read_jobs); + +int eio_io_empty(void) +{ + + return list_empty(&_io_jobs); +} + +struct kcached_job *eio_alloc_cache_job(void) +{ + struct kcached_job *job; + + job = mempool_alloc(_job_pool, GFP_NOIO); + if (likely(job)) + atomic_inc(&nr_cache_jobs); + return job; +} + +void eio_free_cache_job(struct kcached_job *job) +{ + + mempool_free(job, _job_pool); + atomic_dec(&nr_cache_jobs); +} + +/* + * Functions to push and pop a job onto the head of a given job list. + */ +static struct kcached_job *eio_pop(struct list_head *jobs) +{ + struct kcached_job *job = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&_job_lock, flags); + if (!list_empty(jobs)) { + job = list_entry(jobs->next, struct kcached_job, list); + list_del(&job->list); + } + spin_unlock_irqrestore(&_job_lock, flags); + return job; +} + +static void eio_push(struct list_head *jobs, struct kcached_job *job) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&_job_lock, flags); + list_add_tail(&job->list, jobs); + spin_unlock_irqrestore(&_job_lock, flags); +} + +void eio_push_ssdread_failures(struct kcached_job *job) +{ + + eio_push(&_disk_read_jobs, job); +} + +static void +eio_process_jobs(struct list_head *jobs, void (*fn) (struct kcached_job *)) +{ + struct kcached_job *job; + + while ((job = eio_pop(jobs)) != NULL) + (void)fn(job); +} + +static void eio_process_ssd_rm_list(void) +{ + unsigned long int flags = 0; + struct ssd_rm_list *ssd_list_ptr; + extern int ssd_rm_list_not_empty; + extern spinlock_t ssd_rm_list_lock; + extern struct list_head ssd_rm_list; + + spin_lock_irqsave(&ssd_rm_list_lock, flags); + if (likely(list_empty(&ssd_rm_list))) { + spin_unlock_irqrestore(&ssd_rm_list_lock, flags); + return; + } + + while (!list_empty(&ssd_rm_list)) { + ssd_list_ptr = + list_entry(ssd_rm_list.next, struct ssd_rm_list, list); + if (ssd_list_ptr->action == BUS_NOTIFY_DEL_DEVICE) + eio_suspend_caching(ssd_list_ptr->dmc, + ssd_list_ptr->note); + else + pr_err("eio_process_ssd_rm_list:" + "Unknown status (0x%x)\n", ssd_list_ptr->action); + list_del(&ssd_list_ptr->list); + kfree(ssd_list_ptr); + } + ssd_rm_list_not_empty = 0; + spin_unlock_irqrestore(&ssd_rm_list_lock, flags); +} + +/* + * Entry point of the "events" kernel thread. + */ +void eio_do_work(struct work_struct *unused) +{ + extern int ssd_rm_list_not_empty; + + if (unlikely(ssd_rm_list_not_empty)) + eio_process_ssd_rm_list(); + eio_process_jobs(&_disk_read_jobs, eio_ssderror_diskread); +} + +struct kcached_job *eio_new_job(struct cache_c *dmc, struct eio_bio *bio, + index_t index) +{ + struct kcached_job *job; + + EIO_ASSERT((bio != NULL) || (index != -1)); + + job = eio_alloc_cache_job(); + if (unlikely(job == NULL)) { + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->eio_errors.memory_alloc_errors++; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return NULL; + } + job->dmc = dmc; + job->index = index; + job->error = 0; + job->ebio = bio; + if (index != -1) { + job->job_io_regions.cache.bdev = dmc->cache_dev->bdev; + if (bio) { + job->job_io_regions.cache.sector = + (index << dmc->block_shift) + dmc->md_sectors + + (bio->eb_sector - + EIO_ROUND_SECTOR(dmc, bio->eb_sector)); + EIO_ASSERT(eio_to_sector(bio->eb_size) <= + dmc->block_size); + job->job_io_regions.cache.count = + eio_to_sector(bio->eb_size); + } else { + job->job_io_regions.cache.sector = + (index << dmc->block_shift) + dmc->md_sectors; + job->job_io_regions.cache.count = dmc->block_size; + } + } + + job->job_io_regions.disk.bdev = dmc->disk_dev->bdev; + if (bio) { + job->job_io_regions.disk.sector = bio->eb_sector; + job->job_io_regions.disk.count = eio_to_sector(bio->eb_size); + } else { + job->job_io_regions.disk.sector = EIO_DBN_GET(dmc, index); + job->job_io_regions.disk.count = dmc->block_size; + } + job->next = NULL; + job->md_sector = NULL; + + return job; +} + +int +eio_io_sync_pages(struct cache_c *dmc, struct eio_io_region *where, int rw, + struct page **pages, int num_bvecs) +{ + struct eio_io_request req; + int error; + + req.mtype = EIO_PAGES; + req.dptr.plist = pages; + req.num_bvecs = num_bvecs; + req.notify = NULL; + req.context = NULL; + req.hddio = 0; + + if ((unlikely(CACHE_FAILED_IS_SET(dmc)) || + unlikely(CACHE_DEGRADED_IS_SET(dmc))) && + (!CACHE_SSD_ADD_INPROG_IS_SET(dmc))) + error = -ENODEV; + else + error = eio_do_io(dmc, where, rw, &req); + + if (error) + return error; + + return 0; +} + +int +eio_io_sync_vm(struct cache_c *dmc, struct eio_io_region *where, int rw, + struct bio_vec *pages, int num_bvecs) +{ + struct eio_io_request req; + int error; + + memset((char *)&req, 0, sizeof(req)); + /* Fill up the appropriate fields + * in eio_io_request */ + req.mtype = EIO_BVECS; + req.dptr.pages = pages; + req.num_bvecs = num_bvecs; + req.notify = NULL; + req.context = NULL; + req.hddio = 0; + if ((unlikely(CACHE_FAILED_IS_SET(dmc)) || + unlikely(CACHE_DEGRADED_IS_SET(dmc))) && + (!CACHE_SSD_ADD_INPROG_IS_SET(dmc))) + error = -ENODEV; + else + error = eio_do_io(dmc, where, rw, &req); + if (error) + return error; + return 0; +} + +void eio_unplug_cache_device(struct cache_c *dmc) +{ + struct request_queue *q; + struct block_device *bdev; + + if (unlikely(CACHE_FAILED_IS_SET(dmc)) || + unlikely(CACHE_DEGRADED_IS_SET(dmc))) + return; + bdev = dmc->cache_dev->bdev; + q = bdev_get_queue(bdev); +} + +void eio_unplug_disk_device(struct cache_c *dmc) +{ + struct request_queue *q; + struct block_device *bdev; + + if (unlikely(CACHE_DEGRADED_IS_SET(dmc))) + return; + bdev = dmc->disk_dev->bdev; + q = bdev_get_queue(bdev); +} + +void eio_plug_cache_device(struct cache_c *dmc) +{ + struct block_device *bdev; + struct request_queue *q; + + if (unlikely(CACHE_FAILED_IS_SET(dmc)) || + unlikely(CACHE_DEGRADED_IS_SET(dmc))) + return; + bdev = dmc->cache_dev->bdev; + q = bdev_get_queue(bdev); +} + +void eio_plug_disk_device(struct cache_c *dmc) +{ + struct block_device *bdev; + struct request_queue *q; + + if (unlikely(CACHE_DEGRADED_IS_SET(dmc))) + return; + bdev = dmc->disk_dev->bdev; + q = bdev_get_queue(bdev); +} + +/* + * For Linux, we do not do a dm_put_device() when the device underneath + * disappears. The logic to handle the IOs to a missing device is handled + * by the kernel proper. We will get an IO error if an IO is done on a + * device that does not exist. + */ +void eio_suspend_caching(struct cache_c *dmc, enum dev_notifier note) +{ + + spin_lock_irqsave(&dmc->cache_spin_lock, dmc->cache_spin_lock_flags); + if (dmc->mode != CACHE_MODE_WB && CACHE_FAILED_IS_SET(dmc)) { + pr_err("suspend caching: Cache " + "%s is already in FAILED state\n", dmc->cache_name); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return; + } + + switch (note) { + + case NOTIFY_SRC_REMOVED: + if (CACHE_DEGRADED_IS_SET(dmc)) + dmc->cache_flags &= ~CACHE_FLAGS_DEGRADED; + dmc->cache_flags |= CACHE_FLAGS_FAILED; + dmc->eio_errors.no_source_dev = 1; + atomic64_set(&dmc->eio_stats.cached_blocks, 0); + pr_info("suspend_caching: Source Device Removed." + "Cache \"%s\" is in Failed mode.\n", dmc->cache_name); + break; + case NOTIFY_SSD_REMOVED: + if (dmc->mode == CACHE_MODE_WB) { + /* + * For writeback + * - Cache should never be in degraded mode + * - ssd removal should result in FAILED state + * - the cached block should not be reset. + */ + EIO_ASSERT(!CACHE_DEGRADED_IS_SET(dmc)); + dmc->cache_flags |= CACHE_FLAGS_FAILED; + pr_info("suspend caching: SSD Device Removed.\ + Cache \"%s\" is in Failed mode.\n", dmc->cache_name); + } else { + if (CACHE_DEGRADED_IS_SET(dmc) || + CACHE_SSD_ADD_INPROG_IS_SET(dmc)) { + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc-> + cache_spin_lock_flags); + pr_err("suspend_caching: Cache \ + \"%s\" is either degraded \ + or device add in progress, exiting.\n", dmc->cache_name); + return; + } + dmc->cache_flags |= CACHE_FLAGS_DEGRADED; + atomic64_set(&dmc->eio_stats.cached_blocks, 0); + pr_info("suspend caching: Cache \"%s\" \ + is in Degraded mode.\n", dmc->cache_name); + } + dmc->eio_errors.no_cache_dev = 1; + break; + default: + pr_err("suspend_caching: incorrect notify message.\n"); + break; + } + + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); +} + +void eio_put_cache_device(struct cache_c *dmc) +{ + + eio_ttc_put_device(&dmc->cache_dev); +} + +void eio_resume_caching(struct cache_c *dmc, char *dev) +{ + int r; + + if (dmc == NULL || dev == NULL) { + pr_err("resume_caching: Null device or" + "cache instance when resuming caching.\n"); + return; + } + if (strlen(dev) >= DEV_PATHLEN) { + pr_err("resume_caching: Device name %s too long.\n", dev); + return; + } + + spin_lock_irqsave(&dmc->cache_spin_lock, dmc->cache_spin_lock_flags); + if (CACHE_STALE_IS_SET(dmc)) { + pr_err("eio_resume_caching: Hard Failure Detected!!" + "Cache \"%s\" can not be resumed.", dmc->cache_name); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return; + } + + /* sanity check for writeback */ + if (dmc->mode == CACHE_MODE_WB) { + if (!CACHE_FAILED_IS_SET(dmc) || CACHE_SRC_IS_ABSENT(dmc) || + CACHE_SSD_ADD_INPROG_IS_SET(dmc)) { + pr_debug("eio_resume_caching: Cache not in Failed " + "state or Source is absent" + "or SSD add already in progress for cache \"%s\".\n", + dmc->cache_name); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return; + } + } else { + /* sanity check for WT or RO cache. */ + if (CACHE_FAILED_IS_SET(dmc) || !CACHE_DEGRADED_IS_SET(dmc) || + CACHE_SSD_ADD_INPROG_IS_SET(dmc)) { + pr_err("resume_caching: Cache \"%s\" " + "is either in failed mode or " + "cache device add in progress, ignoring.\n ", + dmc->cache_name); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return; + } + } + + dmc->cache_flags |= CACHE_FLAGS_SSD_ADD_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + r = eio_ctr_ssd_add(dmc, dev); + if (r) { + /* error */ + pr_debug(" resume caching: returned error: %d\n ", r); + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_SSD_ADD_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return; + } + + spin_lock_irqsave(&dmc->cache_spin_lock, dmc->cache_spin_lock_flags); + dmc->eio_errors.no_cache_dev = 0; + if (dmc->mode != CACHE_MODE_WB) + dmc->cache_flags &= ~CACHE_FLAGS_DEGRADED; + else + dmc->cache_flags &= ~CACHE_FLAGS_FAILED; + dmc->cache_flags &= ~CACHE_FLAGS_SSD_ADD_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + pr_info(" resume_caching:cache %s is restored to ACTIVE mode.\n", + dmc->cache_name); +} diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_ttc.c linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_ttc.c --- linux-3.10.30/drivers/block/enhanceio/eio_ttc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_ttc.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,1665 @@ +/* + * True Transparent Caching (TTC) code. + * eio_ttc.c + * + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved + * + * Made EIO fully transparent with respect to applications. A cache can be + * created or deleted while a filesystem or applications are online + * Amit Kale + * Ramprasad Chinthekindi + * Akhil Bhansali + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include "eio.h" +#include "eio_ttc.h" +static struct rw_semaphore eio_ttc_lock[EIO_HASHTBL_SIZE]; +static struct list_head eio_ttc_list[EIO_HASHTBL_SIZE]; + +int eio_reboot_notified; + +static void eio_make_request_fn(struct request_queue *, struct bio *); +static void eio_cache_rec_fill(struct cache_c *, struct cache_rec_short *); +static void eio_bio_end_empty_barrier(struct bio *, int); +static void eio_issue_empty_barrier_flush(struct block_device *, struct bio *, + int, make_request_fn *, int rw_flags); +static int eio_finish_nrdirty(struct cache_c *); +static int eio_mode_switch(struct cache_c *, u_int32_t); +static int eio_policy_switch(struct cache_c *, u_int32_t); + +static int eio_overlap_split_bio(struct request_queue *, struct bio *); +static struct bio *eio_split_new_bio(struct bio *, struct bio_container *, + unsigned *, unsigned *, sector_t); +static void eio_split_endio(struct bio *, int); + +static int eio_open(struct inode *ip, struct file *filp) +{ + __module_get(THIS_MODULE); + return 0; +} + +static int eio_release(struct inode *ip, struct file *filp) +{ + module_put(THIS_MODULE); + return 0; +} + +static const struct file_operations eio_fops = { + .open = eio_open, + .release = eio_release, + .unlocked_ioctl = eio_ioctl, + .compat_ioctl = eio_compact_ioctl, + .owner = THIS_MODULE, +}; + +static struct miscdevice eio_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = MISC_DEVICE, + .fops = &eio_fops, +}; + +int eio_create_misc_device() +{ + return misc_register(&eio_misc); +} + +int eio_delete_misc_device() +{ + return misc_deregister(&eio_misc); +} + +int eio_ttc_get_device(const char *path, fmode_t mode, struct eio_bdev **result) +{ + struct block_device *bdev; + struct eio_bdev *eio_bdev; + + static char *eio_holder = "EnhanceIO"; + + bdev = blkdev_get_by_path(path, mode, eio_holder); + if (IS_ERR(bdev)) + return PTR_ERR(bdev); + + eio_bdev = kzalloc(sizeof(*eio_bdev), GFP_KERNEL); + if (eio_bdev == NULL) { + blkdev_put(bdev, mode); + return -ENOMEM; + } + + eio_bdev->bdev = bdev; + eio_bdev->mode = mode; + *result = eio_bdev; + return 0; +} + +void eio_ttc_put_device(struct eio_bdev **d) +{ + struct eio_bdev *eio_bdev; + + eio_bdev = *d; + blkdev_put(eio_bdev->bdev, eio_bdev->mode); + kfree(eio_bdev); + *d = NULL; + return; +} + +struct cache_c *eio_cache_lookup(char *name) +{ + struct cache_c *dmc = NULL; + int i; + + for (i = 0; i < EIO_HASHTBL_SIZE; i++) { + down_read(&eio_ttc_lock[i]); + list_for_each_entry(dmc, &eio_ttc_list[i], cachelist) { + if (!strcmp(name, dmc->cache_name)) { + up_read(&eio_ttc_lock[i]); + return dmc; + } + } + up_read(&eio_ttc_lock[i]); + } + return NULL; +} + +int eio_ttc_activate(struct cache_c *dmc) +{ + struct block_device *bdev; + struct request_queue *rq; + make_request_fn *origmfn; + struct cache_c *dmc1; + int wholedisk; + int error; + int index; + int rw_flags = 0; + + bdev = dmc->disk_dev->bdev; + if (bdev == NULL) { + pr_err("cache_create: Source device not found\n"); + return -ENODEV; + } + rq = bdev->bd_disk->queue; + + wholedisk = 0; + if (bdev == bdev->bd_contains) + wholedisk = 1; + + dmc->dev_start_sect = bdev->bd_part->start_sect; + dmc->dev_end_sect = + bdev->bd_part->start_sect + bdev->bd_part->nr_sects - 1; + + pr_debug("eio_ttc_activate: Device/Partition" \ + " sector_start: %llu, end: %llu\n", + (uint64_t)dmc->dev_start_sect, (uint64_t)dmc->dev_end_sect); + + error = 0; + origmfn = NULL; + index = EIO_HASH_BDEV(bdev->bd_contains->bd_dev); + + down_write(&eio_ttc_lock[index]); + list_for_each_entry(dmc1, &eio_ttc_list[index], cachelist) { + if (dmc1->disk_dev->bdev->bd_contains != bdev->bd_contains) + continue; + + if ((wholedisk) || (dmc1->dev_info == EIO_DEV_WHOLE_DISK) || + (dmc1->disk_dev->bdev == bdev)) { + error = -EINVAL; + up_write(&eio_ttc_lock[index]); + goto out; + } + + /* some partition of same device already cached */ + EIO_ASSERT(dmc1->dev_info == EIO_DEV_PARTITION); + origmfn = dmc1->origmfn; + break; + } + + /* + * Save original make_request_fn. Switch make_request_fn only once. + */ + + if (origmfn) { + dmc->origmfn = origmfn; + dmc->dev_info = EIO_DEV_PARTITION; + EIO_ASSERT(wholedisk == 0); + } else { + dmc->origmfn = rq->make_request_fn; + rq->make_request_fn = eio_make_request_fn; + dmc->dev_info = + (wholedisk) ? EIO_DEV_WHOLE_DISK : EIO_DEV_PARTITION; + } + + list_add_tail(&dmc->cachelist, &eio_ttc_list[index]); + + /* + * Sleep for sometime, to allow previous I/Os to hit + * Issue a barrier I/O on Source device. + */ + + msleep(1); + SET_BARRIER_FLAGS(rw_flags); + eio_issue_empty_barrier_flush(dmc->disk_dev->bdev, NULL, + EIO_HDD_DEVICE, dmc->origmfn, rw_flags); + up_write(&eio_ttc_lock[index]); + +out: + if (error == -EINVAL) { + if (wholedisk) + pr_err + ("cache_create: A partition of this device is already cached.\n"); + else + pr_err("cache_create: Device is already cached.\n"); + } + return error; +} + +int eio_ttc_deactivate(struct cache_c *dmc, int force) +{ + struct block_device *bdev; + struct request_queue *rq; + struct cache_c *dmc1; + int found_partitions; + int index; + int ret; + + ret = 0; + bdev = dmc->disk_dev->bdev; + rq = bdev->bd_disk->queue; + + if (force) + goto deactivate; + + /* Process and wait for nr_dirty to drop to zero */ + if (dmc->mode == CACHE_MODE_WB) { + if (!CACHE_FAILED_IS_SET(dmc)) { + ret = eio_finish_nrdirty(dmc); + if (ret) { + pr_err + ("ttc_deactivate: nrdirty failed to finish for cache \"%s\".", + dmc->cache_name); + return ret; + } + } else + pr_debug + ("ttc_deactivate: Cache \"%s\" failed is already set. Continue with cache delete.", + dmc->cache_name); + } + + /* + * Traverse the list and see if other partitions of this device are + * cached. Switch mfn if this is the only partition of the device + * in the list. + */ +deactivate: + index = EIO_HASH_BDEV(bdev->bd_contains->bd_dev); + found_partitions = 0; + + /* check if barrier QUEUE is empty or not */ + down_write(&eio_ttc_lock[index]); + + if (dmc->dev_info != EIO_DEV_WHOLE_DISK) + list_for_each_entry(dmc1, &eio_ttc_list[index], cachelist) { + if (dmc == dmc1) + continue; + + if (dmc1->disk_dev->bdev->bd_contains != bdev->bd_contains) + continue; + + EIO_ASSERT(dmc1->dev_info == EIO_DEV_PARTITION); + + /* + * There are still other partitions which are cached. + * Do not switch the make_request_fn. + */ + + found_partitions = 1; + break; + } + + if ((dmc->dev_info == EIO_DEV_WHOLE_DISK) || (found_partitions == 0)) + rq->make_request_fn = dmc->origmfn; + + list_del_init(&dmc->cachelist); + up_write(&eio_ttc_lock[index]); + + /* wait for nr_ios to drain-out */ + while (atomic64_read(&dmc->nr_ios) != 0) + schedule_timeout(msecs_to_jiffies(100)); + + return ret; +} + +void eio_ttc_init(void) +{ + int i; + + for (i = 0; i < EIO_HASHTBL_SIZE; i++) { + init_rwsem(&eio_ttc_lock[i]); + INIT_LIST_HEAD(&eio_ttc_list[i]); + } +} + +/* + * Cases:- + * 1. Full device cached. + * if (ENQUEUE || barrier(bio)) + * enqueue (dmc, bio) and return + * else + * call eio_map(dmc, bio) + * 2. Some partitions of the device cached. + * if (ENQUEUE || barrier(bio)) + * All I/Os (both on cached and uncached partitions) are enqueued. + * else + * if (I/O on cached partition) + * call eio_map(dmc, bio) + * else + * origmfn(bio); // uncached partition + * 3. q->mfn got switched back to original + * call origmfn(q, bio) + * 4. Race condition: + */ + +static void eio_make_request_fn(struct request_queue *q, struct bio *bio) +{ + int ret; + int overlap; + int index; + make_request_fn *origmfn; + struct cache_c *dmc, *dmc1; + struct block_device *bdev; + + bdev = bio->bi_bdev; + +re_lookup: + dmc = NULL; + origmfn = NULL; + overlap = ret = 0; + + index = EIO_HASH_BDEV(bdev->bd_contains->bd_dev); + + down_read(&eio_ttc_lock[index]); + + list_for_each_entry(dmc1, &eio_ttc_list[index], cachelist) { + if (dmc1->disk_dev->bdev->bd_contains != bdev->bd_contains) + continue; + + if (dmc1->dev_info == EIO_DEV_WHOLE_DISK) { + dmc = dmc1; /* found cached device */ + break; + } + + /* Handle partitions */ + if (!origmfn) + origmfn = dmc1->origmfn; + + /* I/O perfectly fit within cached partition */ + if ((bio->bi_sector >= dmc1->dev_start_sect) && + ((bio->bi_sector + eio_to_sector(bio->bi_size) - 1) <= + dmc1->dev_end_sect)) { + EIO_ASSERT(overlap == 0); + dmc = dmc1; /* found cached partition */ + break; + } + + /* Check if I/O is overlapping with cached partitions */ + if (((bio->bi_sector >= dmc1->dev_start_sect) && + (bio->bi_sector <= dmc1->dev_end_sect)) || + ((bio->bi_sector + eio_to_sector(bio->bi_size) - 1 >= + dmc1->dev_start_sect) && + (bio->bi_sector + eio_to_sector(bio->bi_size) - 1 <= + dmc1->dev_end_sect))) { + overlap = 1; + pr_err + ("Overlapping I/O detected on %s cache at sector: %llu, size: %u\n", + dmc1->cache_name, (uint64_t)bio->bi_sector, + bio->bi_size); + break; + } + } + + if (unlikely(overlap)) { + up_read(&eio_ttc_lock[index]); + + if (bio_rw_flagged(bio, REQ_DISCARD)) { + pr_err + ("eio_mfn: Overlap I/O with Discard flag." \ + " Discard flag is not supported.\n"); + bio_endio(bio, -EOPNOTSUPP); + } else + ret = eio_overlap_split_bio(q, bio); + } else if (dmc) { /* found cached partition or device */ + /* + * Start sector of cached partition may or may not be + * aligned with cache blocksize. + * Map start of the partition to zero reference. + */ + + if (bio->bi_sector) { + EIO_ASSERT(bio->bi_sector >= dmc->dev_start_sect); + bio->bi_sector -= dmc->dev_start_sect; + } + ret = eio_map(dmc, q, bio); + if (ret) + /* Error case: restore the start sector of bio */ + bio->bi_sector += dmc->dev_start_sect; + } + + if (!overlap) + up_read(&eio_ttc_lock[index]); + + if (overlap || dmc) + return; + + /* + * Race condition:- + * origmfn can be NULL if all partitions or whole disk got uncached. + * We set origmfn = q->mfn if origmfn is NULL. + * The origmfn may now again be eio_make_request_fn because + * someone else switched the q->mfn because of a new + * partition or whole disk being cached. + * Since, we cannot protect q->make_request_fn() by any lock, + * this situation may occur. However, this is a very rare event. + * In this case restart the lookup. + */ + + if (origmfn == NULL) + origmfn = q->make_request_fn; + if (origmfn == eio_make_request_fn) + goto re_lookup; + + origmfn(q, bio); + return; +} + +uint64_t eio_get_cache_count(void) +{ + struct cache_c *dmc; + uint64_t cnt = 0; + int i; + + for (i = 0; i < EIO_HASHTBL_SIZE; i++) { + down_read(&eio_ttc_lock[i]); + list_for_each_entry(dmc, &eio_ttc_list[i], cachelist) { + cnt++; + } + up_read(&eio_ttc_lock[i]); + } + return cnt; +} + +int eio_get_cache_list(unsigned long *arg) +{ + int error = 0; + unsigned int size, i, j; + struct cache_list reclist; + struct cache_rec_short *cache_recs; + struct cache_c *dmc; + + if (copy_from_user(&reclist, (struct cache_list __user *)arg, + sizeof(struct cache_list))) { + error = -EFAULT; + goto out; + } + + size = reclist.ncaches * sizeof(struct cache_rec_short); + cache_recs = vmalloc(size); + if (!cache_recs) { + error = -ENOMEM; + goto out; + } + memset(cache_recs, 0, size); + + i = 0; + for (j = 0; j < EIO_HASHTBL_SIZE; j++) { + down_read(&eio_ttc_lock[j]); + list_for_each_entry(dmc, &eio_ttc_list[j], cachelist) { + eio_cache_rec_fill(dmc, &cache_recs[i]); + i++; + + if (i == reclist.ncaches) + break; + } + up_read(&eio_ttc_lock[j]); + + if (i == reclist.ncaches) + break; + } + + if (copy_to_user((char __user *)reclist.cachelist, + (char *)cache_recs, size)) { + error = -EFAULT; + goto out; + } + + if (copy_to_user((struct cache_list __user *)arg, &reclist, + sizeof(struct cache_list))) { + error = -EFAULT; + goto out; + } + +out: + return error; +} + +static void eio_cache_rec_fill(struct cache_c *dmc, struct cache_rec_short *rec) +{ + strncpy(rec->cr_name, dmc->cache_name, sizeof(rec->cr_name) - 1); + strncpy(rec->cr_src_devname, dmc->disk_devname, + sizeof(rec->cr_src_devname) - 1); + strncpy(rec->cr_ssd_devname, dmc->cache_devname, + sizeof(rec->cr_ssd_devname) - 1); + rec->cr_src_dev_size = eio_get_device_size(dmc->disk_dev); + rec->cr_ssd_dev_size = eio_get_device_size(dmc->cache_dev); + rec->cr_src_sector_size = 0; /* unused in userspace */ + rec->cr_ssd_sector_size = 0; /* unused in userspace */ + rec->cr_flags = dmc->cache_flags; + rec->cr_policy = dmc->req_policy; + rec->cr_mode = dmc->mode; + rec->cr_persistence = dmc->persistence; + rec->cr_blksize = dmc->block_size; /* In sectors */ + rec->cr_assoc = dmc->assoc; + return; +} + +/* + * Few sanity checks before cache creation. + */ + +int eio_do_preliminary_checks(struct cache_c *dmc) +{ + struct block_device *bdev, *ssd_bdev; + struct cache_c *dmc1; + int error; + int wholedisk; + int index; + + error = wholedisk = 0; + bdev = dmc->disk_dev->bdev; + ssd_bdev = dmc->cache_dev->bdev; + + /* + * Disallow cache creation if source and cache device + * belong to same device. + */ + + if (bdev->bd_contains == ssd_bdev->bd_contains) + return -EINVAL; + + /* + * Check if cache with same name exists. + */ + + if (eio_cache_lookup(dmc->cache_name)) + return -EEXIST; + + if (bdev == bdev->bd_contains) + wholedisk = 1; + + index = EIO_HASH_BDEV(bdev->bd_contains->bd_dev); + + down_read(&eio_ttc_lock[index]); + list_for_each_entry(dmc1, &eio_ttc_list[index], cachelist) { + if (dmc1->disk_dev->bdev->bd_contains != bdev->bd_contains) + continue; + + if ((wholedisk) || (dmc1->dev_info == EIO_DEV_WHOLE_DISK) || + (dmc1->disk_dev->bdev == bdev)) { + error = -EINVAL; + break; + } + } + up_read(&eio_ttc_lock[index]); + return error; +} + +/* Use mempool_alloc and free for io in sync_io as well */ +static void eio_dec_count(struct eio_context *io, int error) +{ + if (error) + io->error = error; + + if (atomic_dec_and_test(&io->count)) { + if (io->event) + complete(io->event); + else { + int err = io->error; + eio_notify_fn fn = io->callback; + void *context = io->context; + + mempool_free(io, _io_pool); + io = NULL; + fn(err, context); + } + } +} + +static void eio_endio(struct bio *bio, int error) +{ + struct eio_context *io; + + io = bio->bi_private; + EIO_ASSERT(io != NULL); + + bio_put(bio); + + eio_dec_count(io, error); +} + +static int eio_dispatch_io_pages(struct cache_c *dmc, + struct eio_io_region *where, int rw, + struct page **pagelist, struct eio_context *io, + int hddio, int num_vecs, int sync) +{ + struct bio *bio; + struct page *page; + unsigned long len; + unsigned offset; + int num_bvecs; + int remaining_bvecs = num_vecs; + int ret = 0; + int pindex = 0; + + sector_t remaining = where->count; + + do { + /* Verify that num_vecs should not cross the threshhold */ + /* Check how many max bvecs bdev supports */ + num_bvecs = + min_t(int, bio_get_nr_vecs(where->bdev), remaining_bvecs); + bio = bio_alloc(GFP_NOIO, num_bvecs); + bio->bi_bdev = where->bdev; + bio->bi_sector = where->sector + (where->count - remaining); + + /* Remap the start sector of partition */ + if (hddio) + bio->bi_sector += dmc->dev_start_sect; + bio->bi_rw |= rw; + bio->bi_end_io = eio_endio; + bio->bi_private = io; + + while (remaining) { + page = pagelist[pindex]; + len = + min_t(unsigned long, PAGE_SIZE, + to_bytes(remaining)); + offset = 0; + + if (!bio_add_page(bio, page, len, offset)) + break; + + remaining -= eio_to_sector(len); + pindex++; + remaining_bvecs--; + } + + atomic_inc(&io->count); + if (hddio) + dmc->origmfn(bdev_get_queue(bio->bi_bdev), bio); + + else + submit_bio(rw, bio); + + } while (remaining); + + EIO_ASSERT(remaining_bvecs == 0); + return ret; +} + +/* + * This function will dispatch the i/o. It also takes care of + * splitting the large I/O requets to smaller I/Os which may not + * fit into single bio. + */ + +static int eio_dispatch_io(struct cache_c *dmc, struct eio_io_region *where, + int rw, struct bio_vec *bvec, struct eio_context *io, + int hddio, int num_vecs, int sync) +{ + struct bio *bio; + struct page *page; + unsigned long len; + unsigned offset; + int num_bvecs; + int remaining_bvecs = num_vecs; + int ret = 0; + + sector_t remaining = where->count; + + do { + /* Verify that num_vecs should not cross the threshhold */ + /* Check how many max bvecs bdev supports */ + num_bvecs = + min_t(int, bio_get_nr_vecs(where->bdev), remaining_bvecs); + bio = bio_alloc(GFP_NOIO, num_bvecs); + bio->bi_bdev = where->bdev; + bio->bi_sector = where->sector + (where->count - remaining); + + /* Remap the start sector of partition */ + if (hddio) + bio->bi_sector += dmc->dev_start_sect; + bio->bi_rw |= rw; + bio->bi_end_io = eio_endio; + bio->bi_private = io; + + while (remaining) { + page = bvec->bv_page; + len = + min_t(unsigned long, bvec->bv_len, + to_bytes(remaining)); + offset = bvec->bv_offset; + + if (!bio_add_page(bio, page, len, offset)) + break; + + offset = 0; + remaining -= eio_to_sector(len); + bvec = bvec + 1; + remaining_bvecs--; + } + + atomic_inc(&io->count); + if (hddio) + dmc->origmfn(bdev_get_queue(bio->bi_bdev), bio); + else + submit_bio(rw, bio); + + } while (remaining); + + EIO_ASSERT(remaining_bvecs == 0); + return ret; +} + +static int eio_async_io(struct cache_c *dmc, struct eio_io_region *where, + int rw, struct eio_io_request *req) +{ + struct eio_context *io; + int err = 0; + + io = mempool_alloc(_io_pool, GFP_NOIO); + if (unlikely(io == NULL)) { + pr_err("eio_async_io: failed to allocate eio_context.\n"); + return -ENOMEM; + } + memset((char *)io, 0, sizeof(struct eio_context)); + + atomic_set(&io->count, 1); + io->callback = req->notify; + io->context = req->context; + io->event = NULL; + + switch (req->mtype) { + case EIO_BVECS: + err = + eio_dispatch_io(dmc, where, rw, req->dptr.pages, io, + req->hddio, req->num_bvecs, 0); + break; + + case EIO_PAGES: + err = + eio_dispatch_io_pages(dmc, where, rw, req->dptr.plist, io, + req->hddio, req->num_bvecs, 0); + break; + } + + /* Check if i/o submission has returned any error */ + if (unlikely(err)) { + /* Wait for any i/os which are submitted, to end. */ +retry: + if (atomic_read(&io->count) != 1) { + schedule_timeout(msecs_to_jiffies(1)); + goto retry; + } + + EIO_ASSERT(io != NULL); + mempool_free(io, _io_pool); + io = NULL; + return err; + } + + /* Drop the extra reference count here */ + eio_dec_count(io, err); + return err; +} + +static int eio_sync_io(struct cache_c *dmc, struct eio_io_region *where, + int rw, struct eio_io_request *req) +{ + int ret = 0; + struct eio_context io; + + DECLARE_COMPLETION_ONSTACK(wait); + + memset((char *)&io, 0, sizeof(io)); + + atomic_set(&io.count, 1); + io.event = &wait; + io.callback = NULL; + io.context = NULL; + + /* For synchronous I/Os pass SYNC */ + rw |= REQ_SYNC; + + switch (req->mtype) { + case EIO_BVECS: + ret = eio_dispatch_io(dmc, where, rw, req->dptr.pages, + &io, req->hddio, req->num_bvecs, 1); + break; + case EIO_PAGES: + ret = eio_dispatch_io_pages(dmc, where, rw, req->dptr.plist, + &io, req->hddio, req->num_bvecs, 1); + break; + } + + /* Check if i/o submission has returned any error */ + if (unlikely(ret)) { + /* Wait for any i/os which are submitted, to end. */ +retry: + if (atomic_read(&(io.count)) != 1) { + schedule_timeout(msecs_to_jiffies(1)); + goto retry; + } + + return ret; + } + + /* Drop extra reference count here */ + eio_dec_count(&io, ret); + wait_for_completion(&wait); + + if (io.error) + ret = io.error; + + return ret; +} + +int eio_do_io(struct cache_c *dmc, struct eio_io_region *where, int rw, + struct eio_io_request *io_req) +{ + if (!io_req->notify) + return eio_sync_io(dmc, where, rw, io_req); + + return eio_async_io(dmc, where, rw, io_req); +} + +void eio_process_zero_size_bio(struct cache_c *dmc, struct bio *origbio) +{ + unsigned long rw_flags = 0; + + /* Extract bio flags from original bio */ + rw_flags = origbio->bi_rw; + + EIO_ASSERT(origbio->bi_size == 0); + EIO_ASSERT(rw_flags != 0); + + eio_issue_empty_barrier_flush(dmc->cache_dev->bdev, NULL, + EIO_SSD_DEVICE, NULL, rw_flags); + eio_issue_empty_barrier_flush(dmc->disk_dev->bdev, origbio, + EIO_HDD_DEVICE, dmc->origmfn, rw_flags); +} + +static void eio_bio_end_empty_barrier(struct bio *bio, int err) +{ + if (bio->bi_private) + bio_endio(bio->bi_private, err); + bio_put(bio); + return; +} + +static void eio_issue_empty_barrier_flush(struct block_device *bdev, + struct bio *orig_bio, int device, + make_request_fn *origmfn, + int rw_flags) +{ + struct bio *bio; + + bio = bio_alloc(GFP_KERNEL, 0); + if (!bio) + if (orig_bio) + bio_endio(orig_bio, -ENOMEM); + bio->bi_end_io = eio_bio_end_empty_barrier; + bio->bi_private = orig_bio; + bio->bi_bdev = bdev; + bio->bi_rw |= rw_flags; + + bio_get(bio); + if (device == EIO_HDD_DEVICE) + origmfn(bdev_get_queue(bio->bi_bdev), bio); + + else + submit_bio(0, bio); + bio_put(bio); + return; +} + +static int eio_finish_nrdirty(struct cache_c *dmc) +{ + int index; + int ret = 0; + int retry_count; + + /* + * Due to any transient errors, finish_nr_dirty may not drop + * to zero. Retry the clean operations for FINISH_NRDIRTY_RETRY_COUNT. + */ + retry_count = FINISH_NRDIRTY_RETRY_COUNT; + + index = EIO_HASH_BDEV(dmc->disk_dev->bdev->bd_contains->bd_dev); + down_write(&eio_ttc_lock[index]); + + /* Wait for the in-flight I/Os to drain out */ + while (atomic64_read(&dmc->nr_ios) != 0) { + pr_debug("finish_nrdirty: Draining I/O inflight\n"); + schedule_timeout(msecs_to_jiffies(1)); + } + EIO_ASSERT(!(dmc->sysctl_active.do_clean & EIO_CLEAN_START)); + + dmc->sysctl_active.do_clean |= EIO_CLEAN_KEEP | EIO_CLEAN_START; + up_write(&eio_ttc_lock[index]); + + /* + * In the process of cleaning CACHE if CACHE turns to FAILED state, + * its a severe error. + */ + do { + if (unlikely(CACHE_FAILED_IS_SET(dmc))) { + pr_err + ("finish_nrdirty: CACHE \"%s\" is in FAILED state.", + dmc->cache_name); + ret = -ENODEV; + break; + } + + if (!dmc->sysctl_active.fast_remove) + eio_clean_all(dmc); + } while (!dmc->sysctl_active.fast_remove && + (atomic64_read(&dmc->nr_dirty) > 0) + && (!(dmc->cache_flags & CACHE_FLAGS_SHUTDOWN_INPROG))); + dmc->sysctl_active.do_clean &= ~EIO_CLEAN_START; + + /* + * If all retry_count exhausted and nr_dirty is still not zero. + * Return error. + */ + if (((dmc->cache_flags & CACHE_FLAGS_SHUTDOWN_INPROG) || + (retry_count == 0)) && (atomic64_read(&dmc->nr_dirty) > 0)) + ret = -EINVAL; + if (ret) + pr_err + ("finish_nrdirty: Failed to finish %llu dirty blocks for cache \"%s\".", + (unsigned long long)atomic64_read(&dmc->nr_dirty), dmc->cache_name); + + return ret; +} + +int eio_cache_edit(char *cache_name, u_int32_t mode, u_int32_t policy) +{ + int error = 0; + int index; + struct cache_c *dmc; + uint32_t old_time_thresh = 0; + int restart_async_task = 0; + int ret; + + EIO_ASSERT((mode != 0) || (policy != 0)); + + dmc = eio_cache_lookup(cache_name); + if (NULL == dmc) { + pr_err("cache_edit: cache %s do not exist", cache_name); + return -EINVAL; + } + + if ((dmc->mode == mode) && (dmc->req_policy == policy)) + return 0; + + if (unlikely(CACHE_FAILED_IS_SET(dmc)) || + unlikely(CACHE_DEGRADED_IS_SET(dmc))) { + pr_err("cache_edit: Cannot proceed with edit on cache \"%s\"" \ + ". Cache is in failed or degraded state.", + dmc->cache_name); + return -EINVAL; + } + + spin_lock_irqsave(&dmc->cache_spin_lock, dmc->cache_spin_lock_flags); + if (dmc->cache_flags & CACHE_FLAGS_SHUTDOWN_INPROG) { + pr_err("cache_edit: system shutdown in progress, cannot edit" \ + " cache %s", cache_name); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return -EINVAL; + } + if (dmc->cache_flags & CACHE_FLAGS_MOD_INPROG) { + pr_err("cache_edit: simultaneous edit/delete operation on " \ + "cache %s is not permitted", cache_name); + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + return -EINVAL; + } + dmc->cache_flags |= CACHE_FLAGS_MOD_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + old_time_thresh = dmc->sysctl_active.time_based_clean_interval; + + if (dmc->mode == CACHE_MODE_WB) { + if (CACHE_FAILED_IS_SET(dmc)) { + pr_err + ("cache_edit: Can not proceed with edit for Failed cache \"%s\".", + dmc->cache_name); + error = -EINVAL; + goto out; + } + eio_stop_async_tasks(dmc); + restart_async_task = 1; + } + + /* Wait for nr_dirty to drop to zero */ + if (dmc->mode == CACHE_MODE_WB && mode != CACHE_MODE_WB) { + if (CACHE_FAILED_IS_SET(dmc)) { + pr_err + ("cache_edit: Can not proceed with edit for Failed cache \"%s\".", + dmc->cache_name); + error = -EINVAL; + goto out; + } + + error = eio_finish_nrdirty(dmc); + /* This error can mostly occur due to Device removal */ + if (unlikely(error)) { + pr_err + ("cache_edit: nr_dirty FAILED to finish for cache \"%s\".", + dmc->cache_name); + goto out; + } + EIO_ASSERT((dmc->sysctl_active.do_clean & EIO_CLEAN_KEEP) && + !(dmc->sysctl_active.do_clean & EIO_CLEAN_START)); + EIO_ASSERT(dmc->sysctl_active.fast_remove || + (atomic64_read(&dmc->nr_dirty) == 0)); + } + + index = EIO_HASH_BDEV(dmc->disk_dev->bdev->bd_contains->bd_dev); + down_write(&eio_ttc_lock[index]); + + /* Wait for the in-flight I/Os to drain out */ + while (atomic64_read(&dmc->nr_ios) != 0) { + pr_debug("cache_edit: Draining I/O inflight\n"); + schedule_timeout(msecs_to_jiffies(1)); + } + + pr_debug("cache_edit: Blocking application I/O\n"); + + EIO_ASSERT(atomic64_read(&dmc->nr_ios) == 0); + + /* policy change */ + if ((policy != 0) && (policy != dmc->req_policy)) { + error = eio_policy_switch(dmc, policy); + if (error) { + up_write(&eio_ttc_lock[index]); + goto out; + } + } + + /* mode change */ + if ((mode != 0) && (mode != dmc->mode)) { + error = eio_mode_switch(dmc, mode); + if (error) { + up_write(&eio_ttc_lock[index]); + goto out; + } + } + + dmc->sysctl_active.time_based_clean_interval = old_time_thresh; + /* write updated superblock */ + error = eio_sb_store(dmc); + if (error) { + /* XXX: In case of error put the cache in degraded mode. */ + pr_err("eio_cache_edit: superblock update failed(error %d)", + error); + goto out; + } + + eio_procfs_dtr(dmc); + eio_procfs_ctr(dmc); + + up_write(&eio_ttc_lock[index]); + +out: + dmc->sysctl_active.time_based_clean_interval = old_time_thresh; + + /* + * Resetting EIO_CLEAN_START and EIO_CLEAN_KEEP flags. + * EIO_CLEAN_START flag should be restored if eio_stop_async_tasks() + * is not called in future. + */ + + dmc->sysctl_active.do_clean &= ~(EIO_CLEAN_START | EIO_CLEAN_KEEP); + + /* Restart async-task for "WB" cache. */ + if ((dmc->mode == CACHE_MODE_WB) && (restart_async_task == 1)) { + pr_debug("cache_edit: Restarting the clean_thread.\n"); + EIO_ASSERT(dmc->clean_thread == NULL); + ret = eio_start_clean_thread(dmc); + if (ret) { + error = ret; + pr_err + ("cache_edit: Failed to restart async tasks. error=%d.\n", + ret); + } + if (dmc->sysctl_active.time_based_clean_interval && + atomic64_read(&dmc->nr_dirty)) { + schedule_delayed_work(&dmc->clean_aged_sets_work, + dmc-> + sysctl_active.time_based_clean_interval + * 60 * HZ); + dmc->is_clean_aged_sets_sched = 1; + } + } + spin_lock_irqsave(&dmc->cache_spin_lock, dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_MOD_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + pr_debug("eio_cache_edit: Allowing application I/O\n"); + return error; +} + +static int eio_mode_switch(struct cache_c *dmc, u_int32_t mode) +{ + int error = 0; + u_int32_t orig_mode; + + EIO_ASSERT(dmc->mode != mode); + pr_debug("eio_mode_switch: mode switch from %u to %u\n", + dmc->mode, mode); + + if (mode == CACHE_MODE_WB) { + orig_mode = dmc->mode; + dmc->mode = mode; + + error = eio_allocate_wb_resources(dmc); + if (error) { + dmc->mode = orig_mode; + goto out; + } + } else if (dmc->mode == CACHE_MODE_WB) { + eio_free_wb_resources(dmc); + dmc->mode = mode; + } else { /* (RO -> WT) or (WT -> RO) */ + EIO_ASSERT(((dmc->mode == CACHE_MODE_RO) && (mode == CACHE_MODE_WT)) + || ((dmc->mode == CACHE_MODE_WT) && + (mode == CACHE_MODE_RO))); + dmc->mode = mode; + } + +out: + if (error) + pr_err("mode_switch: Failed to switch mode, error: %d\n", + error); + return error; +} + +/* + * XXX: Error handling. + * In case of error put the cache in degraded mode. + */ + +static int eio_policy_switch(struct cache_c *dmc, u_int32_t policy) +{ + int error = -EINVAL; + struct eio_policy *old_policy_ops; + + EIO_ASSERT(dmc->req_policy != policy); + old_policy_ops = dmc->policy_ops; + + error = eio_policy_init(dmc); + if (error) + goto out; + + error = eio_repl_blk_init(dmc->policy_ops); + if (error) { + error = -ENOMEM; + pr_err ("eio_policy_swtich: Unable to allocate memory for policy cache block"); + goto out; + } + + error = eio_repl_sets_init(dmc->policy_ops); + if (error) { + error = -ENOMEM; + pr_err ("eio_policy_switch: Failed to allocate memory for cache policy"); + goto out; + } + + eio_policy_lru_pushblks(dmc->policy_ops); + dmc->req_policy = policy; + return 0; + +out: + if (dmc->policy_ops != old_policy_ops) + eio_policy_free(dmc); + dmc->policy_ops = old_policy_ops; + return error; +} + +void eio_free_wb_pages(struct page **pages, int allocated) +{ + /* Verify that allocated is never 0 or less that zero. */ + if (allocated <= 0) + return; + + do + put_page(pages[--allocated]); + while (allocated); + + *pages = NULL; +} + +void eio_free_wb_bvecs(struct bio_vec *bvec, int allocated, int blksize) +{ + int i; + + if (allocated <= 0) + return; + + for (i = 0; i < allocated; i++) { + switch (blksize) { + case BLKSIZE_2K: + /* + * For 2k blocksize, each page is shared between two + * bio_vecs. Hence make sure to put_page only for even + * indexes. + */ + if (((i % 2) == 0) && bvec[i].bv_page) { + put_page(bvec[i].bv_page); + bvec[i].bv_page = NULL; + continue; + } + + /* For odd index page should already have been freed. */ + if ((i % 2)) + bvec[i].bv_page = NULL; + + continue; + + case BLKSIZE_4K: + case BLKSIZE_8K: + if (bvec[i].bv_page) { + put_page(bvec[i].bv_page); + bvec[i].bv_page = NULL; + } + + continue; + } + } +} + +/* + * This function allocates pages to array of bvecs allocated by caller. + * It has special handling of blocksize of 2k where single page is + * shared between two bio_vecs. + */ + +int eio_alloc_wb_bvecs(struct bio_vec *bvec, int max, int blksize) +{ + int i, ret; + struct bio_vec *iovec; + struct page *page; + + ret = 0; + iovec = bvec; + page = NULL; + + for (i = 0; i < max; i++) { + switch (blksize) { + case BLKSIZE_2K: + /* + * In case of 2k blocksize, two biovecs will be sharing + * same page address. This is handled below. + */ + + if ((i % 2) == 0) { + /* Allocate page only for even bio vector */ + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (unlikely(!page)) { + pr_err + ("eio_alloc_wb_bvecs: System memory too low.\n"); + goto err; + } + iovec[i].bv_page = page; + iovec[i].bv_len = to_bytes(blksize); + iovec[i].bv_offset = 0; + } else { + /* Let the odd biovec share page allocated earlier. */ + EIO_ASSERT(page != NULL); + iovec[i].bv_page = page; + iovec[i].bv_len = to_bytes(blksize); + iovec[i].bv_offset = + PAGE_SIZE - to_bytes(blksize); + + /* Mark page NULL here as it is not required anymore. */ + page = NULL; + } + + continue; + + case BLKSIZE_4K: + case BLKSIZE_8K: + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (unlikely(!page)) { + pr_err("eio_alloc_wb_bvecs:" \ + " System memory too low.\n"); + goto err; + } + iovec[i].bv_page = page; + iovec[i].bv_offset = 0; + iovec[i].bv_len = PAGE_SIZE; + + page = NULL; + continue; + } + } + + goto out; + +err: + if (i != max) { + if (i > 0) + eio_free_wb_bvecs(bvec, i, blksize); + ret = -ENOMEM; + } + +out: + return ret; +} + +int eio_alloc_wb_pages(struct page **pages, int max) +{ + int i, ret = 0; + struct page *page; + + for (i = 0; i < max; i++) { + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (unlikely(!page)) { + pr_err("alloc_wb_pages: System memory too low.\n"); + break; + } + pages[i] = page; + } + + if (i != max) { + if (i > 0) + eio_free_wb_pages(pages, i); + ret = -ENOMEM; + goto out; + } + +out: + return ret; +} + +/* + **************************************************************************** + * struct bio_vec *eio_alloc_pages(int max_pages, int *page_count) + * dmc : cache object + * pages : bio_vec to be allocated for synchronous I/O. + * page_count : total number of pages allocated. + **************************************************************************** + * + * This function allocates pages capped to minimum of + * MD_MAX_NR_PAGES OR maximun number of pages supported by + * block device. + * This is to ensure that the pages allocated should fit + * into single bio request. + */ + +struct bio_vec *eio_alloc_pages(u_int32_t max_pages, int *page_count) +{ + int pcount, i; + struct bio_vec *pages; + int nr_pages; + + /* + * Find out no. of pages supported by block device max capped to + * MD_MAX_NR_PAGES; + */ + nr_pages = min_t(u_int32_t, max_pages, MD_MAX_NR_PAGES); + + pages = kzalloc(nr_pages * sizeof(struct bio_vec), GFP_NOIO); + if (unlikely(!pages)) { + pr_err("eio_alloc_pages: System memory too low.\n"); + return NULL; + } + + pcount = 0; + for (i = 0; i < nr_pages; i++) { + pages[i].bv_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (unlikely(!pages[i].bv_page)) { + pr_err("eio_alloc_pages: System memory too low.\n"); + break; + } else { + pages[i].bv_len = PAGE_SIZE; + pages[i].bv_offset = 0; + pcount++; + } + } + + if (pcount == 0) { + pr_err("Single page allocation failed. System memory too low."); + kfree(pages); + return NULL; + } + + /* following can be commented out later... + * we may have less pages allocated. + */ + EIO_ASSERT(pcount == nr_pages); + + /* Set the return values here */ + *page_count = pcount; + return pages; +} + +/* + * As part of reboot handling, stop all activies and mark the devices as + * read only. + */ + +int eio_reboot_handling(void) +{ + struct cache_c *dmc, *tempdmc = NULL; + int i, error; + uint32_t old_time_thresh; + + if (eio_reboot_notified == EIO_REBOOT_HANDLING_DONE) + return 0; + + (void)wait_on_bit_lock((void *)&eio_control->synch_flags, + EIO_HANDLE_REBOOT, eio_wait_schedule, + TASK_UNINTERRUPTIBLE); + if (eio_reboot_notified == EIO_REBOOT_HANDLING_DONE) { + clear_bit(EIO_HANDLE_REBOOT, (void *)&eio_control->synch_flags); + smp_mb__after_clear_bit(); + wake_up_bit((void *)&eio_control->synch_flags, + EIO_HANDLE_REBOOT); + return 0; + } + EIO_ASSERT(eio_reboot_notified == 0); + eio_reboot_notified = EIO_REBOOT_HANDLING_INPROG; + + for (i = 0; i < EIO_HASHTBL_SIZE; i++) { + down_write(&eio_ttc_lock[i]); + list_for_each_entry(dmc, &eio_ttc_list[i], cachelist) { + + kfree(tempdmc); + tempdmc = NULL; + if (unlikely(CACHE_FAILED_IS_SET(dmc)) || + unlikely(CACHE_DEGRADED_IS_SET(dmc))) { + pr_err + ("Cache \"%s\" is in failed/degraded " \ + "mode. Cannot mark cache read only.\n", + dmc->cache_name); + continue; + } + + while (atomic64_read(&dmc->nr_ios) != 0) { + pr_debug("rdonly: Draining I/O inflight\n"); + schedule_timeout(msecs_to_jiffies(10)); + } + + EIO_ASSERT(atomic64_read(&dmc->nr_ios) == 0); + EIO_ASSERT(dmc->cache_rdonly == 0); + + /* + * Shutdown processing has the highest priority. + * Stop all ongoing activities. + */ + + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + EIO_ASSERT(! + (dmc-> + cache_flags & CACHE_FLAGS_SHUTDOWN_INPROG)); + dmc->cache_flags |= CACHE_FLAGS_SHUTDOWN_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + + /* + * Wait for ongoing edit/delete to complete. + */ + + while (dmc->cache_flags & CACHE_FLAGS_MOD_INPROG) { + up_write(&eio_ttc_lock[i]); + schedule_timeout(msecs_to_jiffies(1)); + down_write(&eio_ttc_lock[i]); + } + if (dmc->cache_flags & CACHE_FLAGS_DELETED) { + /* + * Cache got deleted. Free the dmc. + */ + + tempdmc = dmc; + continue; + } + old_time_thresh = + dmc->sysctl_active.time_based_clean_interval; + eio_stop_async_tasks(dmc); + dmc->sysctl_active.time_based_clean_interval = + old_time_thresh; + + dmc->cache_rdonly = 1; + pr_info("Cache \"%s\" marked read only\n", + dmc->cache_name); + up_write(&eio_ttc_lock[i]); + + if (dmc->cold_boot && atomic64_read(&dmc->nr_dirty) && + !eio_force_warm_boot) { + pr_info + ("Cold boot set for cache %s: Draining dirty blocks: %llu", + dmc->cache_name, + (unsigned long long)atomic64_read(&dmc->nr_dirty)); + eio_clean_for_reboot(dmc); + } + + error = eio_md_store(dmc); + if (error) + pr_err("Cannot mark cache \"%s\" read only\n", + dmc->cache_name); + + spin_lock_irqsave(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + dmc->cache_flags &= ~CACHE_FLAGS_SHUTDOWN_INPROG; + spin_unlock_irqrestore(&dmc->cache_spin_lock, + dmc->cache_spin_lock_flags); + + down_write(&eio_ttc_lock[i]); + } + kfree(tempdmc); + tempdmc = NULL; + up_write(&eio_ttc_lock[i]); + } + + eio_reboot_notified = EIO_REBOOT_HANDLING_DONE; + clear_bit(EIO_HANDLE_REBOOT, (void *)&eio_control->synch_flags); + smp_mb__after_clear_bit(); + wake_up_bit((void *)&eio_control->synch_flags, EIO_HANDLE_REBOOT); + return 0; +} + +static int eio_overlap_split_bio(struct request_queue *q, struct bio *bio) +{ + int i, nbios; + void **bioptr; + sector_t snum; + struct bio_container *bc; + unsigned bvec_idx; + unsigned bvec_consumed; + + nbios = bio->bi_size >> SECTOR_SHIFT; + snum = bio->bi_sector; + + bioptr = kmalloc(nbios * (sizeof(void *)), GFP_KERNEL); + if (!bioptr) { + bio_endio(bio, -ENOMEM); + return 0; + } + bc = kmalloc(sizeof(struct bio_container), GFP_NOWAIT); + if (!bc) { + bio_endio(bio, -ENOMEM); + kfree(bioptr); + return 0; + } + + atomic_set(&bc->bc_holdcount, nbios); + bc->bc_bio = bio; + bc->bc_error = 0; + + bvec_idx = bio->bi_idx; + bvec_consumed = 0; + for (i = 0; i < nbios; i++) { + bioptr[i] = + eio_split_new_bio(bio, bc, &bvec_idx, + &bvec_consumed, snum); + if (!bioptr[i]) + break; + snum++; + } + + /* Error: cleanup */ + if (i < nbios) { + for (i--; i >= 0; i--) + bio_put(bioptr[i]); + bio_endio(bio, -ENOMEM); + kfree(bc); + goto out; + } + + for (i = 0; i < nbios; i++) + eio_make_request_fn(q, bioptr[i]); + +out: + kfree(bioptr); + return 0; +} + +static struct bio *eio_split_new_bio(struct bio *bio, struct bio_container *bc, + unsigned *bvec_idx, + unsigned *bvec_consumed, sector_t snum) +{ + struct bio *cbio; + unsigned iosize = 1 << SECTOR_SHIFT; + + cbio = bio_alloc(GFP_NOIO, 1); + if (!cbio) + return NULL; + + EIO_ASSERT(bio->bi_io_vec[*bvec_idx].bv_len >= iosize); + + if (bio->bi_io_vec[*bvec_idx].bv_len <= *bvec_consumed) { + EIO_ASSERT(bio->bi_io_vec[*bvec_idx].bv_len == *bvec_consumed); + (*bvec_idx)++; + EIO_ASSERT(bio->bi_vcnt > *bvec_idx); + *bvec_consumed = 0; + } + + cbio->bi_io_vec[0].bv_page = bio->bi_io_vec[*bvec_idx].bv_page; + cbio->bi_io_vec[0].bv_offset = + bio->bi_io_vec[*bvec_idx].bv_offset + *bvec_consumed; + cbio->bi_io_vec[0].bv_len = iosize; + *bvec_consumed += iosize; + + cbio->bi_sector = snum; + cbio->bi_size = iosize; + cbio->bi_bdev = bio->bi_bdev; + cbio->bi_rw = bio->bi_rw; + cbio->bi_vcnt = 1; + cbio->bi_idx = 0; + cbio->bi_end_io = eio_split_endio; + cbio->bi_private = bc; + return cbio; +} + +static void eio_split_endio(struct bio *bio, int error) +{ + struct bio_container *bc = bio->bi_private; + + if (error) + bc->bc_error = error; + bio_put(bio); + if (atomic_dec_and_test(&bc->bc_holdcount)) { + bio_endio(bc->bc_bio, bc->bc_error); + kfree(bc); + } + return; +} diff -Nur linux-3.10.30/drivers/block/enhanceio/eio_ttc.h linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_ttc.h --- linux-3.10.30/drivers/block/enhanceio/eio_ttc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/block/enhanceio/eio_ttc.h 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2012 STEC, Inc. All rights not specifically granted + * under a license included herein are reserved. + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + */ + +#ifndef EIO_TTC_H +#define EIO_TTC_H + +#ifdef __KERNEL__ +#include +#define curthread get_current() +#else +#include +#endif /* __KERNEL__ */ + +static inline bool bio_rw_flagged(struct bio *bio, int flag) +{ + return (bio->bi_rw & flag) != 0; +} + +/* + * Whether the cached (source) device is a partition or a whole device. + * dmc->dev_info stores this info. + */ +enum eio_io_mem_type { + EIO_BVECS, /* bio vectors */ + EIO_PAGES, /* array of pages */ +}; + +struct eio_io_request { + enum eio_io_mem_type mtype; + + union { + struct bio_vec *pages; + struct page **plist; + } dptr; + + unsigned num_bvecs; + eio_notify_fn notify; + void *context; + unsigned hddio; +}; + +struct eio_context { + atomic_t count; + int error; + struct completion *event; + eio_notify_fn callback; + void *context; +}; + +int eio_do_io(struct cache_c *dmc, struct eio_io_region *where, int rw, + struct eio_io_request *io_req); + +enum eio_device { + EIO_HDD_DEVICE = 1, + EIO_SSD_DEVICE, +}; + +enum eio_dev_info { + EIO_DEV_PARTITION = 1, + EIO_DEV_WHOLE_DISK +}; + +enum eio_cache_state { + DMC_TTC_INITIALIZING = 1, + DMC_TTC_READY, + DMC_TTC_IO_FREEZE, + DMC_TTC_UNINITIALIZING, + DMC_TTC_UNINITIALIZED +}; + +#ifdef __KERNEL__ + +#define EIO_HASHTBL_SIZE 1024 + +/* + * In case of i/o errors while eio_clean_all, retry for + * finish_nrdirty_retry count. + */ +#define FINISH_NRDIRTY_RETRY_COUNT 2 + +#define EIO_HASH_BDEV(dev) \ + ((MAJOR(dev) * EIO_MAGIC + MINOR(dev)) % EIO_HASHTBL_SIZE) + +/* + * Reboot status flags. + */ + +#define EIO_REBOOT_HANDLING_INPROG 0x01 +#define EIO_REBOOT_HANDLING_DONE 0x02 + +/* + * kernel function prototypes. + */ + +extern int eio_create_misc_device(void); +extern int eio_delete_misc_device(void); + +extern int eio_ttc_get_device(const char *, fmode_t, struct eio_bdev **); +extern void eio_ttc_put_device(struct eio_bdev **); + +extern struct cache_c *eio_cache_lookup(char *); +extern int eio_ttc_activate(struct cache_c *); +extern int eio_ttc_deactivate(struct cache_c *, int); +extern void eio_ttc_init(void); + +extern int eio_cache_create(struct cache_rec_short *); +extern int eio_cache_delete(char *, int); +extern uint64_t eio_get_cache_count(void); +extern int eio_get_cache_list(unsigned long *); + +extern int eio_handle_ssd_message(char *cache_name, char *ssd_name, + enum dev_notifier note); + +int eio_do_preliminary_checks(struct cache_c *); + +extern int eio_allocate_wb_resources(struct cache_c *); +extern void eio_free_wb_resources(struct cache_c *); + +extern int eio_cache_edit(char *, u_int32_t, u_int32_t); + +extern void eio_stop_async_tasks(struct cache_c *dmc); +extern int eio_start_clean_thread(struct cache_c *dmc); + +extern int eio_policy_init(struct cache_c *); +extern void eio_policy_free(struct cache_c *); +extern int eio_alloc_wb_pages(struct page **pages, int max); +extern void eio_free_wb_pages(struct page **pages, int allocated); +extern int eio_alloc_wb_bvecs(struct bio_vec *bvec, int max, int blksize); +extern void eio_free_wb_bvecs(struct bio_vec *bvec, int allocated, int blksize); +extern struct bio_vec *eio_alloc_pages(u_int32_t max_pages, int *page_count); +extern int eio_md_store(struct cache_c *); +extern int eio_reboot_handling(void); +extern void eio_process_zero_size_bio(struct cache_c *dmc, struct bio *origbio); +extern long eio_ioctl(struct file *filp, unsigned cmd, unsigned long arg); +extern long eio_compact_ioctl(struct file *filp, unsigned cmd, + unsigned long arg); +#endif /* __KERNEL__ */ + +#endif /* EIO_TTC_H */ diff -Nur linux-3.10.30/drivers/bus/Kconfig linux-3.10.30-cubox-i/drivers/bus/Kconfig --- linux-3.10.30/drivers/bus/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/bus/Kconfig 2014-03-08 20:33:28.000000000 +0100 @@ -4,6 +4,15 @@ menu "Bus devices" +config IMX_WEIM + bool "Freescale EIM DRIVER" + depends on ARCH_MXC + help + Driver for i.MX6 WEIM controller. + The WEIM(Wireless External Interface Module) works like a bus. + You can attach many different devices on it, such as NOR, onenand. + But now, we only support the Parallel NOR. + config MVEBU_MBUS bool depends on PLAT_ORION @@ -26,4 +35,11 @@ help Driver to enable OMAP interconnect error handling driver. + +config ARM_CCI + bool "ARM CCI driver support" + depends on ARM + help + Driver supporting the CCI cache coherent interconnect for ARM + platforms. endmenu diff -Nur linux-3.10.30/drivers/bus/Makefile linux-3.10.30-cubox-i/drivers/bus/Makefile --- linux-3.10.30/drivers/bus/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/bus/Makefile 2014-03-08 20:33:28.000000000 +0100 @@ -2,8 +2,11 @@ # Makefile for the bus drivers. # +obj-$(CONFIG_IMX_WEIM) += imx-weim.o obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o # Interconnect bus driver for OMAP SoCs. obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o +# CCI cache coherent interconnect for ARM platforms +obj-$(CONFIG_ARM_CCI) += arm-cci.o diff -Nur linux-3.10.30/drivers/bus/arm-cci.c linux-3.10.30-cubox-i/drivers/bus/arm-cci.c --- linux-3.10.30/drivers/bus/arm-cci.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/bus/arm-cci.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,945 @@ +/* + * CCI cache coherent interconnect driver + * + * Copyright (C) 2013 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DRIVER_NAME "CCI" + +#define CCI_PORT_CTRL 0x0 +#define CCI_CTRL_STATUS 0xc + +#define CCI_ENABLE_SNOOP_REQ 0x1 +#define CCI_ENABLE_DVM_REQ 0x2 +#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ) + +struct cci_nb_ports { + unsigned int nb_ace; + unsigned int nb_ace_lite; +}; + +enum cci_ace_port_type { + ACE_INVALID_PORT = 0x0, + ACE_PORT, + ACE_LITE_PORT, +}; + +struct cci_ace_port { + void __iomem *base; + unsigned long phys; + enum cci_ace_port_type type; + struct device_node *dn; +}; + +static struct cci_ace_port *ports; +static unsigned int nb_cci_ports; + +static void __iomem *cci_ctrl_base; +static unsigned long cci_ctrl_phys; + +#ifdef CONFIG_HW_PERF_EVENTS + +static void __iomem *cci_pmu_base; + +#define CCI400_PMCR 0x0100 + +#define CCI400_PMU_CYCLE_CNTR_BASE 0x0000 +#define CCI400_PMU_CNTR_BASE(idx) (CCI400_PMU_CYCLE_CNTR_BASE + (idx) * 0x1000) + +#define CCI400_PMCR_CEN 0x00000001 +#define CCI400_PMCR_RST 0x00000002 +#define CCI400_PMCR_CCR 0x00000004 +#define CCI400_PMCR_CCD 0x00000008 +#define CCI400_PMCR_EX 0x00000010 +#define CCI400_PMCR_DP 0x00000020 +#define CCI400_PMCR_NCNT_MASK 0x0000F800 +#define CCI400_PMCR_NCNT_SHIFT 11 + +#define CCI400_PMU_EVT_SEL 0x000 +#define CCI400_PMU_CNTR 0x004 +#define CCI400_PMU_CNTR_CTRL 0x008 +#define CCI400_PMU_OVERFLOW 0x00C + +#define CCI400_PMU_OVERFLOW_FLAG 1 + +enum cci400_perf_events { + CCI400_PMU_CYCLES = 0xFF +}; + +#define CCI400_PMU_EVENT_MASK 0xff +#define CCI400_PMU_EVENT_SOURCE(event) ((event >> 5) & 0x7) +#define CCI400_PMU_EVENT_CODE(event) (event & 0x1f) + +#define CCI400_PMU_EVENT_SOURCE_S0 0 +#define CCI400_PMU_EVENT_SOURCE_S4 4 +#define CCI400_PMU_EVENT_SOURCE_M0 5 +#define CCI400_PMU_EVENT_SOURCE_M2 7 + +#define CCI400_PMU_EVENT_SLAVE_MIN 0x0 +#define CCI400_PMU_EVENT_SLAVE_MAX 0x13 + +#define CCI400_PMU_EVENT_MASTER_MIN 0x14 +#define CCI400_PMU_EVENT_MASTER_MAX 0x1A + +#define CCI400_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */ + +#define CCI400_PMU_CYCLE_COUNTER_IDX 0 +#define CCI400_PMU_COUNTER0_IDX 1 +#define CCI400_PMU_COUNTER_LAST(cci_pmu) (CCI400_PMU_CYCLE_COUNTER_IDX + cci_pmu->num_events - 1) + + +static struct perf_event *events[CCI400_PMU_MAX_HW_EVENTS]; +static unsigned long used_mask[BITS_TO_LONGS(CCI400_PMU_MAX_HW_EVENTS)]; +static struct pmu_hw_events cci_hw_events = { + .events = events, + .used_mask = used_mask, +}; + +static int cci_pmu_validate_hw_event(u8 hw_event) +{ + u8 ev_source = CCI400_PMU_EVENT_SOURCE(hw_event); + u8 ev_code = CCI400_PMU_EVENT_CODE(hw_event); + + if (ev_source <= CCI400_PMU_EVENT_SOURCE_S4 && + ev_code <= CCI400_PMU_EVENT_SLAVE_MAX) + return hw_event; + else if (CCI400_PMU_EVENT_SOURCE_M0 <= ev_source && + ev_source <= CCI400_PMU_EVENT_SOURCE_M2 && + CCI400_PMU_EVENT_MASTER_MIN <= ev_code && + ev_code <= CCI400_PMU_EVENT_MASTER_MAX) + return hw_event; + + return -EINVAL; +} + +static inline int cci_pmu_counter_is_valid(struct arm_pmu *cci_pmu, int idx) +{ + return CCI400_PMU_CYCLE_COUNTER_IDX <= idx && + idx <= CCI400_PMU_COUNTER_LAST(cci_pmu); +} + +static inline u32 cci_pmu_read_register(int idx, unsigned int offset) +{ + return readl_relaxed(cci_pmu_base + CCI400_PMU_CNTR_BASE(idx) + offset); +} + +static inline void cci_pmu_write_register(u32 value, int idx, unsigned int offset) +{ + return writel_relaxed(value, cci_pmu_base + CCI400_PMU_CNTR_BASE(idx) + offset); +} + +static inline void cci_pmu_disable_counter(int idx) +{ + cci_pmu_write_register(0, idx, CCI400_PMU_CNTR_CTRL); +} + +static inline void cci_pmu_enable_counter(int idx) +{ + cci_pmu_write_register(1, idx, CCI400_PMU_CNTR_CTRL); +} + +static inline void cci_pmu_select_event(int idx, unsigned long event) +{ + event &= CCI400_PMU_EVENT_MASK; + cci_pmu_write_register(event, idx, CCI400_PMU_EVT_SEL); +} + +static u32 cci_pmu_get_max_counters(void) +{ + u32 n_cnts = (readl_relaxed(cci_ctrl_base + CCI400_PMCR) & + CCI400_PMCR_NCNT_MASK) >> CCI400_PMCR_NCNT_SHIFT; + + /* add 1 for cycle counter */ + return n_cnts + 1; +} + +static struct pmu_hw_events *cci_pmu_get_hw_events(void) +{ + return &cci_hw_events; +} + +static int cci_pmu_get_event_idx(struct pmu_hw_events *hw, struct perf_event *event) +{ + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct hw_perf_event *hw_event = &event->hw; + unsigned long cci_event = hw_event->config_base & CCI400_PMU_EVENT_MASK; + int idx; + + if (cci_event == CCI400_PMU_CYCLES) { + if (test_and_set_bit(CCI400_PMU_CYCLE_COUNTER_IDX, hw->used_mask)) + return -EAGAIN; + + return CCI400_PMU_CYCLE_COUNTER_IDX; + } + + for (idx = CCI400_PMU_COUNTER0_IDX; idx <= CCI400_PMU_COUNTER_LAST(cci_pmu); ++idx) { + if (!test_and_set_bit(idx, hw->used_mask)) + return idx; + } + + /* No counters available */ + return -EAGAIN; +} + +static int cci_pmu_map_event(struct perf_event *event) +{ + int mapping; + u8 config = event->attr.config & CCI400_PMU_EVENT_MASK; + + if (event->attr.type < PERF_TYPE_MAX) + return -ENOENT; + + /* 0xff is used to represent CCI Cycles */ + if (config == 0xff) + mapping = config; + else + mapping = cci_pmu_validate_hw_event(config); + + return mapping; +} + +static int cci_pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler) +{ + int irq, err, i = 0; + struct platform_device *pmu_device = cci_pmu->plat_device; + + if (unlikely(!pmu_device)) + return -ENODEV; + + /* CCI exports 6 interrupts - 1 nERRORIRQ + 5 nEVNTCNTOVERFLOW (PMU) + nERRORIRQ will be handled by secure firmware on TC2. So we + assume that all CCI interrupts listed in the linux device + tree are PMU interrupts. + + The following code should then be able to handle different routing + of the CCI PMU interrupts. + */ + while ((irq = platform_get_irq(pmu_device, i)) > 0) { + err = request_irq(irq, handler, 0, "arm-cci-pmu", cci_pmu); + if (err) { + dev_err(&pmu_device->dev, "unable to request IRQ%d for ARM CCI PMU counters\n", + irq); + return err; + } + i++; + } + + return 0; +} + +static irqreturn_t cci_pmu_handle_irq(int irq_num, void *dev) +{ + struct arm_pmu *cci_pmu = (struct arm_pmu *)dev; + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + struct perf_sample_data data; + struct pt_regs *regs; + int idx; + + regs = get_irq_regs(); + + /* Iterate over counters and update the corresponding perf events. + This should work regardless of whether we have per-counter overflow + interrupt or a combined overflow interrupt. */ + for (idx = CCI400_PMU_CYCLE_COUNTER_IDX; idx <= CCI400_PMU_COUNTER_LAST(cci_pmu); idx++) { + struct perf_event *event = events->events[idx]; + struct hw_perf_event *hw_counter; + + if (!event) + continue; + + hw_counter = &event->hw; + + /* Did this counter overflow? */ + if (!(cci_pmu_read_register(idx, CCI400_PMU_OVERFLOW) & CCI400_PMU_OVERFLOW_FLAG)) + continue; + cci_pmu_write_register(CCI400_PMU_OVERFLOW_FLAG, idx, CCI400_PMU_OVERFLOW); + + armpmu_event_update(event); + perf_sample_data_init(&data, 0, hw_counter->last_period); + if (!armpmu_event_set_period(event)) + continue; + + if (perf_event_overflow(event, &data, regs)) + cci_pmu->disable(event); + } + + irq_work_run(); + return IRQ_HANDLED; +} + +static void cci_pmu_free_irq(struct arm_pmu *cci_pmu) +{ + int irq, i = 0; + struct platform_device *pmu_device = cci_pmu->plat_device; + + while ((irq = platform_get_irq(pmu_device, i)) > 0) { + free_irq(irq, cci_pmu); + i++; + } +} + +static void cci_pmu_enable_event(struct perf_event *event) +{ + unsigned long flags; + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + + if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) { + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + return; + } + + raw_spin_lock_irqsave(&events->pmu_lock, flags); + + /* Configure the event to count, unless you are counting cycles */ + if (idx != CCI400_PMU_CYCLE_COUNTER_IDX) + cci_pmu_select_event(idx, hw_counter->config_base); + + cci_pmu_enable_counter(idx); + + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +} + +static void cci_pmu_disable_event(struct perf_event *event) +{ + unsigned long flags; + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + + if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) { + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + return; + } + + raw_spin_lock_irqsave(&events->pmu_lock, flags); + + cci_pmu_disable_counter(idx); + + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +} + +static void cci_pmu_start(struct arm_pmu *cci_pmu) +{ + u32 val; + unsigned long flags; + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + + raw_spin_lock_irqsave(&events->pmu_lock, flags); + + /* Enable all the PMU counters. */ + val = readl(cci_ctrl_base + CCI400_PMCR) | CCI400_PMCR_CEN; + writel(val, cci_ctrl_base + CCI400_PMCR); + + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +} + +static void cci_pmu_stop(struct arm_pmu *cci_pmu) +{ + u32 val; + unsigned long flags; + struct pmu_hw_events *events = cci_pmu->get_hw_events(); + + raw_spin_lock_irqsave(&events->pmu_lock, flags); + + /* Disable all the PMU counters. */ + val = readl(cci_ctrl_base + CCI400_PMCR) & ~CCI400_PMCR_CEN; + writel(val, cci_ctrl_base + CCI400_PMCR); + + raw_spin_unlock_irqrestore(&events->pmu_lock, flags); +} + +static u32 cci_pmu_read_counter(struct perf_event *event) +{ + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + u32 value; + + if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) { + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + return 0; + } + value = cci_pmu_read_register(idx, CCI400_PMU_CNTR); + + return value; +} + +static void cci_pmu_write_counter(struct perf_event *event, u32 value) +{ + struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu); + struct hw_perf_event *hw_counter = &event->hw; + int idx = hw_counter->idx; + + if (unlikely(!cci_pmu_counter_is_valid(cci_pmu, idx))) + dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx); + else + cci_pmu_write_register(value, idx, CCI400_PMU_CNTR); +} + +static struct arm_pmu cci_pmu = { + .name = DRIVER_NAME, + .max_period = (1LLU << 32) - 1, + .get_hw_events = cci_pmu_get_hw_events, + .get_event_idx = cci_pmu_get_event_idx, + .map_event = cci_pmu_map_event, + .request_irq = cci_pmu_request_irq, + .handle_irq = cci_pmu_handle_irq, + .free_irq = cci_pmu_free_irq, + .enable = cci_pmu_enable_event, + .disable = cci_pmu_disable_event, + .start = cci_pmu_start, + .stop = cci_pmu_stop, + .read_counter = cci_pmu_read_counter, + .write_counter = cci_pmu_write_counter, +}; + +static int cci_pmu_probe(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cci_pmu_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(cci_pmu_base)) + return PTR_ERR(cci_pmu_base); + + cci_pmu.plat_device = pdev; + cci_pmu.num_events = cci_pmu_get_max_counters(); + raw_spin_lock_init(&cci_hw_events.pmu_lock); + cpumask_setall(&cci_pmu.valid_cpus); + + return armpmu_register(&cci_pmu, -1); +} + +static const struct of_device_id arm_cci_pmu_matches[] = { + {.compatible = "arm,cci-400-pmu"}, + {}, +}; + +static struct platform_driver cci_pmu_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = arm_cci_pmu_matches, + }, + .probe = cci_pmu_probe, +}; + +static int __init cci_pmu_init(void) +{ + if (platform_driver_register(&cci_pmu_platform_driver)) + WARN(1, "unable to register CCI platform driver\n"); + return 0; +} + +#else + +static int __init cci_pmu_init(void) +{ + return 0; +} + +#endif /* CONFIG_HW_PERF_EVENTS */ + +struct cpu_port { + u64 mpidr; + u32 port; +}; + +/* + * Use the port MSB as valid flag, shift can be made dynamic + * by computing number of bits required for port indexes. + * Code disabling CCI cpu ports runs with D-cache invalidated + * and SCTLR bit clear so data accesses must be kept to a minimum + * to improve performance; for now shift is left static to + * avoid one more data access while disabling the CCI port. + */ +#define PORT_VALID_SHIFT 31 +#define PORT_VALID (0x1 << PORT_VALID_SHIFT) + +static inline void init_cpu_port(struct cpu_port *port, u32 index, u64 mpidr) +{ + port->port = PORT_VALID | index; + port->mpidr = mpidr; +} + +static inline bool cpu_port_is_valid(struct cpu_port *port) +{ + return !!(port->port & PORT_VALID); +} + +static inline bool cpu_port_match(struct cpu_port *port, u64 mpidr) +{ + return port->mpidr == (mpidr & MPIDR_HWID_BITMASK); +} + +static struct cpu_port cpu_port[NR_CPUS]; + +/** + * __cci_ace_get_port - Function to retrieve the port index connected to + * a cpu or device. + * + * @dn: device node of the device to look-up + * @type: port type + * + * Return value: + * - CCI port index if success + * - -ENODEV if failure + */ +static int __cci_ace_get_port(struct device_node *dn, int type) +{ + int i; + bool ace_match; + struct device_node *cci_portn; + + cci_portn = of_parse_phandle(dn, "cci-control-port", 0); + for (i = 0; i < nb_cci_ports; i++) { + ace_match = ports[i].type == type; + if (ace_match && cci_portn == ports[i].dn) + return i; + } + return -ENODEV; +} + +int cci_ace_get_port(struct device_node *dn) +{ + return __cci_ace_get_port(dn, ACE_LITE_PORT); +} +EXPORT_SYMBOL_GPL(cci_ace_get_port); + +static void __init cci_ace_init_ports(void) +{ + int port, ac, cpu; + u64 hwid; + const u32 *cell; + struct device_node *cpun, *cpus; + + cpus = of_find_node_by_path("/cpus"); + if (WARN(!cpus, "Missing cpus node, bailing out\n")) + return; + + if (WARN_ON(of_property_read_u32(cpus, "#address-cells", &ac))) + ac = of_n_addr_cells(cpus); + + /* + * Port index look-up speeds up the function disabling ports by CPU, + * since the logical to port index mapping is done once and does + * not change after system boot. + * The stashed index array is initialized for all possible CPUs + * at probe time. + */ + for_each_child_of_node(cpus, cpun) { + if (of_node_cmp(cpun->type, "cpu")) + continue; + cell = of_get_property(cpun, "reg", NULL); + if (WARN(!cell, "%s: missing reg property\n", cpun->full_name)) + continue; + + hwid = of_read_number(cell, ac); + cpu = get_logical_index(hwid & MPIDR_HWID_BITMASK); + + if (cpu < 0 || !cpu_possible(cpu)) + continue; + port = __cci_ace_get_port(cpun, ACE_PORT); + if (port < 0) + continue; + + init_cpu_port(&cpu_port[cpu], port, cpu_logical_map(cpu)); + } + + for_each_possible_cpu(cpu) { + WARN(!cpu_port_is_valid(&cpu_port[cpu]), + "CPU %u does not have an associated CCI port\n", + cpu); + } +} +/* + * Functions to enable/disable a CCI interconnect slave port + * + * They are called by low-level power management code to disable slave + * interfaces snoops and DVM broadcast. + * Since they may execute with cache data allocation disabled and + * after the caches have been cleaned and invalidated the functions provide + * no explicit locking since they may run with D-cache disabled, so normal + * cacheable kernel locks based on ldrex/strex may not work. + * Locking has to be provided by BSP implementations to ensure proper + * operations. + */ + +/** + * cci_port_control() - function to control a CCI port + * + * @port: index of the port to setup + * @enable: if true enables the port, if false disables it + */ +static void notrace cci_port_control(unsigned int port, bool enable) +{ + void __iomem *base = ports[port].base; + + writel_relaxed(enable ? CCI_ENABLE_REQ : 0, base + CCI_PORT_CTRL); + /* + * This function is called from power down procedures + * and must not execute any instruction that might + * cause the processor to be put in a quiescent state + * (eg wfi). Hence, cpu_relax() can not be added to this + * read loop to optimize power, since it might hide possibly + * disruptive operations. + */ + while (readl_relaxed(cci_ctrl_base + CCI_CTRL_STATUS) & 0x1) + ; +} + +/** + * cci_disable_port_by_cpu() - function to disable a CCI port by CPU + * reference + * + * @mpidr: mpidr of the CPU whose CCI port should be disabled + * + * Disabling a CCI port for a CPU implies disabling the CCI port + * controlling that CPU cluster. Code disabling CPU CCI ports + * must make sure that the CPU running the code is the last active CPU + * in the cluster ie all other CPUs are quiescent in a low power state. + * + * Return: + * 0 on success + * -ENODEV on port look-up failure + */ +int notrace cci_disable_port_by_cpu(u64 mpidr) +{ + int cpu; + bool is_valid; + for (cpu = 0; cpu < nr_cpu_ids; cpu++) { + is_valid = cpu_port_is_valid(&cpu_port[cpu]); + if (is_valid && cpu_port_match(&cpu_port[cpu], mpidr)) { + cci_port_control(cpu_port[cpu].port, false); + return 0; + } + } + return -ENODEV; +} +EXPORT_SYMBOL_GPL(cci_disable_port_by_cpu); + +/** + * cci_enable_port_for_self() - enable a CCI port for calling CPU + * + * Enabling a CCI port for the calling CPU implies enabling the CCI + * port controlling that CPU's cluster. Caller must make sure that the + * CPU running the code is the first active CPU in the cluster and all + * other CPUs are quiescent in a low power state or waiting for this CPU + * to complete the CCI initialization. + * + * Because this is called when the MMU is still off and with no stack, + * the code must be position independent and ideally rely on callee + * clobbered registers only. To achieve this we must code this function + * entirely in assembler. + * + * On success this returns with the proper CCI port enabled. In case of + * any failure this never returns as the inability to enable the CCI is + * fatal and there is no possible recovery at this stage. + */ +asmlinkage void __naked cci_enable_port_for_self(void) +{ + asm volatile ("\n" + +" mrc p15, 0, r0, c0, c0, 5 @ get MPIDR value \n" +" and r0, r0, #"__stringify(MPIDR_HWID_BITMASK)" \n" +" adr r1, 5f \n" +" ldr r2, [r1] \n" +" add r1, r1, r2 @ &cpu_port \n" +" add ip, r1, %[sizeof_cpu_port] \n" + + /* Loop over the cpu_port array looking for a matching MPIDR */ +"1: ldr r2, [r1, %[offsetof_cpu_port_mpidr_lsb]] \n" +" cmp r2, r0 @ compare MPIDR \n" +" bne 2f \n" + + /* Found a match, now test port validity */ +" ldr r3, [r1, %[offsetof_cpu_port_port]] \n" +" tst r3, #"__stringify(PORT_VALID)" \n" +" bne 3f \n" + + /* no match, loop with the next cpu_port entry */ +"2: add r1, r1, %[sizeof_struct_cpu_port] \n" +" cmp r1, ip @ done? \n" +" blo 1b \n" + + /* CCI port not found -- cheaply try to stall this CPU */ +"cci_port_not_found: \n" +" wfi \n" +" wfe \n" +" b cci_port_not_found \n" + + /* Use matched port index to look up the corresponding ports entry */ +"3: bic r3, r3, #"__stringify(PORT_VALID)" \n" +" adr r0, 6f \n" +" ldmia r0, {r1, r2} \n" +" sub r1, r1, r0 @ virt - phys \n" +" ldr r0, [r0, r2] @ *(&ports) \n" +" mov r2, %[sizeof_struct_ace_port] \n" +" mla r0, r2, r3, r0 @ &ports[index] \n" +" sub r0, r0, r1 @ virt_to_phys() \n" + + /* Enable the CCI port */ +" ldr r0, [r0, %[offsetof_port_phys]] \n" +" mov r3, #"__stringify(CCI_ENABLE_REQ)" \n" +" str r3, [r0, #"__stringify(CCI_PORT_CTRL)"] \n" + + /* poll the status reg for completion */ +" adr r1, 7f \n" +" ldr r0, [r1] \n" +" ldr r0, [r0, r1] @ cci_ctrl_base \n" +"4: ldr r1, [r0, #"__stringify(CCI_CTRL_STATUS)"] \n" +" tst r1, #1 \n" +" bne 4b \n" + +" mov r0, #0 \n" +" bx lr \n" + +" .align 2 \n" +"5: .word cpu_port - . \n" +"6: .word . \n" +" .word ports - 6b \n" +"7: .word cci_ctrl_phys - . \n" + : : + [sizeof_cpu_port] "i" (sizeof(cpu_port)), +#ifndef __ARMEB__ + [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)), +#else + [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)+4), +#endif + [offsetof_cpu_port_port] "i" (offsetof(struct cpu_port, port)), + [sizeof_struct_cpu_port] "i" (sizeof(struct cpu_port)), + [sizeof_struct_ace_port] "i" (sizeof(struct cci_ace_port)), + [offsetof_port_phys] "i" (offsetof(struct cci_ace_port, phys)) ); + + unreachable(); +} + +/** + * __cci_control_port_by_device() - function to control a CCI port by device + * reference + * + * @dn: device node pointer of the device whose CCI port should be + * controlled + * @enable: if true enables the port, if false disables it + * + * Return: + * 0 on success + * -ENODEV on port look-up failure + */ +int notrace __cci_control_port_by_device(struct device_node *dn, bool enable) +{ + int port; + + if (!dn) + return -ENODEV; + + port = __cci_ace_get_port(dn, ACE_LITE_PORT); + if (WARN_ONCE(port < 0, "node %s ACE lite port look-up failure\n", + dn->full_name)) + return -ENODEV; + cci_port_control(port, enable); + return 0; +} +EXPORT_SYMBOL_GPL(__cci_control_port_by_device); + +/** + * __cci_control_port_by_index() - function to control a CCI port by port index + * + * @port: port index previously retrieved with cci_ace_get_port() + * @enable: if true enables the port, if false disables it + * + * Return: + * 0 on success + * -ENODEV on port index out of range + * -EPERM if operation carried out on an ACE PORT + */ +int notrace __cci_control_port_by_index(u32 port, bool enable) +{ + if (port >= nb_cci_ports || ports[port].type == ACE_INVALID_PORT) + return -ENODEV; + /* + * CCI control for ports connected to CPUS is extremely fragile + * and must be made to go through a specific and controlled + * interface (ie cci_disable_port_by_cpu(); control by general purpose + * indexing is therefore disabled for ACE ports. + */ + if (ports[port].type == ACE_PORT) + return -EPERM; + + cci_port_control(port, enable); + return 0; +} +EXPORT_SYMBOL_GPL(__cci_control_port_by_index); + +static const struct cci_nb_ports cci400_ports = { + .nb_ace = 2, + .nb_ace_lite = 3 +}; + +static const struct of_device_id arm_cci_matches[] = { + {.compatible = "arm,cci-400", .data = &cci400_ports }, + {}, +}; + +static const struct of_device_id arm_cci_ctrl_if_matches[] = { + {.compatible = "arm,cci-400-ctrl-if", }, + {}, +}; + +static int __init cci_probe(void) +{ + struct cci_nb_ports const *cci_config; + int ret, i, nb_ace = 0, nb_ace_lite = 0; + struct device_node *np, *cp; + struct resource res; + const char *match_str; + bool is_ace; + + np = of_find_matching_node(NULL, arm_cci_matches); + if (!np) + return -ENODEV; + + cci_config = of_match_node(arm_cci_matches, np)->data; + if (!cci_config) + return -ENODEV; + + nb_cci_ports = cci_config->nb_ace + cci_config->nb_ace_lite; + + ports = kcalloc(sizeof(*ports), nb_cci_ports, GFP_KERNEL); + if (!ports) + return -ENOMEM; + + ret = of_address_to_resource(np, 0, &res); + if (!ret) { + cci_ctrl_base = ioremap(res.start, resource_size(&res)); + cci_ctrl_phys = res.start; + } + if (ret || !cci_ctrl_base) { + WARN(1, "unable to ioremap CCI ctrl\n"); + ret = -ENXIO; + goto memalloc_err; + } + + for_each_child_of_node(np, cp) { + if (!of_match_node(arm_cci_ctrl_if_matches, cp)) + continue; + + i = nb_ace + nb_ace_lite; + + if (i >= nb_cci_ports) + break; + + if (of_property_read_string(cp, "interface-type", + &match_str)) { + WARN(1, "node %s missing interface-type property\n", + cp->full_name); + continue; + } + is_ace = strcmp(match_str, "ace") == 0; + if (!is_ace && strcmp(match_str, "ace-lite")) { + WARN(1, "node %s containing invalid interface-type property, skipping it\n", + cp->full_name); + continue; + } + + ret = of_address_to_resource(cp, 0, &res); + if (!ret) { + ports[i].base = ioremap(res.start, resource_size(&res)); + ports[i].phys = res.start; + } + if (ret || !ports[i].base) { + WARN(1, "unable to ioremap CCI port %d\n", i); + continue; + } + + if (is_ace) { + if (WARN_ON(nb_ace >= cci_config->nb_ace)) + continue; + ports[i].type = ACE_PORT; + ++nb_ace; + } else { + if (WARN_ON(nb_ace_lite >= cci_config->nb_ace_lite)) + continue; + ports[i].type = ACE_LITE_PORT; + ++nb_ace_lite; + } + ports[i].dn = cp; + } + + /* initialize a stashed array of ACE ports to speed-up look-up */ + cci_ace_init_ports(); + + /* + * Multi-cluster systems may need this data when non-coherent, during + * cluster power-up/power-down. Make sure it reaches main memory. + */ + sync_cache_w(&cci_ctrl_base); + sync_cache_w(&cci_ctrl_phys); + sync_cache_w(&ports); + sync_cache_w(&cpu_port); + __sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports); + pr_info("ARM CCI driver probed\n"); + return 0; + +memalloc_err: + + kfree(ports); + return ret; +} + +static int cci_init_status = -EAGAIN; +static DEFINE_MUTEX(cci_probing); + +static int __init cci_init(void) +{ + if (cci_init_status != -EAGAIN) + return cci_init_status; + + mutex_lock(&cci_probing); + if (cci_init_status == -EAGAIN) + cci_init_status = cci_probe(); + mutex_unlock(&cci_probing); + return cci_init_status; +} + +/* + * To sort out early init calls ordering a helper function is provided to + * check if the CCI driver has beed initialized. Function check if the driver + * has been initialized, if not it calls the init function that probes + * the driver and updates the return value. + */ +bool __init cci_probed(void) +{ + return cci_init() == 0; +} +EXPORT_SYMBOL_GPL(cci_probed); + +early_initcall(cci_init); +core_initcall(cci_pmu_init); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ARM CCI support"); diff -Nur linux-3.10.30/drivers/bus/imx-weim.c linux-3.10.30-cubox-i/drivers/bus/imx-weim.c --- linux-3.10.30/drivers/bus/imx-weim.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/bus/imx-weim.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,138 @@ +/* + * EIM driver for Freescale's i.MX chips + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include + +struct imx_weim { + void __iomem *base; + struct clk *clk; +}; + +static const struct of_device_id weim_id_table[] = { + { .compatible = "fsl,imx6q-weim", }, + {} +}; +MODULE_DEVICE_TABLE(of, weim_id_table); + +#define CS_TIMING_LEN 6 +#define CS_REG_RANGE 0x18 + +/* Parse and set the timing for this device. */ +static int +weim_timing_setup(struct platform_device *pdev, struct device_node *np) +{ + struct imx_weim *weim = platform_get_drvdata(pdev); + u32 value[CS_TIMING_LEN]; + u32 cs_idx; + int ret; + int i; + + /* get the CS index from this child node's "reg" property. */ + ret = of_property_read_u32(np, "reg", &cs_idx); + if (ret) + return ret; + + /* The weim has four chip selects. */ + if (cs_idx > 3) + return -EINVAL; + + ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", + value, CS_TIMING_LEN); + if (ret) + return ret; + + /* set the timing for WEIM */ + for (i = 0; i < CS_TIMING_LEN; i++) + writel(value[i], weim->base + cs_idx * CS_REG_RANGE + i * 4); + return 0; +} + +static int weim_parse_dt(struct platform_device *pdev) +{ + struct device_node *child; + int ret; + + for_each_child_of_node(pdev->dev.of_node, child) { + if (!child->name) + continue; + + ret = weim_timing_setup(pdev, child); + if (ret) { + dev_err(&pdev->dev, "%s set timing failed.\n", + child->full_name); + return ret; + } + } + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret) + dev_err(&pdev->dev, "%s fail to create devices.\n", + pdev->dev.of_node->full_name); + return ret; +} + +static int weim_probe(struct platform_device *pdev) +{ + struct imx_weim *weim; + struct resource *res; + int ret = -EINVAL; + + weim = devm_kzalloc(&pdev->dev, sizeof(*weim), GFP_KERNEL); + if (!weim) { + ret = -ENOMEM; + goto weim_err; + } + platform_set_drvdata(pdev, weim); + + /* get the resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + weim->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(weim->base)) { + ret = PTR_ERR(weim->base); + goto weim_err; + } + + /* get the clock */ + weim->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(weim->clk)) + goto weim_err; + + ret = clk_prepare_enable(weim->clk); + if (ret) + goto weim_err; + + /* parse the device node */ + ret = weim_parse_dt(pdev); + if (ret) { + clk_disable_unprepare(weim->clk); + goto weim_err; + } + + dev_info(&pdev->dev, "WEIM driver registered.\n"); + return 0; + +weim_err: + return ret; +} + +static struct platform_driver weim_driver = { + .driver = { + .name = "imx-weim", + .of_match_table = weim_id_table, + }, + .probe = weim_probe, +}; + +module_platform_driver(weim_driver); +MODULE_AUTHOR("Freescale Semiconductor Inc."); +MODULE_DESCRIPTION("i.MX EIM Controller Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/char/Kconfig linux-3.10.30-cubox-i/drivers/char/Kconfig --- linux-3.10.30/drivers/char/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/char/Kconfig 2014-03-08 20:33:28.000000000 +0100 @@ -94,6 +94,21 @@ If unsure, say N. +config FSL_OTP + tristate "Freescale On-Chip OTP Memory Support" + depends on HAS_IOMEM && OF + help + If you say Y here, you will get support for a character device + interface into the One Time Programmable memory pages that are + stored on the some Freescale i.MX processors. This will not get + you access to the secure memory pages however. You will need to + write your own secure code and reader for that. + + To compile this driver as a module, choose M here: the module + will be called fsl_otp. + + If unsure, it is safe to say Y. + config PRINTER tristate "Parallel printer support" depends on PARPORT diff -Nur linux-3.10.30/drivers/char/Makefile linux-3.10.30-cubox-i/drivers/char/Makefile --- linux-3.10.30/drivers/char/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/char/Makefile 2014-03-08 20:33:28.000000000 +0100 @@ -17,6 +17,7 @@ obj-$(CONFIG_IBM_BSR) += bsr.o obj-$(CONFIG_SGI_MBCS) += mbcs.o obj-$(CONFIG_BFIN_OTP) += bfin-otp.o +obj-$(CONFIG_FSL_OTP) += fsl_otp.o obj-$(CONFIG_PRINTER) += lp.o diff -Nur linux-3.10.30/drivers/char/fsl_otp.c linux-3.10.30-cubox-i/drivers/char/fsl_otp.c --- linux-3.10.30/drivers/char/fsl_otp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/char/fsl_otp.c 2014-03-08 20:33:28.000000000 +0100 @@ -0,0 +1,299 @@ +/* + * Freescale On-Chip OTP driver + * + * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HW_OCOTP_CTRL 0x00000000 +#define HW_OCOTP_CTRL_SET 0x00000004 +#define BP_OCOTP_CTRL_WR_UNLOCK 16 +#define BM_OCOTP_CTRL_WR_UNLOCK 0xFFFF0000 +#define BM_OCOTP_CTRL_RELOAD_SHADOWS 0x00000400 +#define BM_OCOTP_CTRL_ERROR 0x00000200 +#define BM_OCOTP_CTRL_BUSY 0x00000100 +#define BP_OCOTP_CTRL_ADDR 0 +#define BM_OCOTP_CTRL_ADDR 0x0000007F + +#define HW_OCOTP_TIMING 0x00000010 +#define BP_OCOTP_TIMING_STROBE_READ 16 +#define BM_OCOTP_TIMING_STROBE_READ 0x003F0000 +#define BP_OCOTP_TIMING_RELAX 12 +#define BM_OCOTP_TIMING_RELAX 0x0000F000 +#define BP_OCOTP_TIMING_STROBE_PROG 0 +#define BM_OCOTP_TIMING_STROBE_PROG 0x00000FFF + +#define HW_OCOTP_DATA 0x00000020 + +#define HW_OCOTP_CUST_N(n) (0x00000400 + (n) * 0x10) +#define BF(value, field) (((value) << BP_##field) & BM_##field) + +#define DEF_RELAX 20 /* > 16.5ns */ + +#define BANK(a, b, c, d, e, f, g, h) { \ + "HW_OCOTP_"#a, "HW_OCOTP_"#b, "HW_OCOTP_"#c, "HW_OCOTP_"#d, \ + "HW_OCOTP_"#e, "HW_OCOTP_"#f, "HW_OCOTP_"#g, "HW_OCOTP_"#h, \ +} + +static const char *imx6q_otp_desc[16][8] = { + BANK(LOCK, CFG0, CFG1, CFG2, CFG3, CFG4, CFG5, CFG6), + BANK(MEM0, MEM1, MEM2, MEM3, MEM4, ANA0, ANA1, ANA2), + BANK(OTPMK0, OTPMK1, OTPMK2, OTPMK3, OTPMK4, OTPMK5, OTPMK6, OTPMK7), + BANK(SRK0, SRK1, SRK2, SRK3, SRK4, SRK5, SRK6, SRK7), + BANK(RESP0, HSJC_RESP1, MAC0, MAC1, HDCP_KSV0, HDCP_KSV1, GP1, GP2), + BANK(DTCP_KEY0, DTCP_KEY1, DTCP_KEY2, DTCP_KEY3, DTCP_KEY4, MISC_CONF, FIELD_RETURN, SRK_REVOKE), + BANK(HDCP_KEY0, HDCP_KEY1, HDCP_KEY2, HDCP_KEY3, HDCP_KEY4, HDCP_KEY5, HDCP_KEY6, HDCP_KEY7), + BANK(HDCP_KEY8, HDCP_KEY9, HDCP_KEY10, HDCP_KEY11, HDCP_KEY12, HDCP_KEY13, HDCP_KEY14, HDCP_KEY15), + BANK(HDCP_KEY16, HDCP_KEY17, HDCP_KEY18, HDCP_KEY19, HDCP_KEY20, HDCP_KEY21, HDCP_KEY22, HDCP_KEY23), + BANK(HDCP_KEY24, HDCP_KEY25, HDCP_KEY26, HDCP_KEY27, HDCP_KEY28, HDCP_KEY29, HDCP_KEY30, HDCP_KEY31), + BANK(HDCP_KEY32, HDCP_KEY33, HDCP_KEY34, HDCP_KEY35, HDCP_KEY36, HDCP_KEY37, HDCP_KEY38, HDCP_KEY39), + BANK(HDCP_KEY40, HDCP_KEY41, HDCP_KEY42, HDCP_KEY43, HDCP_KEY44, HDCP_KEY45, HDCP_KEY46, HDCP_KEY47), + BANK(HDCP_KEY48, HDCP_KEY49, HDCP_KEY50, HDCP_KEY51, HDCP_KEY52, HDCP_KEY53, HDCP_KEY54, HDCP_KEY55), + BANK(HDCP_KEY56, HDCP_KEY57, HDCP_KEY58, HDCP_KEY59, HDCP_KEY60, HDCP_KEY61, HDCP_KEY62, HDCP_KEY63), + BANK(HDCP_KEY64, HDCP_KEY65, HDCP_KEY66, HDCP_KEY67, HDCP_KEY68, HDCP_KEY69, HDCP_KEY70, HDCP_KEY71), + BANK(CRC0, CRC1, CRC2, CRC3, CRC4, CRC5, CRC6, CRC7), +}; + +static DEFINE_MUTEX(otp_mutex); +static void __iomem *otp_base; +static struct clk *otp_clk; +struct kobject *otp_kobj; +struct kobj_attribute *otp_kattr; +struct attribute_group *otp_attr_group; + +static void set_otp_timing(void) +{ + unsigned long clk_rate = 0; + unsigned long strobe_read, relex, strobe_prog; + u32 timing = 0; + + clk_rate = clk_get_rate(otp_clk); + + /* do optimization for too many zeros */ + relex = clk_rate / (1000000000 / DEF_RELAX) - 1; + strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; + strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; + + timing = BF(relex, OCOTP_TIMING_RELAX); + timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ); + timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG); + + __raw_writel(timing, otp_base + HW_OCOTP_TIMING); +} + +static int otp_wait_busy(u32 flags) +{ + int count; + u32 c; + + for (count = 10000; count >= 0; count--) { + c = __raw_readl(otp_base + HW_OCOTP_CTRL); + if (!(c & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR | flags))) + break; + cpu_relax(); + } + + if (count < 0) + return -ETIMEDOUT; + + return 0; +} + +static ssize_t fsl_otp_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + unsigned int index = attr - otp_kattr; + u32 value = 0; + int ret; + + ret = clk_prepare_enable(otp_clk); + if (ret) + return 0; + + mutex_lock(&otp_mutex); + + set_otp_timing(); + ret = otp_wait_busy(0); + if (ret) + goto out; + + value = __raw_readl(otp_base + HW_OCOTP_CUST_N(index)); + +out: + mutex_unlock(&otp_mutex); + clk_disable_unprepare(otp_clk); + return ret ? 0 : sprintf(buf, "0x%x\n", value); +} + +static int otp_write_bits(int addr, u32 data, u32 magic) +{ + u32 c; /* for control register */ + + /* init the control register */ + c = __raw_readl(otp_base + HW_OCOTP_CTRL); + c &= ~BM_OCOTP_CTRL_ADDR; + c |= BF(addr, OCOTP_CTRL_ADDR); + c |= BF(magic, OCOTP_CTRL_WR_UNLOCK); + __raw_writel(c, otp_base + HW_OCOTP_CTRL); + + /* init the data register */ + __raw_writel(data, otp_base + HW_OCOTP_DATA); + otp_wait_busy(0); + + mdelay(2); /* Write Postamble */ + + return 0; +} + +static ssize_t fsl_otp_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned int index = attr - otp_kattr; + u32 value; + int ret; + + sscanf(buf, "0x%x", &value); + + ret = clk_prepare_enable(otp_clk); + if (ret) + return 0; + + mutex_lock(&otp_mutex); + + set_otp_timing(); + ret = otp_wait_busy(0); + if (ret) + goto out; + + otp_write_bits(index, value, 0x3e77); + + /* Reload all the shadow registers */ + __raw_writel(BM_OCOTP_CTRL_RELOAD_SHADOWS, + otp_base + HW_OCOTP_CTRL_SET); + udelay(1); + otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS); + +out: + mutex_unlock(&otp_mutex); + clk_disable_unprepare(otp_clk); + return ret ? 0 : count; +} + +static int fsl_otp_probe(struct platform_device *pdev) +{ + struct resource *res; + struct attribute **attrs; + const char **desc; + int i, num; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + otp_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(otp_base)) { + ret = PTR_ERR(otp_base); + dev_err(&pdev->dev, "failed to ioremap resource: %d\n", ret); + return ret; + } + + otp_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(otp_clk)) { + ret = PTR_ERR(otp_clk); + dev_err(&pdev->dev, "failed to get clock: %d\n", ret); + return ret; + } + + desc = (const char **) imx6q_otp_desc; + num = sizeof(imx6q_otp_desc) / sizeof(void *); + + /* The last one is NULL, which is used to detect the end */ + attrs = devm_kzalloc(&pdev->dev, (num + 1) * sizeof(*attrs), + GFP_KERNEL); + otp_kattr = devm_kzalloc(&pdev->dev, num * sizeof(*otp_kattr), + GFP_KERNEL); + otp_attr_group = devm_kzalloc(&pdev->dev, sizeof(*otp_attr_group), + GFP_KERNEL); + if (!attrs || !otp_kattr || !otp_attr_group) + return -ENOMEM; + + for (i = 0; i < num; i++) { + sysfs_attr_init(&otp_kattr[i].attr); + otp_kattr[i].attr.name = desc[i]; + otp_kattr[i].attr.mode = 0600; + otp_kattr[i].show = fsl_otp_show; + otp_kattr[i].store = fsl_otp_store; + attrs[i] = &otp_kattr[i].attr; + } + otp_attr_group->attrs = attrs; + + otp_kobj = kobject_create_and_add("fsl_otp", NULL); + if (!otp_kobj) { + dev_err(&pdev->dev, "failed to add kobject\n"); + return -ENOMEM; + } + + ret = sysfs_create_group(otp_kobj, otp_attr_group); + if (ret) { + dev_err(&pdev->dev, "failed to create sysfs group: %d\n", ret); + kobject_put(otp_kobj); + return ret; + } + + mutex_init(&otp_mutex); + + return 0; +} + +static int fsl_otp_remove(struct platform_device *pdev) +{ + sysfs_remove_group(otp_kobj, otp_attr_group); + kobject_put(otp_kobj); + + return 0; +} + +static const struct of_device_id fsl_otp_dt_ids[] = { + { .compatible = "fsl,imx6q-ocotp", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_otp_dt_ids); + +static struct platform_driver fsl_otp_driver = { + .driver = { + .name = "imx-ocotp", + .owner = THIS_MODULE, + .of_match_table = fsl_otp_dt_ids, + }, + .probe = fsl_otp_probe, + .remove = fsl_otp_remove, +}; +module_platform_driver(fsl_otp_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huang Shijie "); +MODULE_DESCRIPTION("Freescale i.MX OCOTP driver"); diff -Nur linux-3.10.30/drivers/clk/Kconfig linux-3.10.30-cubox-i/drivers/clk/Kconfig --- linux-3.10.30/drivers/clk/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/clk/Kconfig 2014-03-08 20:33:29.000000000 +0100 @@ -42,7 +42,7 @@ config COMMON_CLK_VERSATILE bool "Clock driver for ARM Reference designs" - depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS + depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64 ---help--- Supports clocking on ARM Reference designs: - Integrator/AP and Integrator/CP diff -Nur linux-3.10.30/drivers/clk/clk-divider.c linux-3.10.30-cubox-i/drivers/clk/clk-divider.c --- linux-3.10.30/drivers/clk/clk-divider.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/clk/clk-divider.c 2014-03-08 20:33:29.000000000 +0100 @@ -150,6 +150,7 @@ struct clk_divider *divider = to_clk_divider(hw); int i, bestdiv = 0; unsigned long parent_rate, best = 0, now, maxdiv; + unsigned long parent_rate_saved = *best_parent_rate; if (!rate) rate = 1; @@ -173,6 +174,15 @@ for (i = 1; i <= maxdiv; i++) { if (!_is_valid_div(divider, i)) continue; + if (rate * i == parent_rate_saved) { + /* + * It's the most ideal case if the requested rate can be + * divided from parent clock without needing to change + * parent rate, so return the divider immediately. + */ + *best_parent_rate = parent_rate_saved; + return i; + } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); now = parent_rate / i; diff -Nur linux-3.10.30/drivers/clk/versatile/Makefile linux-3.10.30-cubox-i/drivers/clk/versatile/Makefile --- linux-3.10.30/drivers/clk/versatile/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/clk/versatile/Makefile 2014-03-08 20:33:29.000000000 +0100 @@ -4,4 +4,4 @@ obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o clk-sp810.o -obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o +obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o clk-vexpress-spc.o diff -Nur linux-3.10.30/drivers/clk/versatile/clk-vexpress-osc.c linux-3.10.30-cubox-i/drivers/clk/versatile/clk-vexpress-osc.c --- linux-3.10.30/drivers/clk/versatile/clk-vexpress-osc.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/clk/versatile/clk-vexpress-osc.c 2014-03-08 20:33:29.000000000 +0100 @@ -107,7 +107,7 @@ osc->func = vexpress_config_func_get_by_node(node); if (!osc->func) { pr_err("Failed to obtain config func for node '%s'!\n", - node->name); + node->full_name); goto error; } @@ -119,7 +119,7 @@ of_property_read_string(node, "clock-output-names", &init.name); if (!init.name) - init.name = node->name; + init.name = node->full_name; init.ops = &vexpress_osc_ops; init.flags = CLK_IS_ROOT; diff -Nur linux-3.10.30/drivers/clk/versatile/clk-vexpress-spc.c linux-3.10.30-cubox-i/drivers/clk/versatile/clk-vexpress-spc.c --- linux-3.10.30/drivers/clk/versatile/clk-vexpress-spc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/clk/versatile/clk-vexpress-spc.c 2014-03-08 20:33:29.000000000 +0100 @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2012 ARM Limited + * Copyright (C) 2012 Linaro + * + * Author: Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +/* SPC clock programming interface for Vexpress cpus */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +struct clk_spc { + struct clk_hw hw; + spinlock_t *lock; + int cluster; +}; + +#define to_clk_spc(spc) container_of(spc, struct clk_spc, hw) + +static unsigned long spc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_spc *spc = to_clk_spc(hw); + u32 freq; + + if (vexpress_spc_get_performance(spc->cluster, &freq)) { + return -EIO; + pr_err("%s: Failed", __func__); + } + + return freq * 1000; +} + +static long spc_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *parent_rate) +{ + return drate; +} + +static int spc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_spc *spc = to_clk_spc(hw); + + return vexpress_spc_set_performance(spc->cluster, rate / 1000); +} + +static struct clk_ops clk_spc_ops = { + .recalc_rate = spc_recalc_rate, + .round_rate = spc_round_rate, + .set_rate = spc_set_rate, +}; + +struct clk *vexpress_clk_register_spc(const char *name, int cluster_id) +{ + struct clk_init_data init; + struct clk_spc *spc; + struct clk *clk; + + if (!name) { + pr_err("Invalid name passed"); + return ERR_PTR(-EINVAL); + } + + spc = kzalloc(sizeof(*spc), GFP_KERNEL); + if (!spc) { + pr_err("could not allocate spc clk\n"); + return ERR_PTR(-ENOMEM); + } + + spc->hw.init = &init; + spc->cluster = cluster_id; + + init.name = name; + init.ops = &clk_spc_ops; + init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE; + init.num_parents = 0; + + clk = clk_register(NULL, &spc->hw); + if (!IS_ERR_OR_NULL(clk)) + return clk; + + pr_err("clk register failed\n"); + kfree(spc); + + return NULL; +} + +#if defined(CONFIG_OF) +void __init vexpress_clk_of_register_spc(void) +{ + char name[14] = "cpu-cluster.X"; + struct device_node *node = NULL; + struct clk *clk; + const u32 *val; + int cluster_id = 0, len; + + if (!of_find_compatible_node(NULL, NULL, "arm,vexpress-spc")) { + pr_debug("%s: No SPC found, Exiting!!\n", __func__); + return; + } + + while ((node = of_find_node_by_name(node, "cluster"))) { + val = of_get_property(node, "reg", &len); + if (val && len == 4) + cluster_id = be32_to_cpup(val); + + name[12] = cluster_id + '0'; + clk = vexpress_clk_register_spc(name, cluster_id); + if (IS_ERR(clk)) + return; + + pr_debug("Registered clock '%s'\n", name); + clk_register_clkdev(clk, NULL, name); + } +} +CLK_OF_DECLARE(spc, "arm,vexpress-spc", vexpress_clk_of_register_spc); +#endif diff -Nur linux-3.10.30/drivers/clocksource/Kconfig linux-3.10.30-cubox-i/drivers/clocksource/Kconfig --- linux-3.10.30/drivers/clocksource/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/clocksource/Kconfig 2014-03-08 20:33:29.000000000 +0100 @@ -85,3 +85,8 @@ Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver for all devicetree enabled platforms. This driver will be needed only on systems that do not have the Exynos MCT available. + +config VF_PIT_TIMER + bool + help + Support for Period Interrupt Timer on Freescale Vybrid Family SoCs. diff -Nur linux-3.10.30/drivers/clocksource/Makefile linux-3.10.30-cubox-i/drivers/clocksource/Makefile --- linux-3.10.30/drivers/clocksource/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/clocksource/Makefile 2014-03-08 20:33:29.000000000 +0100 @@ -26,6 +26,7 @@ obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o +obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o diff -Nur linux-3.10.30/drivers/clocksource/vf_pit_timer.c linux-3.10.30-cubox-i/drivers/clocksource/vf_pit_timer.c --- linux-3.10.30/drivers/clocksource/vf_pit_timer.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/clocksource/vf_pit_timer.c 2014-03-08 20:33:29.000000000 +0100 @@ -0,0 +1,194 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, 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. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Each pit takes 0x10 Bytes register space + */ +#define PITMCR 0x00 +#define PIT0_OFFSET 0x100 +#define PITn_OFFSET(n) (PIT0_OFFSET + 0x10 * (n)) +#define PITLDVAL 0x00 +#define PITCVAL 0x04 +#define PITTCTRL 0x08 +#define PITTFLG 0x0c + +#define PITMCR_MDIS (0x1 << 1) + +#define PITTCTRL_TEN (0x1 << 0) +#define PITTCTRL_TIE (0x1 << 1) +#define PITCTRL_CHN (0x1 << 2) + +#define PITTFLG_TIF 0x1 + +static void __iomem *clksrc_base; +static void __iomem *clkevt_base; +static unsigned long cycle_per_jiffy; + +static inline void pit_timer_enable(void) +{ + __raw_writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL); +} + +static inline void pit_timer_disable(void) +{ + __raw_writel(0, clkevt_base + PITTCTRL); +} + +static inline void pit_irq_acknowledge(void) +{ + __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); +} + +static unsigned int pit_read_sched_clock(void) +{ + return __raw_readl(clksrc_base + PITCVAL); +} + +static int __init pit_clocksource_init(unsigned long rate) +{ + /* set the max load value and start the clock source counter */ + __raw_writel(0, clksrc_base + PITTCTRL); + __raw_writel(~0UL, clksrc_base + PITLDVAL); + __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); + + setup_sched_clock(pit_read_sched_clock, 32, rate); + return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate, + 300, 32, clocksource_mmio_readl_down); +} + +static int pit_set_next_event(unsigned long delta, + struct clock_event_device *unused) +{ + /* + * set a new value to PITLDVAL register will not restart the timer, + * to abort the current cycle and start a timer period with the new + * value, the timer must be disabled and enabled again. + * and the PITLAVAL should be set to delta minus one according to pit + * hardware requirement. + */ + pit_timer_disable(); + __raw_writel(delta - 1, clkevt_base + PITLDVAL); + pit_timer_enable(); + + return 0; +} + +static void pit_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + pit_set_next_event(cycle_per_jiffy, evt); + break; + default: + break; + } +} + +static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + pit_irq_acknowledge(); + + /* + * pit hardware doesn't support oneshot, it will generate an interrupt + * and reload the counter value from PITLDVAL when PITCVAL reach zero, + * and start the counter again. So software need to disable the timer + * to stop the counter loop in ONESHOT mode. + */ + if (likely(evt->mode == CLOCK_EVT_MODE_ONESHOT)) + pit_timer_disable(); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct clock_event_device clockevent_pit = { + .name = "VF pit timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = pit_set_mode, + .set_next_event = pit_set_next_event, + .rating = 300, +}; + +static struct irqaction pit_timer_irq = { + .name = "VF pit timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = pit_timer_interrupt, + .dev_id = &clockevent_pit, +}; + +static int __init pit_clockevent_init(unsigned long rate, int irq) +{ + __raw_writel(0, clkevt_base + PITTCTRL); + __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); + + BUG_ON(setup_irq(irq, &pit_timer_irq)); + + clockevent_pit.cpumask = cpumask_of(0); + clockevent_pit.irq = irq; + /* + * The value for the LDVAL register trigger is calculated as: + * LDVAL trigger = (period / clock period) - 1 + * The pit is a 32-bit down count timer, when the conter value + * reaches 0, it will generate an interrupt, thus the minimal + * LDVAL trigger value is 1. And then the min_delta is + * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit. + */ + clockevents_config_and_register(&clockevent_pit, rate, 2, 0xffffffff); + + return 0; +} + +static void __init pit_timer_init(struct device_node *np) +{ + struct clk *pit_clk; + void __iomem *timer_base; + unsigned long clk_rate; + int irq; + + timer_base = of_iomap(np, 0); + BUG_ON(!timer_base); + + /* + * PIT0 and PIT1 can be chained to build a 64-bit timer, + * so choose PIT2 as clocksource, PIT3 as clockevent device, + * and leave PIT0 and PIT1 unused for anyone else who needs them. + */ + clksrc_base = timer_base + PITn_OFFSET(2); + clkevt_base = timer_base + PITn_OFFSET(3); + + irq = irq_of_parse_and_map(np, 0); + BUG_ON(irq <= 0); + + pit_clk = of_clk_get(np, 0); + BUG_ON(IS_ERR(pit_clk)); + + BUG_ON(clk_prepare_enable(pit_clk)); + + clk_rate = clk_get_rate(pit_clk); + cycle_per_jiffy = clk_rate / (HZ); + + /* enable the pit module */ + __raw_writel(~PITMCR_MDIS, timer_base + PITMCR); + + BUG_ON(pit_clocksource_init(clk_rate)); + + pit_clockevent_init(clk_rate, irq); +} +CLOCKSOURCE_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); diff -Nur linux-3.10.30/drivers/cpufreq/Kconfig linux-3.10.30-cubox-i/drivers/cpufreq/Kconfig --- linux-3.10.30/drivers/cpufreq/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/Kconfig 2014-03-08 20:33:29.000000000 +0100 @@ -102,6 +102,18 @@ Be aware that not all cpufreq drivers support the conservative governor. If unsure have a look at the help section of the driver. Fallback governor will be the performance governor. + +config CPU_FREQ_DEFAULT_GOV_INTERACTIVE + bool "interactive" + select CPU_FREQ_GOV_INTERACTIVE + select CPU_FREQ_GOV_PERFORMANCE + help + Use the CPUFreq governor 'interactive' as default. This allows + you to get a full dynamic cpu frequency capable system by simply + loading your cpufreq low-level hardware driver, using the + 'interactive' governor for latency-sensitive workloads. Fallback + governor will be the performance governor. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -184,6 +196,23 @@ If in doubt, say N. +config CPU_FREQ_GOV_INTERACTIVE + tristate "'interactive' cpufreq policy governor" + help + 'interactive' - This driver adds a dynamic cpufreq policy governor + designed for latency-sensitive workloads. + + This governor attempts to reduce the latency of clock + increases so that the system is more responsive to + interactive workloads. + + To compile this driver as a module, choose M here: the + module will be called cpufreq_interactive. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + config GENERIC_CPUFREQ_CPU0 tristate "Generic CPU0 cpufreq driver" depends on HAVE_CLK && REGULATOR && PM_OPP && OF @@ -201,7 +230,7 @@ endmenu menu "ARM CPU frequency scaling drivers" -depends on ARM +depends on ARM || ARM64 source "drivers/cpufreq/Kconfig.arm" endmenu diff -Nur linux-3.10.30/drivers/cpufreq/Kconfig.arm linux-3.10.30-cubox-i/drivers/cpufreq/Kconfig.arm --- linux-3.10.30/drivers/cpufreq/Kconfig.arm 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/Kconfig.arm 2014-03-08 20:33:29.000000000 +0100 @@ -4,7 +4,7 @@ config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" - depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK + depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK && BIG_LITTLE help This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. @@ -15,6 +15,14 @@ This enables probing via DT for Generic CPUfreq driver for ARM big.LITTLE platform. This gets frequency tables from DT. +config ARM_VEXPRESS_BL_CPUFREQ + tristate "ARM Vexpress big LITTLE CPUfreq driver" + select ARM_BIG_LITTLE_CPUFREQ + depends on VEXPRESS_SPC + help + This enables the CPUfreq driver for ARM Vexpress big.LITTLE platform. + If in doubt, say N. + config ARM_EXYNOS_CPUFREQ bool "SAMSUNG EXYNOS SoCs" depends on ARCH_EXYNOS @@ -67,12 +75,12 @@ If in doubt, say N. -config ARM_IMX6Q_CPUFREQ - tristate "Freescale i.MX6Q cpufreq support" - depends on SOC_IMX6Q +config ARM_IMX6_CPUFREQ + tristate "Freescale i.MX6 cpufreq support" + depends on SOC_IMX6Q || SOC_IMX6SL depends on REGULATOR_ANATOP help - This adds cpufreq driver support for Freescale i.MX6Q SOC. + This adds cpufreq driver support for Freescale i.MX6 series SOC. If in doubt, say N. diff -Nur linux-3.10.30/drivers/cpufreq/Makefile linux-3.10.30-cubox-i/drivers/cpufreq/Makefile --- linux-3.10.30/drivers/cpufreq/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/Makefile 2014-03-08 20:33:29.000000000 +0100 @@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o # CPUfreq cross-arch helpers @@ -48,6 +49,7 @@ obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o # big LITTLE per platform glues. Keep DT_BL_CPUFREQ as the last entry in all big # LITTLE drivers, so that it is probed last. +obj-$(CONFIG_ARM_VEXPRESS_BL_CPUFREQ) += vexpress_big_little.o obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o obj-$(CONFIG_ARCH_DAVINCI_DA850) += davinci-cpufreq.o @@ -58,7 +60,7 @@ obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o -obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o +obj-$(CONFIG_ARM_IMX6_CPUFREQ) += cpufreq-imx6.o obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o diff -Nur linux-3.10.30/drivers/cpufreq/arm_big_little.c linux-3.10.30-cubox-i/drivers/cpufreq/arm_big_little.c --- linux-3.10.30/drivers/cpufreq/arm_big_little.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/arm_big_little.c 2014-03-08 20:33:29.000000000 +0100 @@ -24,27 +24,148 @@ #include #include #include +#include #include #include #include #include #include +#include #include "arm_big_little.h" -/* Currently we support only two clusters */ -#define MAX_CLUSTERS 2 +#ifdef CONFIG_BL_SWITCHER +bool bL_switching_enabled; +#endif + +#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq) +#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq) static struct cpufreq_arm_bL_ops *arm_bL_ops; static struct clk *clk[MAX_CLUSTERS]; -static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS]; -static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)}; +static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1]; +static atomic_t cluster_usage[MAX_CLUSTERS + 1] = {ATOMIC_INIT(0), + ATOMIC_INIT(0)}; + +static unsigned int clk_big_min; /* (Big) clock frequencies */ +static unsigned int clk_little_max; /* Maximum clock frequency (Little) */ + +static DEFINE_PER_CPU(unsigned int, physical_cluster); +static DEFINE_PER_CPU(unsigned int, cpu_last_req_freq); + +static struct mutex cluster_lock[MAX_CLUSTERS]; + +static unsigned int find_cluster_maxfreq(int cluster) +{ + int j; + u32 max_freq = 0, cpu_freq; + + for_each_online_cpu(j) { + cpu_freq = per_cpu(cpu_last_req_freq, j); + + if ((cluster == per_cpu(physical_cluster, j)) && + (max_freq < cpu_freq)) + max_freq = cpu_freq; + } + + pr_debug("%s: cluster: %d, max freq: %d\n", __func__, cluster, + max_freq); + + return max_freq; +} + +static unsigned int clk_get_cpu_rate(unsigned int cpu) +{ + u32 cur_cluster = per_cpu(physical_cluster, cpu); + u32 rate = clk_get_rate(clk[cur_cluster]) / 1000; + + /* For switcher we use virtual A15 clock rates */ + if (is_bL_switching_enabled()) + rate = VIRT_FREQ(cur_cluster, rate); + + pr_debug("%s: cpu: %d, cluster: %d, freq: %u\n", __func__, cpu, + cur_cluster, rate); + + return rate; +} -static unsigned int bL_cpufreq_get(unsigned int cpu) +static unsigned int bL_cpufreq_get_rate(unsigned int cpu) { - u32 cur_cluster = cpu_to_cluster(cpu); + if (is_bL_switching_enabled()) { + pr_debug("%s: freq: %d\n", __func__, per_cpu(cpu_last_req_freq, + cpu)); + + return per_cpu(cpu_last_req_freq, cpu); + } else { + return clk_get_cpu_rate(cpu); + } +} + +static unsigned int +bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate) +{ + u32 new_rate, prev_rate; + int ret; + bool bLs = is_bL_switching_enabled(); + + mutex_lock(&cluster_lock[new_cluster]); + + if (bLs) { + prev_rate = per_cpu(cpu_last_req_freq, cpu); + per_cpu(cpu_last_req_freq, cpu) = rate; + per_cpu(physical_cluster, cpu) = new_cluster; + + new_rate = find_cluster_maxfreq(new_cluster); + new_rate = ACTUAL_FREQ(new_cluster, new_rate); + } else { + new_rate = rate; + } + + pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d, freq: %d\n", + __func__, cpu, old_cluster, new_cluster, new_rate); + + ret = clk_set_rate(clk[new_cluster], new_rate * 1000); + if (WARN_ON(ret)) { + pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret, + new_cluster); + if (bLs) { + per_cpu(cpu_last_req_freq, cpu) = prev_rate; + per_cpu(physical_cluster, cpu) = old_cluster; + } + + mutex_unlock(&cluster_lock[new_cluster]); + + return ret; + } + + mutex_unlock(&cluster_lock[new_cluster]); - return clk_get_rate(clk[cur_cluster]) / 1000; + /* Recalc freq for old cluster when switching clusters */ + if (old_cluster != new_cluster) { + pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d\n", + __func__, cpu, old_cluster, new_cluster); + + /* Switch cluster */ + bL_switch_request(cpu, new_cluster); + + mutex_lock(&cluster_lock[old_cluster]); + + /* Set freq of old cluster if there are cpus left on it */ + new_rate = find_cluster_maxfreq(old_cluster); + new_rate = ACTUAL_FREQ(old_cluster, new_rate); + + if (new_rate) { + pr_debug("%s: Updating rate of old cluster: %d, to freq: %d\n", + __func__, old_cluster, new_rate); + + if (clk_set_rate(clk[old_cluster], new_rate * 1000)) + pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n", + __func__, ret, old_cluster); + } + mutex_unlock(&cluster_lock[old_cluster]); + } + + return 0; } /* Validate policy frequency range */ @@ -60,12 +181,14 @@ unsigned int target_freq, unsigned int relation) { struct cpufreq_freqs freqs; - u32 cpu = policy->cpu, freq_tab_idx, cur_cluster; + u32 cpu = policy->cpu, freq_tab_idx, cur_cluster, new_cluster, + actual_cluster; int ret = 0; - cur_cluster = cpu_to_cluster(policy->cpu); + cur_cluster = cpu_to_cluster(cpu); + new_cluster = actual_cluster = per_cpu(physical_cluster, cpu); - freqs.old = bL_cpufreq_get(policy->cpu); + freqs.old = bL_cpufreq_get_rate(cpu); /* Determine valid target frequency using freq_table */ cpufreq_frequency_table_target(policy, freq_table[cur_cluster], @@ -79,13 +202,21 @@ if (freqs.old == freqs.new) return 0; + if (is_bL_switching_enabled()) { + if ((actual_cluster == A15_CLUSTER) && + (freqs.new < clk_big_min)) { + new_cluster = A7_CLUSTER; + } else if ((actual_cluster == A7_CLUSTER) && + (freqs.new > clk_little_max)) { + new_cluster = A15_CLUSTER; + } + } + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000); - if (ret) { - pr_err("clk_set_rate failed: %d\n", ret); + ret = bL_cpufreq_set_rate(cpu, actual_cluster, new_cluster, freqs.new); + if (ret) return ret; - } policy->cur = freqs.new; @@ -94,7 +225,73 @@ return ret; } -static void put_cluster_clk_and_freq_table(struct device *cpu_dev) +static inline u32 get_table_count(struct cpufreq_frequency_table *table) +{ + int count; + + for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++) + ; + + return count; +} + +/* get the minimum frequency in the cpufreq_frequency_table */ +static inline u32 get_table_min(struct cpufreq_frequency_table *table) +{ + int i; + uint32_t min_freq = ~0; + for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) + if (table[i].frequency < min_freq) + min_freq = table[i].frequency; + return min_freq; +} + +/* get the maximum frequency in the cpufreq_frequency_table */ +static inline u32 get_table_max(struct cpufreq_frequency_table *table) +{ + int i; + uint32_t max_freq = 0; + for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) + if (table[i].frequency > max_freq) + max_freq = table[i].frequency; + return max_freq; +} + +static int merge_cluster_tables(void) +{ + int i, j, k = 0, count = 1; + struct cpufreq_frequency_table *table; + + for (i = 0; i < MAX_CLUSTERS; i++) + count += get_table_count(freq_table[i]); + + table = kzalloc(sizeof(*table) * count, GFP_KERNEL); + if (!table) + return -ENOMEM; + + freq_table[MAX_CLUSTERS] = table; + + /* Add in reverse order to get freqs in increasing order */ + for (i = MAX_CLUSTERS - 1; i >= 0; i--) { + for (j = 0; freq_table[i][j].frequency != CPUFREQ_TABLE_END; + j++) { + table[k].frequency = VIRT_FREQ(i, + freq_table[i][j].frequency); + pr_debug("%s: index: %d, freq: %d\n", __func__, k, + table[k].frequency); + k++; + } + } + + table[k].index = k; + table[k].frequency = CPUFREQ_TABLE_END; + + pr_debug("%s: End, table: %p, count: %d\n", __func__, table, k); + + return 0; +} + +static void _put_cluster_clk_and_freq_table(struct device *cpu_dev) { u32 cluster = cpu_to_cluster(cpu_dev->id); @@ -105,10 +302,35 @@ } } -static int get_cluster_clk_and_freq_table(struct device *cpu_dev) +static void put_cluster_clk_and_freq_table(struct device *cpu_dev) +{ + u32 cluster = cpu_to_cluster(cpu_dev->id); + int i; + + if (cluster < MAX_CLUSTERS) + return _put_cluster_clk_and_freq_table(cpu_dev); + + if (atomic_dec_return(&cluster_usage[MAX_CLUSTERS])) + return; + + for (i = 0; i < MAX_CLUSTERS; i++) { + struct device *cdev = get_cpu_device(i); + if (!cdev) { + pr_err("%s: failed to get cpu%d device\n", __func__, i); + return; + } + + _put_cluster_clk_and_freq_table(cdev); + } + + /* free virtual table */ + kfree(freq_table[MAX_CLUSTERS]); +} + +static int _get_cluster_clk_and_freq_table(struct device *cpu_dev) { u32 cluster = cpu_to_cluster(cpu_dev->id); - char name[14] = "cpu-cluster."; + char name[14] = "cpu-cluster.X"; int ret; if (atomic_inc_return(&cluster_usage[cluster]) != 1) @@ -149,6 +371,62 @@ return ret; } +static int get_cluster_clk_and_freq_table(struct device *cpu_dev) +{ + u32 cluster = cpu_to_cluster(cpu_dev->id); + int i, ret; + + if (cluster < MAX_CLUSTERS) + return _get_cluster_clk_and_freq_table(cpu_dev); + + if (atomic_inc_return(&cluster_usage[MAX_CLUSTERS]) != 1) + return 0; + + /* + * Get data for all clusters and fill virtual cluster with a merge of + * both + */ + for (i = 0; i < MAX_CLUSTERS; i++) { + struct device *cdev = get_cpu_device(i); + if (!cdev) { + pr_err("%s: failed to get cpu%d device\n", __func__, i); + return -ENODEV; + } + + ret = _get_cluster_clk_and_freq_table(cdev); + if (ret) + goto put_clusters; + } + + ret = merge_cluster_tables(); + if (ret) + goto put_clusters; + + /* Assuming 2 cluster, set clk_big_min and clk_little_max */ + clk_big_min = get_table_min(freq_table[0]); + clk_little_max = VIRT_FREQ(1, get_table_max(freq_table[1])); + + pr_debug("%s: cluster: %d, clk_big_min: %d, clk_little_max: %d\n", + __func__, cluster, clk_big_min, clk_little_max); + + return 0; + +put_clusters: + while (i--) { + struct device *cdev = get_cpu_device(i); + if (!cdev) { + pr_err("%s: failed to get cpu%d device\n", __func__, i); + return -ENODEV; + } + + _put_cluster_clk_and_freq_table(cdev); + } + + atomic_dec(&cluster_usage[MAX_CLUSTERS]); + + return ret; +} + /* Per-CPU initialization */ static int bL_cpufreq_init(struct cpufreq_policy *policy) { @@ -177,37 +455,30 @@ cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); + if (cur_cluster < MAX_CLUSTERS) { + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + + per_cpu(physical_cluster, policy->cpu) = cur_cluster; + } else { + /* Assumption: during init, we are always running on A15 */ + per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER; + } + if (arm_bL_ops->get_transition_latency) policy->cpuinfo.transition_latency = arm_bL_ops->get_transition_latency(cpu_dev); else policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; - policy->cur = bL_cpufreq_get(policy->cpu); + policy->cur = clk_get_cpu_rate(policy->cpu); - cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + if (is_bL_switching_enabled()) + per_cpu(cpu_last_req_freq, policy->cpu) = policy->cur; dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu); return 0; } -static int bL_cpufreq_exit(struct cpufreq_policy *policy) -{ - struct device *cpu_dev; - - cpu_dev = get_cpu_device(policy->cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - policy->cpu); - return -ENODEV; - } - - put_cluster_clk_and_freq_table(cpu_dev); - dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu); - - return 0; -} - /* Export freq_table to sysfs */ static struct freq_attr *bL_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, @@ -219,16 +490,47 @@ .flags = CPUFREQ_STICKY, .verify = bL_cpufreq_verify_policy, .target = bL_cpufreq_set_target, - .get = bL_cpufreq_get, + .get = bL_cpufreq_get_rate, .init = bL_cpufreq_init, - .exit = bL_cpufreq_exit, .have_governor_per_policy = true, .attr = bL_cpufreq_attr, }; +static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb, + unsigned long action, void *_arg) +{ + pr_debug("%s: action: %ld\n", __func__, action); + + switch (action) { + case BL_NOTIFY_PRE_ENABLE: + case BL_NOTIFY_PRE_DISABLE: + cpufreq_unregister_driver(&bL_cpufreq_driver); + break; + + case BL_NOTIFY_POST_ENABLE: + set_switching_enabled(true); + cpufreq_register_driver(&bL_cpufreq_driver); + break; + + case BL_NOTIFY_POST_DISABLE: + set_switching_enabled(false); + cpufreq_register_driver(&bL_cpufreq_driver); + break; + + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block bL_switcher_notifier = { + .notifier_call = bL_cpufreq_switcher_notifier, +}; + int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) { - int ret; + int ret, i; if (arm_bL_ops) { pr_debug("%s: Already registered: %s, exiting\n", __func__, @@ -243,16 +545,29 @@ arm_bL_ops = ops; + ret = bL_switcher_get_enabled(); + set_switching_enabled(ret); + + for (i = 0; i < MAX_CLUSTERS; i++) + mutex_init(&cluster_lock[i]); + ret = cpufreq_register_driver(&bL_cpufreq_driver); if (ret) { pr_info("%s: Failed registering platform driver: %s, err: %d\n", __func__, ops->name, ret); arm_bL_ops = NULL; } else { - pr_info("%s: Registered platform driver: %s\n", __func__, - ops->name); + ret = bL_switcher_register_notifier(&bL_switcher_notifier); + if (ret) { + cpufreq_unregister_driver(&bL_cpufreq_driver); + arm_bL_ops = NULL; + } else { + pr_info("%s: Registered platform driver: %s\n", + __func__, ops->name); + } } + bL_switcher_put_enabled(); return ret; } EXPORT_SYMBOL_GPL(bL_cpufreq_register); @@ -265,9 +580,31 @@ return; } + bL_switcher_get_enabled(); + bL_switcher_unregister_notifier(&bL_switcher_notifier); cpufreq_unregister_driver(&bL_cpufreq_driver); + bL_switcher_put_enabled(); pr_info("%s: Un-registered platform driver: %s\n", __func__, arm_bL_ops->name); + + /* For saving table get/put on every cpu in/out */ + if (is_bL_switching_enabled()) { + put_cluster_clk_and_freq_table(get_cpu_device(0)); + } else { + int i; + + for (i = 0; i < MAX_CLUSTERS; i++) { + struct device *cdev = get_cpu_device(i); + if (!cdev) { + pr_err("%s: failed to get cpu%d device\n", + __func__, i); + return; + } + + put_cluster_clk_and_freq_table(cdev); + } + } + arm_bL_ops = NULL; } EXPORT_SYMBOL_GPL(bL_cpufreq_unregister); diff -Nur linux-3.10.30/drivers/cpufreq/arm_big_little.h linux-3.10.30-cubox-i/drivers/cpufreq/arm_big_little.h --- linux-3.10.30/drivers/cpufreq/arm_big_little.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/arm_big_little.h 2014-03-08 20:33:29.000000000 +0100 @@ -23,6 +23,20 @@ #include #include +/* Currently we support only two clusters */ +#define A15_CLUSTER 0 +#define A7_CLUSTER 1 +#define MAX_CLUSTERS 2 + +#ifdef CONFIG_BL_SWITCHER +extern bool bL_switching_enabled; +#define is_bL_switching_enabled() bL_switching_enabled +#define set_switching_enabled(x) (bL_switching_enabled = (x)) +#else +#define is_bL_switching_enabled() false +#define set_switching_enabled(x) do { } while (0) +#endif + struct cpufreq_arm_bL_ops { char name[CPUFREQ_NAME_LEN]; int (*get_transition_latency)(struct device *cpu_dev); @@ -36,7 +50,8 @@ static inline int cpu_to_cluster(int cpu) { - return topology_physical_package_id(cpu); + return is_bL_switching_enabled() ? MAX_CLUSTERS: + topology_physical_package_id(cpu); } int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops); diff -Nur linux-3.10.30/drivers/cpufreq/cpufreq-imx6.c linux-3.10.30-cubox-i/drivers/cpufreq/cpufreq-imx6.c --- linux-3.10.30/drivers/cpufreq/cpufreq-imx6.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/cpufreq-imx6.c 2014-03-08 20:33:29.000000000 +0100 @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct regulator *arm_reg; +static struct regulator *pu_reg; +static struct regulator *soc_reg; + +static struct clk *arm_clk; +static struct clk *pll1_sys_clk; +static struct clk *pll1_sw_clk; +static struct clk *step_clk; +static struct clk *pll2_pfd2_396m_clk; + +static struct device *cpu_dev; +static struct cpufreq_frequency_table *freq_table; +static unsigned int transition_latency; +static DEFINE_MUTEX(set_cpufreq_lock); + +struct soc_opp { + u32 arm_freq; + u32 soc_volt; +}; + +static struct soc_opp *imx6_soc_opp; +static u32 soc_opp_count; + +static int imx6_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +static unsigned int imx6_get_speed(unsigned int cpu) +{ + return clk_get_rate(arm_clk) / 1000; +} + +static int imx6_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + struct opp *opp; + unsigned long freq_hz, volt, volt_old; + unsigned int index, soc_opp_index = 0; + int ret; + + mutex_lock(&set_cpufreq_lock); + + ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &index); + if (ret) { + dev_err(cpu_dev, "failed to match target frequency %d: %d\n", + target_freq, ret); + mutex_unlock(&set_cpufreq_lock); + return ret; + } + + freqs.new = freq_table[index].frequency; + freq_hz = freqs.new * 1000; + freqs.old = clk_get_rate(arm_clk) / 1000; + + if (freqs.old == freqs.new) { + mutex_unlock(&set_cpufreq_lock); + return 0; + } + + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + + rcu_read_lock(); + opp = opp_find_freq_ceil(cpu_dev, &freq_hz); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz); + mutex_unlock(&set_cpufreq_lock); + return PTR_ERR(opp); + } + + volt = opp_get_voltage(opp); + rcu_read_unlock(); + volt_old = regulator_get_voltage(arm_reg); + + /* Find the matching VDDSOC/VDDPU operating voltage */ + while (soc_opp_index < soc_opp_count) { + if (freqs.new == imx6_soc_opp[soc_opp_index].arm_freq) + break; + soc_opp_index++; + } + if (soc_opp_index >= soc_opp_count) { + dev_err(cpu_dev, + "Cannot find matching imx6_soc_opp voltage\n"); + mutex_unlock(&set_cpufreq_lock); + return -EINVAL; + } + + dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", + freqs.old / 1000, volt_old / 1000, + freqs.new / 1000, volt / 1000); + + /* + * CPU freq is increasing, so need to ensure + * that bus frequency is increased too. + */ + if (freqs.old == freq_table[0].frequency) + request_bus_freq(BUS_FREQ_HIGH); + + /* scaling up? scale voltage before frequency */ + if (freqs.new > freqs.old) { + if (regulator_is_enabled(pu_reg)) { + ret = regulator_set_voltage_tol(pu_reg, + imx6_soc_opp[soc_opp_index].soc_volt, + 0); + if (ret) { + dev_err(cpu_dev, + "failed to scale vddpu up: %d\n", ret); + goto err1; + } + } + ret = regulator_set_voltage_tol(soc_reg, + imx6_soc_opp[soc_opp_index].soc_volt, 0); + if (ret) { + dev_err(cpu_dev, + "failed to scale vddsoc up: %d\n", ret); + goto err1; + } + ret = regulator_set_voltage_tol(arm_reg, volt, 0); + if (ret) { + dev_err(cpu_dev, + "failed to scale vddarm up: %d\n", ret); + goto err1; + } + } + + /* + * The setpoints are selected per PLL/PDF frequencies, so we need to + * reprogram PLL for frequency scaling. The procedure of reprogramming + * PLL1 is as below. + * + * - Enable pll2_pfd2_396m_clk and reparent pll1_sw_clk to it + * - Reprogram pll1_sys_clk and reparent pll1_sw_clk back to it + * - Disable pll2_pfd2_396m_clk + */ + clk_set_parent(step_clk, pll2_pfd2_396m_clk); + clk_set_parent(pll1_sw_clk, step_clk); + if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) { + clk_set_rate(pll1_sys_clk, freqs.new * 1000); + clk_set_parent(pll1_sw_clk, pll1_sys_clk); + } + + /* Ensure the arm clock divider is what we expect */ + ret = clk_set_rate(arm_clk, freqs.new * 1000); + if (ret) { + dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); + goto err1; + } + + /* scaling down? scale voltage after frequency */ + if (freqs.new < freqs.old) { + ret = regulator_set_voltage_tol(arm_reg, volt, 0); + if (ret) { + dev_warn(cpu_dev, + "failed to scale vddarm down: %d\n", ret); + goto err1; + } + + ret = regulator_set_voltage_tol(soc_reg, + imx6_soc_opp[soc_opp_index].soc_volt, 0); + if (ret) { + dev_err(cpu_dev, + "failed to scale vddsoc down: %d\n", ret); + goto err1; + } + + if (regulator_is_enabled(pu_reg)) { + ret = regulator_set_voltage_tol(pu_reg, + imx6_soc_opp[soc_opp_index].soc_volt, + 0); + if (ret) { + dev_err(cpu_dev, + "failed to scale vddpu down: %d\n", ret); + goto err1; + } + } + } + /* + * If CPU is dropped to the lowest level, release the need + * for a high bus frequency. + */ + if (freqs.new == freq_table[0].frequency) + release_bus_freq(BUS_FREQ_HIGH); + + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + + mutex_unlock(&set_cpufreq_lock); + return 0; +err1: + mutex_unlock(&set_cpufreq_lock); + return -1; +} + +static int imx6_cpufreq_init(struct cpufreq_policy *policy) +{ + int ret; + + ret = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (ret) { + dev_err(cpu_dev, "invalid frequency table: %d\n", ret); + return ret; + } + + policy->cpuinfo.transition_latency = transition_latency; + policy->cur = clk_get_rate(arm_clk) / 1000; + cpumask_setall(policy->cpus); + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + + if (policy->cur > freq_table[0].frequency) + request_bus_freq(BUS_FREQ_HIGH); + + return 0; +} + +static int imx6_cpufreq_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr *imx6_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver imx6_cpufreq_driver = { + .verify = imx6_verify_speed, + .target = imx6_set_target, + .get = imx6_get_speed, + .init = imx6_cpufreq_init, + .exit = imx6_cpufreq_exit, + .name = "imx6-cpufreq", + .attr = imx6_cpufreq_attr, +}; + +static int imx6_cpufreq_pm_notify(struct notifier_block *nb, + unsigned long event, void *dummy) +{ + struct cpufreq_policy *data = cpufreq_cpu_get(0); + static u32 cpufreq_policy_min_pre_suspend; + + /* + * During suspend/resume, When cpufreq driver try to increase + * voltage/freq, it needs to control I2C/SPI to communicate + * with external PMIC to adjust voltage, but these I2C/SPI + * devices may be already suspended, to avoid such scenario, + * we just increase cpufreq to highest setpoint before suspend. + */ + switch (event) { + case PM_SUSPEND_PREPARE: + cpufreq_policy_min_pre_suspend = data->user_policy.min; + data->user_policy.min = data->user_policy.max; + break; + case PM_POST_SUSPEND: + data->user_policy.min = cpufreq_policy_min_pre_suspend; + break; + default: + break; + } + + cpufreq_update_policy(0); + + return NOTIFY_OK; +} + +static struct notifier_block imx6_cpufreq_pm_notifier = { + .notifier_call = imx6_cpufreq_pm_notify, +}; + +static int imx6_cpufreq_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct opp *opp; + unsigned long min_volt = 0, max_volt = 0; + int num, ret; + const struct property *prop; + const __be32 *val; + u32 nr, i; + + cpu_dev = &pdev->dev; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + dev_err(cpu_dev, "failed to find cpu0 node\n"); + return -ENOENT; + } + + cpu_dev->of_node = np; + + arm_clk = devm_clk_get(cpu_dev, "arm"); + pll1_sys_clk = devm_clk_get(cpu_dev, "pll1_sys"); + pll1_sw_clk = devm_clk_get(cpu_dev, "pll1_sw"); + step_clk = devm_clk_get(cpu_dev, "step"); + pll2_pfd2_396m_clk = devm_clk_get(cpu_dev, "pll2_pfd2_396m"); + if (IS_ERR(arm_clk) || IS_ERR(pll1_sys_clk) || IS_ERR(pll1_sw_clk) || + IS_ERR(step_clk) || IS_ERR(pll2_pfd2_396m_clk)) { + dev_err(cpu_dev, "failed to get clocks\n"); + ret = -ENOENT; + goto put_node; + } + + arm_reg = devm_regulator_get(cpu_dev, "arm"); + pu_reg = devm_regulator_get(cpu_dev, "pu"); + soc_reg = devm_regulator_get(cpu_dev, "soc"); + if (IS_ERR(arm_reg) || IS_ERR(pu_reg) || IS_ERR(soc_reg)) { + dev_err(cpu_dev, "failed to get regulators\n"); + ret = -ENOENT; + goto put_node; + } + + /* We expect an OPP table supplied by platform */ + num = opp_get_opp_count(cpu_dev); + if (num < 0) { + ret = num; + dev_err(cpu_dev, "no OPP table is found: %d\n", ret); + goto put_node; + } + + ret = opp_init_cpufreq_table(cpu_dev, &freq_table); + if (ret) { + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); + goto put_node; + } + + prop = of_find_property(np, "fsl,soc-operating-points", NULL); + if (!prop) { + dev_err(cpu_dev, + "fsl,soc-operating-points node not found\n"); + goto free_freq_table; + } + if (!prop->value) { + dev_err(cpu_dev, + "No entries in fsl-soc-operating-points node.\n"); + goto free_freq_table; + } + + /* + * Each OPP is a set of tuples consisting of frequency and + * voltage like . + */ + nr = prop->length / sizeof(u32); + if (nr % 2) { + dev_err(cpu_dev, "Invalid fsl-soc-operating-points list\n"); + goto free_freq_table; + } + + /* Get the VDDSOC/VDDPU voltages that need to track the CPU voltages. */ + imx6_soc_opp = devm_kzalloc(cpu_dev, + sizeof(struct soc_opp) * (nr / 2), + GFP_KERNEL); + + if (imx6_soc_opp == NULL) { + dev_err(cpu_dev, "No Memory for VDDSOC/PU table\n"); + goto free_freq_table; + } + + rcu_read_lock(); + val = prop->value; + + for (i = 0; i < nr / 2; i++) { + unsigned long freq = be32_to_cpup(val++); + unsigned long volt = be32_to_cpup(val++); + + if (i == 0) + min_volt = max_volt = volt; + if (volt < min_volt) + min_volt = volt; + if (volt > max_volt) + max_volt = volt; + opp = opp_find_freq_exact(cpu_dev, + freq * 1000, true); + if (IS_ERR(opp)) { + opp = opp_find_freq_exact(cpu_dev, + freq * 1000, false); + if (IS_ERR(opp)) { + dev_err(cpu_dev, + "freq in soc-operating-points does not \ + match cpufreq table\n"); + rcu_read_unlock(); + goto free_freq_table; + } + } + imx6_soc_opp[i].arm_freq = freq; + imx6_soc_opp[i].soc_volt = volt; + soc_opp_count++; + } + rcu_read_unlock(); + + if (of_property_read_u32(np, "clock-latency", &transition_latency)) + transition_latency = CPUFREQ_ETERNAL; + + /* + * Calculate the ramp time for max voltage change in the + * VDDSOC and VDDPU regulators. + */ + ret = regulator_set_voltage_time(soc_reg, min_volt, max_volt); + if (ret > 0) + transition_latency += ret * 1000; + + ret = regulator_set_voltage_time(pu_reg, min_volt, max_volt); + if (ret > 0) + transition_latency += ret * 1000; + + /* + * OPP is maintained in order of increasing frequency, and + * freq_table initialised from OPP is therefore sorted in the + * same order. + */ + rcu_read_lock(); + opp = opp_find_freq_exact(cpu_dev, + freq_table[0].frequency * 1000, true); + min_volt = opp_get_voltage(opp); + opp = opp_find_freq_exact(cpu_dev, + freq_table[num - 1].frequency * 1000, true); + max_volt = opp_get_voltage(opp); + rcu_read_unlock(); + ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); + if (ret > 0) + transition_latency += ret * 1000; + + mutex_init(&set_cpufreq_lock); + register_pm_notifier(&imx6_cpufreq_pm_notifier); + + ret = cpufreq_register_driver(&imx6_cpufreq_driver); + if (ret) { + dev_err(cpu_dev, "failed register driver: %d\n", ret); + goto free_freq_table; + } + + of_node_put(np); + return 0; + +free_freq_table: + opp_free_cpufreq_table(cpu_dev, &freq_table); +put_node: + of_node_put(np); + return ret; +} + +static int imx6_cpufreq_remove(struct platform_device *pdev) +{ + cpufreq_unregister_driver(&imx6_cpufreq_driver); + opp_free_cpufreq_table(cpu_dev, &freq_table); + + return 0; +} + +static struct platform_driver imx6_cpufreq_platdrv = { + .driver = { + .name = "imx6-cpufreq", + .owner = THIS_MODULE, + }, + .probe = imx6_cpufreq_probe, + .remove = imx6_cpufreq_remove, +}; +module_platform_driver(imx6_cpufreq_platdrv); + +MODULE_AUTHOR("Shawn Guo "); +MODULE_DESCRIPTION("Freescale i.MX6 cpufreq driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/cpufreq/cpufreq_interactive.c linux-3.10.30-cubox-i/drivers/cpufreq/cpufreq_interactive.c --- linux-3.10.30/drivers/cpufreq/cpufreq_interactive.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/cpufreq_interactive.c 2014-03-08 20:33:29.000000000 +0100 @@ -0,0 +1,705 @@ +/* + * drivers/cpufreq/cpufreq_interactive.c + * + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2012-2013 Freescale Semiconductor, 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. + * + * Author: Mike Chan (mike@android.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static atomic_t active_count = ATOMIC_INIT(0); + +struct cpufreq_interactive_cpuinfo { + struct timer_list cpu_timer; + int timer_idlecancel; + u64 time_in_idle; + u64 idle_exit_time; + u64 timer_run_time; + int idling; + u64 freq_change_time; + u64 freq_change_time_in_idle; + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *freq_table; + unsigned int target_freq; + int governor_enabled; +}; + +static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); + +/* Workqueues handle frequency scaling */ +static struct task_struct *up_task; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_down_work; +static cpumask_t up_cpumask; +static spinlock_t up_cpumask_lock; +static cpumask_t down_cpumask; +static spinlock_t down_cpumask_lock; +static struct mutex set_speed_lock; + +/* Hi speed to bump to from lo speed when load burst (default max) */ +static u64 hispeed_freq; + +/* Go to hi speed when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED_LOAD 95 +static unsigned long go_hispeed_load; + +/* + * The minimum amount of time to spend at a frequency before we can ramp down. + */ +#define DEFAULT_MIN_SAMPLE_TIME (20 * USEC_PER_MSEC) +static unsigned long min_sample_time; + +/* + * The sample rate of the timer used to increase frequency + */ +#define DEFAULT_TIMER_RATE (50 * USEC_PER_MSEC) +#define CPUFREQ_IRQ_LEN 60 +#define CPUFREQ_NOTE_LEN 120 +static unsigned long timer_rate; + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +static +#endif +struct cpufreq_governor cpufreq_gov_interactive = { + .name = "interactive", + .governor = cpufreq_governor_interactive, + .max_transition_latency = 10000000, + .owner = THIS_MODULE, +}; + +static void cpufreq_interactive_timer(unsigned long data) +{ + unsigned int delta_idle; + unsigned int delta_time; + int cpu_load; + int load_since_change; + u64 time_in_idle; + u64 idle_exit_time; + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, data); + u64 now_idle; + unsigned int new_freq; + unsigned int index; + unsigned long flags; + + smp_rmb(); + + if (!pcpu->governor_enabled) + goto exit; + + /* + * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time, + * this lets idle exit know the current idle time sample has + * been processed, and idle exit can generate a new sample and + * re-arm the timer. This prevents a concurrent idle + * exit on that CPU from writing a new set of info at the same time + * the timer function runs (the timer function can't use that info + * until more time passes). + */ + time_in_idle = pcpu->time_in_idle; + idle_exit_time = pcpu->idle_exit_time; + now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time); + smp_wmb(); + + /* If we raced with cancelling a timer, skip. */ + if (!idle_exit_time) + goto exit; + + delta_idle = (unsigned int)(now_idle - time_in_idle); + delta_time = (unsigned int)(pcpu->timer_run_time - idle_exit_time); + + /* + * If timer ran less than 1ms after short-term sample started, retry. + */ + if (delta_time < 1000) + goto rearm; + + if (delta_idle > delta_time) + cpu_load = 0; + else + cpu_load = 100 * (delta_time - delta_idle) / delta_time; + + delta_idle = (unsigned int)(now_idle - pcpu->freq_change_time_in_idle); + delta_time = (unsigned int)(pcpu->timer_run_time - + pcpu->freq_change_time); + + if ((delta_time == 0) || (delta_idle > delta_time)) + load_since_change = 0; + else + load_since_change = + 100 * (delta_time - delta_idle) / delta_time; + + /* + * Choose greater of short-term load (since last idle timer + * started or timer function re-armed itself) or long-term load + * (since last frequency change). + */ + if (load_since_change > cpu_load) + cpu_load = load_since_change; + + if (cpu_load >= go_hispeed_load) { + if (pcpu->policy->cur == pcpu->policy->min) + new_freq = hispeed_freq; + else + new_freq = pcpu->policy->max * cpu_load / 100; + } else { + new_freq = pcpu->policy->cur * cpu_load / 100; + } + + if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, + new_freq, CPUFREQ_RELATION_H, + &index)) { + pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", + (int) data); + goto rearm; + } + + new_freq = pcpu->freq_table[index].frequency; + if (pcpu->target_freq == new_freq) + goto rearm_if_notmax; + + /* + * Do not scale down unless we have been at this frequency for the + * minimum sample time. + */ + if (new_freq < pcpu->target_freq) { + if ((pcpu->timer_run_time - pcpu->freq_change_time) + < min_sample_time) + goto rearm; + } + + if (new_freq < pcpu->target_freq) { + pcpu->target_freq = new_freq; + spin_lock_irqsave(&down_cpumask_lock, flags); + cpumask_set_cpu(data, &down_cpumask); + spin_unlock_irqrestore(&down_cpumask_lock, flags); + queue_work(down_wq, &freq_scale_down_work); + } else { + pcpu->target_freq = new_freq; + spin_lock_irqsave(&up_cpumask_lock, flags); + cpumask_set_cpu(data, &up_cpumask); + spin_unlock_irqrestore(&up_cpumask_lock, flags); + wake_up_process(up_task); + } + +rearm_if_notmax: + /* + * Already set max speed and don't see a need to change that, + * wait until next idle to re-evaluate, don't need timer. + */ + if (pcpu->target_freq == pcpu->policy->max) + goto exit; + +rearm: + if (!timer_pending(&pcpu->cpu_timer)) { + /* + * If already at min: if that CPU is idle, don't set timer. + * Else cancel the timer if that CPU goes idle. We don't + * need to re-evaluate speed until the next idle exit. + */ + if (pcpu->target_freq == pcpu->policy->min) { + smp_rmb(); + + if (pcpu->idling) + goto exit; + + pcpu->timer_idlecancel = 1; + } + + pcpu->time_in_idle = get_cpu_idle_time_us( + data, &pcpu->idle_exit_time); + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); + } + +exit: + return; +} + +static void cpufreq_interactive_idle_start(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + int pending; + + pcpu->idling = 1; + smp_wmb(); + if (!pcpu->governor_enabled) + return; + pending = timer_pending(&pcpu->cpu_timer); + + if (pcpu->target_freq != pcpu->policy->min) { +#ifdef CONFIG_SMP + /* + * Entering idle while not at lowest speed. On some + * platforms this can hold the other CPU(s) at that speed + * even though the CPU is idle. Set a timer to re-evaluate + * speed so this idle CPU doesn't hold the other CPUs above + * min indefinitely. This should probably be a quirk of + * the CPUFreq driver. + */ + if (!pending) { + pcpu->time_in_idle = get_cpu_idle_time_us( + smp_processor_id(), &pcpu->idle_exit_time); + pcpu->timer_idlecancel = 0; + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); + } +#endif + } else { + /* + * If at min speed and entering idle after load has + * already been evaluated, and a timer has been set just in + * case the CPU suddenly goes busy, cancel that timer. The + * CPU didn't go busy; we'll recheck things upon idle exit. + */ + if (pending && pcpu->timer_idlecancel) { + del_timer(&pcpu->cpu_timer); + /* + * Ensure last timer run time is after current idle + * sample start time, so next idle exit will always + * start a new idle sampling period. + */ + pcpu->idle_exit_time = 0; + pcpu->timer_idlecancel = 0; + } + } + +} + +static void cpufreq_interactive_idle_end(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + + pcpu->idling = 0; + smp_wmb(); + + /* + * Arm the timer for 1-2 ticks later if not already, and if the timer + * function has already processed the previous load sampling + * interval. (If the timer is not pending but has not processed + * the previous interval, it is probably racing with us on another + * CPU. Let it compute load based on the previous sample and then + * re-arm the timer for another interval when it's done, rather + * than updating the interval start time to be "now", which doesn't + * give the timer function enough time to make a decision on this + * run.) + */ + if (timer_pending(&pcpu->cpu_timer) == 0 && + pcpu->timer_run_time >= pcpu->idle_exit_time && + pcpu->governor_enabled) { + pcpu->time_in_idle = + get_cpu_idle_time_us(smp_processor_id(), + &pcpu->idle_exit_time); + pcpu->timer_idlecancel = 0; + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); + } + +} + +static int cpufreq_interactive_up_task(void *data) +{ + unsigned int cpu; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&up_cpumask_lock, flags); + + if (cpumask_empty(&up_cpumask)) { + spin_unlock_irqrestore(&up_cpumask_lock, flags); + schedule(); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&up_cpumask_lock, flags); + } + + set_current_state(TASK_RUNNING); + cpumask_clear(&up_cpumask); + spin_unlock_irqrestore(&up_cpumask_lock, flags); + + for_each_online_cpu(cpu) { + unsigned int j; + unsigned int max_freq = 0; + + pcpu = &per_cpu(cpuinfo, cpu); + smp_rmb(); + + if (!pcpu->governor_enabled) + continue; + + mutex_lock(&set_speed_lock); + + for_each_online_cpu(j) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, + max_freq, + CPUFREQ_RELATION_H); + mutex_unlock(&set_speed_lock); + + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu, + &pcpu->freq_change_time); + } + } + + return 0; +} + +static void cpufreq_interactive_freq_down(struct work_struct *work) +{ + unsigned int cpu; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + spin_lock_irqsave(&down_cpumask_lock, flags); + cpumask_clear(&down_cpumask); + spin_unlock_irqrestore(&down_cpumask_lock, flags); + + for_each_online_cpu(cpu) { + unsigned int j; + unsigned int max_freq = 0; + + pcpu = &per_cpu(cpuinfo, cpu); + smp_rmb(); + + if (!pcpu->governor_enabled) + continue; + + mutex_lock(&set_speed_lock); + + for_each_online_cpu(j) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, max_freq, + CPUFREQ_RELATION_H); + + mutex_unlock(&set_speed_lock); + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu, + &pcpu->freq_change_time); + } +} + +static ssize_t show_hispeed_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%llu\n", hispeed_freq); +} + +static ssize_t store_hispeed_freq(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + u64 val; + + ret = strict_strtoull(buf, 0, &val); + if (ret < 0) + return ret; + hispeed_freq = val; + return count; +} + +static struct global_attr hispeed_freq_attr = __ATTR(hispeed_freq, 0644, + show_hispeed_freq, store_hispeed_freq); + + +static ssize_t show_go_hispeed_load(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", go_hispeed_load); +} + +static ssize_t store_go_hispeed_load(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + go_hispeed_load = val; + return count; +} + +static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, + show_go_hispeed_load, store_go_hispeed_load); + +static ssize_t show_min_sample_time(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_sample_time); +} + +static ssize_t store_min_sample_time(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + min_sample_time = val; + return count; +} + +static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, + show_min_sample_time, store_min_sample_time); + +static ssize_t show_timer_rate(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", timer_rate); +} + +static ssize_t store_timer_rate(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + timer_rate = val; + return count; +} + +static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, + show_timer_rate, store_timer_rate); + +static struct attribute *interactive_attributes[] = { + &hispeed_freq_attr.attr, + &go_hispeed_load_attr.attr, + &min_sample_time_attr.attr, + &timer_rate_attr.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group = { + .attrs = interactive_attributes, + .name = "interactive", +}; + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event) +{ + int rc; + unsigned int j; + struct cpufreq_interactive_cpuinfo *pcpu; + struct cpufreq_frequency_table *freq_table; + + switch (event) { + case CPUFREQ_GOV_START: + if (!cpu_online(policy->cpu)) + return -EINVAL; + + freq_table = + cpufreq_frequency_get_table(policy->cpu); + + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->policy = policy; + if (pcpu->idling) + pcpu->target_freq = policy->min; + else + pcpu->target_freq = policy->cur; + + pcpu->freq_table = freq_table; + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(j, + &pcpu->freq_change_time); + pcpu->governor_enabled = 1; + smp_wmb(); + } + + if (!hispeed_freq) + hispeed_freq = policy->max; + + /* + * Do not register the idle hook and create sysfs + * entries if we have already done so. + */ + if (atomic_inc_return(&active_count) > 1) + return 0; + + rc = sysfs_create_group(cpufreq_global_kobject, + &interactive_attr_group); + if (rc) + return rc; + + break; + + case CPUFREQ_GOV_STOP: + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->governor_enabled = 0; + smp_wmb(); + del_timer_sync(&pcpu->cpu_timer); + + /* + * Reset idle exit time since we may cancel the timer + * before it can run after the last idle exit time, + * to avoid tripping the check in idle exit for a timer + * that is trying to run. + */ + pcpu->idle_exit_time = 0; + } + + flush_work(&freq_scale_down_work); + if (atomic_dec_return(&active_count) > 0) + return 0; + + sysfs_remove_group(cpufreq_global_kobject, + &interactive_attr_group); + + break; + + case CPUFREQ_GOV_LIMITS: + if (policy->max < policy->cur) + __cpufreq_driver_target(policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > policy->cur) + __cpufreq_driver_target(policy, + policy->min, CPUFREQ_RELATION_L); + break; + } + return 0; +} + +static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, + unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + cpufreq_interactive_idle_start(); + break; + case IDLE_END: + cpufreq_interactive_idle_end(); + break; + } + + return 0; +} + +static struct notifier_block cpufreq_interactive_idle_nb = { + .notifier_call = cpufreq_interactive_idle_notifier, +}; + +static int __init cpufreq_interactive_init(void) +{ + unsigned int i; + struct cpufreq_interactive_cpuinfo *pcpu; + struct sched_param param = { .sched_priority = 99 }; + + go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; + min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + timer_rate = DEFAULT_TIMER_RATE; + + /* Initalize per-cpu timers */ + for_each_possible_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + init_timer(&pcpu->cpu_timer); + pcpu->cpu_timer.function = cpufreq_interactive_timer; + pcpu->cpu_timer.data = i; + } + + up_task = kthread_create(cpufreq_interactive_up_task, NULL, + "kinteractiveup"); + if (IS_ERR(up_task)) + return PTR_ERR(up_task); + + sched_setscheduler_nocheck(up_task, SCHED_FIFO, ¶m); + get_task_struct(up_task); + + /* No rescuer thread, bind to CPU queuing the work for possibly + warm cache (probably doesn't matter much). */ + down_wq = alloc_workqueue("kinteractive_down", 0, 1); + + if (!down_wq) + goto err_freeuptask; + + INIT_WORK(&freq_scale_down_work, + cpufreq_interactive_freq_down); + + spin_lock_init(&up_cpumask_lock); + spin_lock_init(&down_cpumask_lock); + mutex_init(&set_speed_lock); + + idle_notifier_register(&cpufreq_interactive_idle_nb); + + return cpufreq_register_governor(&cpufreq_gov_interactive); + +err_freeuptask: + put_task_struct(up_task); + return -ENOMEM; +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +late_initcall(cpufreq_interactive_init); +#else +module_init(cpufreq_interactive_init); +#endif + +static void __exit cpufreq_interactive_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_interactive); + kthread_stop(up_task); + put_task_struct(up_task); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_interactive_exit); + +MODULE_AUTHOR("Mike Chan "); +MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " + "Latency sensitive workloads"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/cpufreq/cpufreq_ondemand.c linux-3.10.30-cubox-i/drivers/cpufreq/cpufreq_ondemand.c --- linux-3.10.30/drivers/cpufreq/cpufreq_ondemand.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/cpufreq_ondemand.c 2014-03-08 20:33:29.000000000 +0100 @@ -76,6 +76,8 @@ boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model >= 15) return 1; +#else if defined(CONFIG_ARM_IMX6_CPUFREQ) + return 1; #endif return 0; } diff -Nur linux-3.10.30/drivers/cpufreq/cpufreq_stats.c linux-3.10.30-cubox-i/drivers/cpufreq/cpufreq_stats.c --- linux-3.10.30/drivers/cpufreq/cpufreq_stats.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/cpufreq_stats.c 2014-03-08 20:33:29.000000000 +0100 @@ -21,6 +21,9 @@ #include #include #include +#ifdef CONFIG_BL_SWITCHER +#include +#endif static spinlock_t cpufreq_stats_lock; @@ -378,7 +381,7 @@ .notifier_call = cpufreq_stat_notifier_trans }; -static int __init cpufreq_stats_init(void) +static int cpufreq_stats_setup(void) { int ret; unsigned int cpu; @@ -406,7 +409,8 @@ return 0; } -static void __exit cpufreq_stats_exit(void) + +static void cpufreq_stats_cleanup(void) { unsigned int cpu; @@ -421,6 +425,54 @@ } } +#ifdef CONFIG_BL_SWITCHER +static int cpufreq_stats_switcher_notifier(struct notifier_block *nfb, + unsigned long action, void *_arg) +{ + switch (action) { + case BL_NOTIFY_PRE_ENABLE: + case BL_NOTIFY_PRE_DISABLE: + cpufreq_stats_cleanup(); + break; + + case BL_NOTIFY_POST_ENABLE: + case BL_NOTIFY_POST_DISABLE: + cpufreq_stats_setup(); + break; + + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block switcher_notifier = { + .notifier_call = cpufreq_stats_switcher_notifier, +}; +#endif + +static int __init cpufreq_stats_init(void) +{ + int ret; + spin_lock_init(&cpufreq_stats_lock); + + ret = cpufreq_stats_setup(); +#ifdef CONFIG_BL_SWITCHER + if (!ret) + bL_switcher_register_notifier(&switcher_notifier); +#endif + return ret; +} + +static void __exit cpufreq_stats_exit(void) +{ +#ifdef CONFIG_BL_SWITCHER + bL_switcher_unregister_notifier(&switcher_notifier); +#endif + cpufreq_stats_cleanup(); +} + MODULE_AUTHOR("Zou Nan hai "); MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats " "through sysfs filesystem"); diff -Nur linux-3.10.30/drivers/cpufreq/imx6q-cpufreq.c linux-3.10.30-cubox-i/drivers/cpufreq/imx6q-cpufreq.c --- linux-3.10.30/drivers/cpufreq/imx6q-cpufreq.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/imx6q-cpufreq.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,330 +0,0 @@ -/* - * Copyright (C) 2013 Freescale Semiconductor, 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PU_SOC_VOLTAGE_NORMAL 1250000 -#define PU_SOC_VOLTAGE_HIGH 1275000 -#define FREQ_1P2_GHZ 1200000000 - -static struct regulator *arm_reg; -static struct regulator *pu_reg; -static struct regulator *soc_reg; - -static struct clk *arm_clk; -static struct clk *pll1_sys_clk; -static struct clk *pll1_sw_clk; -static struct clk *step_clk; -static struct clk *pll2_pfd2_396m_clk; - -static struct device *cpu_dev; -static struct cpufreq_frequency_table *freq_table; -static unsigned int transition_latency; - -static int imx6q_verify_speed(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, freq_table); -} - -static unsigned int imx6q_get_speed(unsigned int cpu) -{ - return clk_get_rate(arm_clk) / 1000; -} - -static int imx6q_set_target(struct cpufreq_policy *policy, - unsigned int target_freq, unsigned int relation) -{ - struct cpufreq_freqs freqs; - struct opp *opp; - unsigned long freq_hz, volt, volt_old; - unsigned int index; - int ret; - - ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, - relation, &index); - if (ret) { - dev_err(cpu_dev, "failed to match target frequency %d: %d\n", - target_freq, ret); - return ret; - } - - freqs.new = freq_table[index].frequency; - freq_hz = freqs.new * 1000; - freqs.old = clk_get_rate(arm_clk) / 1000; - - if (freqs.old == freqs.new) - return 0; - - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - - rcu_read_lock(); - opp = opp_find_freq_ceil(cpu_dev, &freq_hz); - if (IS_ERR(opp)) { - rcu_read_unlock(); - dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz); - return PTR_ERR(opp); - } - - volt = opp_get_voltage(opp); - rcu_read_unlock(); - volt_old = regulator_get_voltage(arm_reg); - - dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", - freqs.old / 1000, volt_old / 1000, - freqs.new / 1000, volt / 1000); - - /* scaling up? scale voltage before frequency */ - if (freqs.new > freqs.old) { - ret = regulator_set_voltage_tol(arm_reg, volt, 0); - if (ret) { - dev_err(cpu_dev, - "failed to scale vddarm up: %d\n", ret); - return ret; - } - - /* - * Need to increase vddpu and vddsoc for safety - * if we are about to run at 1.2 GHz. - */ - if (freqs.new == FREQ_1P2_GHZ / 1000) { - regulator_set_voltage_tol(pu_reg, - PU_SOC_VOLTAGE_HIGH, 0); - regulator_set_voltage_tol(soc_reg, - PU_SOC_VOLTAGE_HIGH, 0); - } - } - - /* - * The setpoints are selected per PLL/PDF frequencies, so we need to - * reprogram PLL for frequency scaling. The procedure of reprogramming - * PLL1 is as below. - * - * - Enable pll2_pfd2_396m_clk and reparent pll1_sw_clk to it - * - Reprogram pll1_sys_clk and reparent pll1_sw_clk back to it - * - Disable pll2_pfd2_396m_clk - */ - clk_prepare_enable(pll2_pfd2_396m_clk); - clk_set_parent(step_clk, pll2_pfd2_396m_clk); - clk_set_parent(pll1_sw_clk, step_clk); - if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) { - clk_set_rate(pll1_sys_clk, freqs.new * 1000); - /* - * If we are leaving 396 MHz set-point, we need to enable - * pll1_sys_clk and disable pll2_pfd2_396m_clk to keep - * their use count correct. - */ - if (freqs.old * 1000 <= clk_get_rate(pll2_pfd2_396m_clk)) { - clk_prepare_enable(pll1_sys_clk); - clk_disable_unprepare(pll2_pfd2_396m_clk); - } - clk_set_parent(pll1_sw_clk, pll1_sys_clk); - clk_disable_unprepare(pll2_pfd2_396m_clk); - } else { - /* - * Disable pll1_sys_clk if pll2_pfd2_396m_clk is sufficient - * to provide the frequency. - */ - clk_disable_unprepare(pll1_sys_clk); - } - - /* Ensure the arm clock divider is what we expect */ - ret = clk_set_rate(arm_clk, freqs.new * 1000); - if (ret) { - dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); - regulator_set_voltage_tol(arm_reg, volt_old, 0); - return ret; - } - - /* scaling down? scale voltage after frequency */ - if (freqs.new < freqs.old) { - ret = regulator_set_voltage_tol(arm_reg, volt, 0); - if (ret) - dev_warn(cpu_dev, - "failed to scale vddarm down: %d\n", ret); - - if (freqs.old == FREQ_1P2_GHZ / 1000) { - regulator_set_voltage_tol(pu_reg, - PU_SOC_VOLTAGE_NORMAL, 0); - regulator_set_voltage_tol(soc_reg, - PU_SOC_VOLTAGE_NORMAL, 0); - } - } - - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - - return 0; -} - -static int imx6q_cpufreq_init(struct cpufreq_policy *policy) -{ - int ret; - - ret = cpufreq_frequency_table_cpuinfo(policy, freq_table); - if (ret) { - dev_err(cpu_dev, "invalid frequency table: %d\n", ret); - return ret; - } - - policy->cpuinfo.transition_latency = transition_latency; - policy->cur = clk_get_rate(arm_clk) / 1000; - cpumask_setall(policy->cpus); - cpufreq_frequency_table_get_attr(freq_table, policy->cpu); - - return 0; -} - -static int imx6q_cpufreq_exit(struct cpufreq_policy *policy) -{ - cpufreq_frequency_table_put_attr(policy->cpu); - return 0; -} - -static struct freq_attr *imx6q_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -static struct cpufreq_driver imx6q_cpufreq_driver = { - .verify = imx6q_verify_speed, - .target = imx6q_set_target, - .get = imx6q_get_speed, - .init = imx6q_cpufreq_init, - .exit = imx6q_cpufreq_exit, - .name = "imx6q-cpufreq", - .attr = imx6q_cpufreq_attr, -}; - -static int imx6q_cpufreq_probe(struct platform_device *pdev) -{ - struct device_node *np; - struct opp *opp; - unsigned long min_volt, max_volt; - int num, ret; - - cpu_dev = &pdev->dev; - - np = of_find_node_by_path("/cpus/cpu@0"); - if (!np) { - dev_err(cpu_dev, "failed to find cpu0 node\n"); - return -ENOENT; - } - - cpu_dev->of_node = np; - - arm_clk = devm_clk_get(cpu_dev, "arm"); - pll1_sys_clk = devm_clk_get(cpu_dev, "pll1_sys"); - pll1_sw_clk = devm_clk_get(cpu_dev, "pll1_sw"); - step_clk = devm_clk_get(cpu_dev, "step"); - pll2_pfd2_396m_clk = devm_clk_get(cpu_dev, "pll2_pfd2_396m"); - if (IS_ERR(arm_clk) || IS_ERR(pll1_sys_clk) || IS_ERR(pll1_sw_clk) || - IS_ERR(step_clk) || IS_ERR(pll2_pfd2_396m_clk)) { - dev_err(cpu_dev, "failed to get clocks\n"); - ret = -ENOENT; - goto put_node; - } - - arm_reg = devm_regulator_get(cpu_dev, "arm"); - pu_reg = devm_regulator_get(cpu_dev, "pu"); - soc_reg = devm_regulator_get(cpu_dev, "soc"); - if (IS_ERR(arm_reg) || IS_ERR(pu_reg) || IS_ERR(soc_reg)) { - dev_err(cpu_dev, "failed to get regulators\n"); - ret = -ENOENT; - goto put_node; - } - - /* We expect an OPP table supplied by platform */ - num = opp_get_opp_count(cpu_dev); - if (num < 0) { - ret = num; - dev_err(cpu_dev, "no OPP table is found: %d\n", ret); - goto put_node; - } - - ret = opp_init_cpufreq_table(cpu_dev, &freq_table); - if (ret) { - dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); - goto put_node; - } - - if (of_property_read_u32(np, "clock-latency", &transition_latency)) - transition_latency = CPUFREQ_ETERNAL; - - /* - * OPP is maintained in order of increasing frequency, and - * freq_table initialised from OPP is therefore sorted in the - * same order. - */ - rcu_read_lock(); - opp = opp_find_freq_exact(cpu_dev, - freq_table[0].frequency * 1000, true); - min_volt = opp_get_voltage(opp); - opp = opp_find_freq_exact(cpu_dev, - freq_table[--num].frequency * 1000, true); - max_volt = opp_get_voltage(opp); - rcu_read_unlock(); - ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); - if (ret > 0) - transition_latency += ret * 1000; - - /* Count vddpu and vddsoc latency in for 1.2 GHz support */ - if (freq_table[num].frequency == FREQ_1P2_GHZ / 1000) { - ret = regulator_set_voltage_time(pu_reg, PU_SOC_VOLTAGE_NORMAL, - PU_SOC_VOLTAGE_HIGH); - if (ret > 0) - transition_latency += ret * 1000; - ret = regulator_set_voltage_time(soc_reg, PU_SOC_VOLTAGE_NORMAL, - PU_SOC_VOLTAGE_HIGH); - if (ret > 0) - transition_latency += ret * 1000; - } - - ret = cpufreq_register_driver(&imx6q_cpufreq_driver); - if (ret) { - dev_err(cpu_dev, "failed register driver: %d\n", ret); - goto free_freq_table; - } - - of_node_put(np); - return 0; - -free_freq_table: - opp_free_cpufreq_table(cpu_dev, &freq_table); -put_node: - of_node_put(np); - return ret; -} - -static int imx6q_cpufreq_remove(struct platform_device *pdev) -{ - cpufreq_unregister_driver(&imx6q_cpufreq_driver); - opp_free_cpufreq_table(cpu_dev, &freq_table); - - return 0; -} - -static struct platform_driver imx6q_cpufreq_platdrv = { - .driver = { - .name = "imx6q-cpufreq", - .owner = THIS_MODULE, - }, - .probe = imx6q_cpufreq_probe, - .remove = imx6q_cpufreq_remove, -}; -module_platform_driver(imx6q_cpufreq_platdrv); - -MODULE_AUTHOR("Shawn Guo "); -MODULE_DESCRIPTION("Freescale i.MX6Q cpufreq driver"); -MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/cpufreq/vexpress_big_little.c linux-3.10.30-cubox-i/drivers/cpufreq/vexpress_big_little.c --- linux-3.10.30/drivers/cpufreq/vexpress_big_little.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpufreq/vexpress_big_little.c 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,86 @@ +/* + * Vexpress big.LITTLE CPUFreq Interface driver + * + * It provides necessary ops to arm_big_little cpufreq driver and gets + * Frequency information from Device Tree. Freq table in DT must be in KHz. + * + * Copyright (C) 2013 Linaro. + * Viresh Kumar + * + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 +#include +#include +#include +#include +#include +#include "arm_big_little.h" + +static int vexpress_init_opp_table(struct device *cpu_dev) +{ + int i = -1, count, cluster = cpu_to_cluster(cpu_dev->id); + u32 *table; + int ret; + + count = vexpress_spc_get_freq_table(cluster, &table); + if (!table || !count) { + pr_err("SPC controller returned invalid freq table"); + return -EINVAL; + } + + while (++i < count) { + /* FIXME: Voltage value */ + ret = opp_add(cpu_dev, table[i] * 1000, 900000); + if (ret) { + dev_warn(cpu_dev, "%s: Failed to add OPP %d, err: %d\n", + __func__, table[i] * 1000, ret); + return ret; + } + } + + return 0; +} + +static int vexpress_get_transition_latency(struct device *cpu_dev) +{ + /* 1 ms */ + return 1000000; +} + +static struct cpufreq_arm_bL_ops vexpress_bL_ops = { + .name = "vexpress-bL", + .get_transition_latency = vexpress_get_transition_latency, + .init_opp_table = vexpress_init_opp_table, +}; + +static int vexpress_bL_init(void) +{ + if (!vexpress_spc_check_loaded()) { + pr_info("%s: No SPC found\n", __func__); + return -ENOENT; + } + + return bL_cpufreq_register(&vexpress_bL_ops); +} +module_init(vexpress_bL_init); + +static void vexpress_bL_exit(void) +{ + return bL_cpufreq_unregister(&vexpress_bL_ops); +} +module_exit(vexpress_bL_exit); + +MODULE_AUTHOR("Viresh Kumar "); +MODULE_DESCRIPTION("ARM Vexpress big LITTLE cpufreq driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/cpuidle/Kconfig linux-3.10.30-cubox-i/drivers/cpuidle/Kconfig --- linux-3.10.30/drivers/cpuidle/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpuidle/Kconfig 2014-03-08 20:33:30.000000000 +0100 @@ -20,7 +20,7 @@ config CPU_IDLE_GOV_LADDER bool - depends on CPU_IDLE + depends on CPU_IDLE && !NO_HZ default y config CPU_IDLE_GOV_MENU diff -Nur linux-3.10.30/drivers/cpuidle/Makefile linux-3.10.30-cubox-i/drivers/cpuidle/Makefile --- linux-3.10.30/drivers/cpuidle/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpuidle/Makefile 2014-03-08 20:33:30.000000000 +0100 @@ -4,6 +4,6 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o - +obj-$(CONFIG_BIG_LITTLE) += arm_big_little.o obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o diff -Nur linux-3.10.30/drivers/cpuidle/arm_big_little.c linux-3.10.30-cubox-i/drivers/cpuidle/arm_big_little.c --- linux-3.10.30/drivers/cpuidle/arm_big_little.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpuidle/arm_big_little.c 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,183 @@ +/* + * big.LITTLE CPU idle driver. + * + * Copyright (C) 2012 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int bl_cpuidle_simple_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + ktime_t time_start, time_end; + s64 diff; + + time_start = ktime_get(); + + cpu_do_idle(); + + time_end = ktime_get(); + + local_irq_enable(); + + diff = ktime_to_us(ktime_sub(time_end, time_start)); + if (diff > INT_MAX) + diff = INT_MAX; + + dev->last_residency = (int) diff; + + return index; +} + +static int bl_enter_powerdown(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx); + +static struct cpuidle_state bl_cpuidle_set[] __initdata = { + [0] = { + .enter = bl_cpuidle_simple_enter, + .exit_latency = 1, + .target_residency = 1, + .power_usage = UINT_MAX, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "WFI", + .desc = "ARM WFI", + }, + [1] = { + .enter = bl_enter_powerdown, + .exit_latency = 300, + .target_residency = 1000, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "C1", + .desc = "ARM power down", + }, +}; + +struct cpuidle_driver bl_idle_driver = { + .name = "bl_idle", + .owner = THIS_MODULE, + .safe_state_index = 0 +}; + +static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev); + +static int notrace bl_powerdown_finisher(unsigned long arg) +{ + unsigned int mpidr = read_cpuid_mpidr(); + unsigned int cluster = (mpidr >> 8) & 0xf; + unsigned int cpu = mpidr & 0xf; + + mcpm_set_entry_vector(cpu, cluster, cpu_resume); + mcpm_cpu_suspend(0); /* 0 should be replaced with better value here */ + return 1; +} + +/* + * bl_enter_powerdown - Programs CPU to enter the specified state + * @dev: cpuidle device + * @drv: The target state to be programmed + * @idx: state index + * + * Called from the CPUidle framework to program the device to the + * specified target state selected by the governor. + */ +static int bl_enter_powerdown(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx) +{ + struct timespec ts_preidle, ts_postidle, ts_idle; + int ret; + + /* Used to keep track of the total time in idle */ + getnstimeofday(&ts_preidle); + + BUG_ON(!irqs_disabled()); + + cpu_pm_enter(); + + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); + + ret = cpu_suspend((unsigned long) dev, bl_powerdown_finisher); + if (ret) + BUG(); + + mcpm_cpu_powered_up(); + + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); + + cpu_pm_exit(); + + getnstimeofday(&ts_postidle); + local_irq_enable(); + ts_idle = timespec_sub(ts_postidle, ts_preidle); + + dev->last_residency = ts_idle.tv_nsec / NSEC_PER_USEC + + ts_idle.tv_sec * USEC_PER_SEC; + return idx; +} + +/* + * bl_idle_init + * + * Registers the bl specific cpuidle driver with the cpuidle + * framework with the valid set of states. + */ +int __init bl_idle_init(void) +{ + struct cpuidle_device *dev; + int i, cpu_id; + struct cpuidle_driver *drv = &bl_idle_driver; + + if (!of_find_compatible_node(NULL, NULL, "arm,generic")) { + pr_info("%s: No compatible node found\n", __func__); + return -ENODEV; + } + + drv->state_count = (sizeof(bl_cpuidle_set) / + sizeof(struct cpuidle_state)); + + for (i = 0; i < drv->state_count; i++) { + memcpy(&drv->states[i], &bl_cpuidle_set[i], + sizeof(struct cpuidle_state)); + } + + cpuidle_register_driver(drv); + + for_each_cpu(cpu_id, cpu_online_mask) { + pr_err("CPUidle for CPU%d registered\n", cpu_id); + dev = &per_cpu(bl_idle_dev, cpu_id); + dev->cpu = cpu_id; + + dev->state_count = drv->state_count; + + if (cpuidle_register_device(dev)) { + printk(KERN_ERR "%s: Cpuidle register device failed\n", + __func__); + return -EIO; + } + } + + return 0; +} + +device_initcall(bl_idle_init); diff -Nur linux-3.10.30/drivers/cpuidle/cpuidle-calxeda.c linux-3.10.30-cubox-i/drivers/cpuidle/cpuidle-calxeda.c --- linux-3.10.30/drivers/cpuidle/cpuidle-calxeda.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/cpuidle/cpuidle-calxeda.c 2014-03-08 20:33:30.000000000 +0100 @@ -37,20 +37,6 @@ extern void highbank_set_cpu_jump(int cpu, void *jump_addr); extern void *scu_base_addr; -static inline unsigned int get_auxcr(void) -{ - unsigned int val; - asm("mrc p15, 0, %0, c1, c0, 1 @ get AUXCR" : "=r" (val) : : "cc"); - return val; -} - -static inline void set_auxcr(unsigned int val) -{ - asm volatile("mcr p15, 0, %0, c1, c0, 1 @ set AUXCR" - : : "r" (val) : "cc"); - isb(); -} - static noinline void calxeda_idle_restore(void) { set_cr(get_cr() | CR_C); diff -Nur linux-3.10.30/drivers/crypto/caam/Kconfig linux-3.10.30-cubox-i/drivers/crypto/caam/Kconfig --- linux-3.10.30/drivers/crypto/caam/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/Kconfig 2014-03-08 20:33:30.000000000 +0100 @@ -1,6 +1,6 @@ config CRYPTO_DEV_FSL_CAAM tristate "Freescale CAAM-Multicore driver backend" - depends on FSL_SOC + depends on FSL_SOC || ARCH_MXC help Enables the driver module for Freescale's Cryptographic Accelerator and Assurance Module (CAAM), also known as the SEC version 4 (SEC4). @@ -98,3 +98,51 @@ To compile this as a module, choose M here: the module will be called caamrng. + +config CRYPTO_DEV_FSL_CAAM_RNG_TEST + boolean "Test caam rng" + depends on CRYPTO_DEV_FSL_CAAM_RNG_API + default n + help + Selecting this will enable self-test for caam rng. + +config CRYPTO_DEV_FSL_CAAM_SM + tristate "CAAM Secure Memory / Keystore API (EXPERIMENTAL)" + default n + help + Enables use of a prototype kernel-level Keystore API with CAAM + Secure Memory for insertion/extraction of bus-protected secrets. + +config CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE + int "Size of each keystore slot in Secure Memory" + depends on CRYPTO_DEV_FSL_CAAM_SM + range 5 9 + default 7 + help + Select size of allocation units to divide Secure Memory pages into + (the size of a "slot" as referenced inside the API code). + Established as powers of two. + Examples: + 5 => 32 bytes + 6 => 64 bytes + 7 => 128 bytes + 8 => 256 bytes + 9 => 512 bytes + +config CRYPTO_DEV_FSL_CAAM_SM_TEST + tristate "CAAM Secure Memory - Keystore Test/Example (EXPERIMENTAL)" + depends on CRYPTO_DEV_FSL_CAAM_SM + default n + help + Example thread to exercise the Keystore API and to verify that + stored and recovered secrets can be used for general purpose + encryption/decryption. + +config CRYPTO_DEV_FSL_CAAM_SECVIO + tristate "CAAM/SNVS Security Violation Handler (EXPERIMENTAL)" + depends on CRYPTO_DEV_FSL_CAAM + default n + help + Enables installation of an interrupt handler with registrable + handler functions which can be specified to act on the consequences + of a security violation. diff -Nur linux-3.10.30/drivers/crypto/caam/Makefile linux-3.10.30-cubox-i/drivers/crypto/caam/Makefile --- linux-3.10.30/drivers/crypto/caam/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/Makefile 2014-03-08 20:33:30.000000000 +0100 @@ -6,5 +6,8 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o +obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM) += sm_store.o +obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST) += sm_test.o +obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO) += secvio.o caam-objs := ctrl.o jr.o error.o key_gen.o diff -Nur linux-3.10.30/drivers/crypto/caam/caamalg.c linux-3.10.30-cubox-i/drivers/crypto/caam/caamalg.c --- linux-3.10.30/drivers/crypto/caam/caamalg.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/caamalg.c 2014-03-08 20:33:30.000000000 +0100 @@ -1,7 +1,7 @@ /* * caam - Freescale FSL CAAM support for crypto API * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. * * Based on talitos crypto API driver. * @@ -53,6 +53,7 @@ #include "error.h" #include "sg_sw_sec4.h" #include "key_gen.h" +#include /* * crypto alg @@ -290,6 +291,8 @@ desc_bytes(desc), 1); #endif + dma_sync_single_for_cpu(jrdev, ctx->sh_desc_enc_dma, desc_bytes(desc), + DMA_TO_DEVICE); /* * Job Descriptor and Shared Descriptors * must all fit into the 64-word Descriptor h/w Buffer @@ -357,6 +360,8 @@ DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + dma_sync_single_for_cpu(jrdev, ctx->sh_desc_dec_dma, desc_bytes(desc), + DMA_TO_DEVICE); /* * Job Descriptor and Shared Descriptors @@ -440,6 +445,8 @@ DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + dma_sync_single_for_cpu(jrdev, ctx->sh_desc_givenc_dma, + desc_bytes(desc), DMA_TO_DEVICE); return 0; } @@ -523,6 +530,9 @@ DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, ctx->split_key_pad_len + enckeylen, 1); #endif + dma_sync_single_for_device(jrdev, ctx->key_dma, + ctx->split_key_pad_len + enckeylen, + DMA_TO_DEVICE); ctx->enckeylen = enckeylen; @@ -561,6 +571,7 @@ return -ENOMEM; } ctx->enckeylen = keylen; + dma_sync_single_for_device(jrdev, ctx->key_dma, keylen, DMA_TO_DEVICE); /* ablkcipher_encrypt shared descriptor */ desc = ctx->sh_desc_enc; @@ -579,10 +590,15 @@ /* Propagate errors from shared to job descriptor */ append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD); - /* Load iv */ - append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT | - LDST_CLASS_1_CCB | tfm->ivsize); - + /* load IV */ + if (strncmp(ablkcipher->base.__crt_alg->cra_name, "ctr(aes)", 8) == 0) { + append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT | + LDST_CLASS_1_CCB | tfm->ivsize | + (16 << LDST_OFFSET_SHIFT)); + } else { + append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT | + LDST_CLASS_1_CCB | tfm->ivsize); + } /* Load operation */ append_operation(desc, ctx->class1_alg_type | OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT); @@ -602,6 +618,9 @@ DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_enc_dma, + desc_bytes(desc), DMA_TO_DEVICE); + /* ablkcipher_decrypt shared descriptor */ desc = ctx->sh_desc_dec; @@ -622,11 +641,20 @@ set_jump_tgt_here(desc, jump_cmd); /* load IV */ - append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT | - LDST_CLASS_1_CCB | tfm->ivsize); + if (strncmp(ablkcipher->base.__crt_alg->cra_name, "ctr(aes)", 8) == 0) { + append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT | + LDST_CLASS_1_CCB | tfm->ivsize | + (16 << LDST_OFFSET_SHIFT)); - /* Choose operation */ - append_dec_op1(desc, ctx->class1_alg_type); + append_operation(desc, ctx->class1_alg_type | + OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT); + } else { + append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT | + LDST_CLASS_1_CCB | tfm->ivsize); + + /* Choose operation */ + append_dec_op1(desc, ctx->class1_alg_type); + } /* Perform operation */ ablkcipher_append_src_dst(desc); @@ -647,6 +675,8 @@ DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_dec_dma, + desc_bytes(desc), DMA_TO_DEVICE); return ret; } @@ -1154,7 +1184,7 @@ dst_nents = sg_count(req->dst, req->cryptlen, &dst_chained); sgc = dma_map_sg_chained(jrdev, req->assoc, assoc_nents ? : 1, - DMA_TO_DEVICE, assoc_chained); + DMA_BIDIRECTIONAL, assoc_chained); if (likely(req->src == req->dst)) { sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1, DMA_BIDIRECTIONAL, src_chained); @@ -1178,9 +1208,10 @@ sec4_sg_len += dst_nents; sec4_sg_bytes = sec4_sg_len * sizeof(struct sec4_sg_entry); + dma_sync_single_for_device(jrdev, iv_dma, ivsize, DMA_TO_DEVICE); /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kmalloc(sizeof(struct aead_edesc) + desc_bytes + + edesc = kzalloc(sizeof(struct aead_edesc) + desc_bytes + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, "could not allocate extended descriptor\n"); @@ -1221,6 +1252,8 @@ sg_to_sec4_sg_last(req->dst, dst_nents, edesc->sec4_sg + sec4_sg_index, 0); } + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, sec4_sg_bytes, + DMA_TO_DEVICE); return edesc; } @@ -1336,7 +1369,7 @@ dst_nents = sg_count(req->dst, req->cryptlen, &dst_chained); sgc = dma_map_sg_chained(jrdev, req->assoc, assoc_nents ? : 1, - DMA_TO_DEVICE, assoc_chained); + DMA_BIDIRECTIONAL, assoc_chained); if (likely(req->src == req->dst)) { sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1, DMA_BIDIRECTIONAL, src_chained); @@ -1369,8 +1402,10 @@ sec4_sg_bytes = sec4_sg_len * sizeof(struct sec4_sg_entry); + dma_sync_single_for_device(jrdev, iv_dma, ivsize, DMA_TO_DEVICE); + /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kmalloc(sizeof(struct aead_edesc) + desc_bytes + + edesc = kzalloc(sizeof(struct aead_edesc) + desc_bytes + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, "could not allocate extended descriptor\n"); @@ -1412,6 +1447,8 @@ sg_to_sec4_sg_last(req->dst, dst_nents, edesc->sec4_sg + sec4_sg_index, 0); } + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, sec4_sg_bytes, + DMA_TO_DEVICE); return edesc; } @@ -1505,6 +1542,7 @@ * If so, include it. If not, create scatterlist. */ iv_dma = dma_map_single(jrdev, req->info, ivsize, DMA_TO_DEVICE); + dma_sync_single_for_device(jrdev, iv_dma, ivsize, DMA_TO_DEVICE); if (!src_nents && iv_dma + ivsize == sg_dma_address(req->src)) iv_contig = true; else @@ -1513,7 +1551,7 @@ sizeof(struct sec4_sg_entry); /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kmalloc(sizeof(struct ablkcipher_edesc) + desc_bytes + + edesc = kzalloc(sizeof(struct ablkcipher_edesc) + desc_bytes + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, "could not allocate extended descriptor\n"); @@ -1545,6 +1583,9 @@ sec4_sg_bytes, DMA_TO_DEVICE); edesc->iv_dma = iv_dma; + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, sec4_sg_bytes, + DMA_TO_DEVICE); + #ifdef DEBUG print_hex_dump(KERN_ERR, "ablkcipher sec4_sg@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, edesc->sec4_sg, @@ -2008,6 +2049,70 @@ }, /* ablkcipher descriptor */ { + .name = "ecb(des)", + .driver_name = "ecb-des-caam", + .blocksize = DES_BLOCK_SIZE, + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .template_ablkcipher = { + .setkey = ablkcipher_setkey, + .encrypt = ablkcipher_encrypt, + .decrypt = ablkcipher_decrypt, + .geniv = "eseqiv", + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, + .ivsize = DES_BLOCK_SIZE, + }, + .class1_alg_type = OP_ALG_ALGSEL_DES | OP_ALG_AAI_ECB, + }, + { + .name = "ecb(arc4)", + .driver_name = "ecb-arc4-caam", + .blocksize = ARC4_BLOCK_SIZE, + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .template_ablkcipher = { + .setkey = ablkcipher_setkey, + .encrypt = ablkcipher_encrypt, + .decrypt = ablkcipher_decrypt, + .geniv = "eseqiv", + .min_keysize = ARC4_MIN_KEY_SIZE, + .max_keysize = ARC4_MAX_KEY_SIZE, + .ivsize = ARC4_BLOCK_SIZE, + }, + .class1_alg_type = OP_ALG_ALGSEL_ARC4 | OP_ALG_AAI_ECB + }, + { + .name = "ecb(aes)", + .driver_name = "ecb-aes-caam", + .blocksize = AES_BLOCK_SIZE, + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .template_ablkcipher = { + .setkey = ablkcipher_setkey, + .encrypt = ablkcipher_encrypt, + .decrypt = ablkcipher_decrypt, + .geniv = "eseqiv", + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + }, + .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_ECB, + }, + { + .name = "ctr(aes)", + .driver_name = "ctr-aes-caam", + .blocksize = AES_BLOCK_SIZE, + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .template_ablkcipher = { + .setkey = ablkcipher_setkey, + .encrypt = ablkcipher_encrypt, + .decrypt = ablkcipher_decrypt, + .geniv = "eseqiv", + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + }, + .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CTR_MOD128, + }, + { .name = "cbc(aes)", .driver_name = "cbc-aes-caam", .blocksize = AES_BLOCK_SIZE, @@ -2024,6 +2129,22 @@ .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC, }, { + .name = "ecb(des3_ede)", + .driver_name = "ecb-des3-caam", + .blocksize = DES3_EDE_BLOCK_SIZE, + .type = CRYPTO_ALG_TYPE_ABLKCIPHER, + .template_ablkcipher = { + .setkey = ablkcipher_setkey, + .encrypt = ablkcipher_encrypt, + .decrypt = ablkcipher_decrypt, + .geniv = "eseqiv", + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + }, + .class1_alg_type = OP_ALG_ALGSEL_3DES | OP_ALG_AAI_ECB, + }, + { .name = "cbc(des3_ede)", .driver_name = "cbc-3des-caam", .blocksize = DES3_EDE_BLOCK_SIZE, @@ -2079,7 +2200,7 @@ * distribute tfms across job rings to ensure in-order * crypto request processing per tfm */ - ctx->jrdev = priv->jrdev[(tgt_jr / 2) % priv->total_jobrs]; + ctx->jrdev = priv->algapi_jr[(tgt_jr / 2) % priv->num_jrs_for_algapi]; /* copy descriptor header template value */ ctx->class1_alg_type = OP_TYPE_CLASS1_ALG | caam_alg->class1_alg_type; @@ -2116,6 +2237,7 @@ struct device *ctrldev; struct caam_drv_private *priv; struct caam_crypto_alg *t_alg, *n; + int i, err; dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); if (!dev_node) { @@ -2125,21 +2247,33 @@ } pdev = of_find_device_by_node(dev_node); - if (!pdev) + if (!pdev) { + of_node_put(dev_node); return; + } ctrldev = &pdev->dev; - of_node_put(dev_node); priv = dev_get_drvdata(ctrldev); - if (!priv->alg_list.next) + if (!priv->alg_list.next) { + of_node_put(dev_node); return; + } list_for_each_entry_safe(t_alg, n, &priv->alg_list, entry) { crypto_unregister_alg(&t_alg->crypto_alg); list_del(&t_alg->entry); kfree(t_alg); } + + for (i = 0; i < priv->total_jobrs; i++) { + err = caam_jr_deregister(priv->algapi_jr[i]); + if (err < 0) + break; + } + kfree(priv->algapi_jr); + + of_node_put(dev_node); } static struct caam_crypto_alg *caam_alg_alloc(struct device *ctrldev, @@ -2167,8 +2301,12 @@ alg->cra_blocksize = template->blocksize; alg->cra_alignmask = 0; alg->cra_ctxsize = sizeof(struct caam_ctx); - alg->cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_KERN_DRIVER_ONLY | - template->type; + alg->cra_flags = CRYPTO_ALG_ASYNC | template->type; + +#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY + alg->cra_flags |= CRYPTO_ALG_KERN_DRIVER_ONLY; +#endif + switch (template->type) { case CRYPTO_ALG_TYPE_ABLKCIPHER: alg->cra_type = &crypto_ablkcipher_type; @@ -2192,9 +2330,11 @@ { struct device_node *dev_node; struct platform_device *pdev; - struct device *ctrldev; + struct device *ctrldev, **jrdev; struct caam_drv_private *priv; - int i = 0, err = 0; + int i = 0, err = 0, md_limit = 0; + int des_inst, aes_inst, md_inst; + u64 cha_inst; dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); if (!dev_node) { @@ -2204,21 +2344,81 @@ } pdev = of_find_device_by_node(dev_node); - if (!pdev) + if (!pdev) { + of_node_put(dev_node); return -ENODEV; + } ctrldev = &pdev->dev; priv = dev_get_drvdata(ctrldev); - of_node_put(dev_node); INIT_LIST_HEAD(&priv->alg_list); + jrdev = kmalloc(sizeof(*jrdev) * priv->total_jobrs, GFP_KERNEL); + if (!jrdev) { + of_node_put(dev_node); + return -ENOMEM; + } + + for (i = 0; i < priv->total_jobrs; i++) { + err = caam_jr_register(ctrldev, &jrdev[i]); + if (err < 0) + break; + } + if (err < 0 && i == 0) { + dev_err(ctrldev, "algapi error in job ring registration: %d\n", + err); + of_node_put(dev_node); + kfree(jrdev); + return err; + } + + priv->num_jrs_for_algapi = i; + priv->algapi_jr = jrdev; atomic_set(&priv->tfm_count, -1); - /* register crypto algorithms the device supports */ + /* + * register crypto algorithms the device supports + * first, detect presence of DES, AES, and MD blocks. If MD present, + * determine limit of supported digest size + */ + cha_inst = rd_reg64(&priv->ctrl->perfmon.cha_num); + des_inst = (cha_inst & CHA_ID_DES_MASK) >> CHA_ID_DES_SHIFT; + aes_inst = (cha_inst & CHA_ID_AES_MASK) >> CHA_ID_AES_SHIFT; + md_inst = (cha_inst & CHA_ID_MD_MASK) >> CHA_ID_MD_SHIFT; + if (md_inst) { + md_limit = SHA512_DIGEST_SIZE; + if ((rd_reg64(&priv->ctrl->perfmon.cha_id) & CHA_ID_MD_MASK) + == CHA_ID_MD_LP256) /* LP256 limits digest size */ + md_limit = SHA256_DIGEST_SIZE; + } + for (i = 0; i < ARRAY_SIZE(driver_algs); i++) { - /* TODO: check if h/w supports alg */ struct caam_crypto_alg *t_alg; + bool done = false; + +authencesn: + /* + * All registrable algs in this module require a blockcipher + * All aead algs require message digests, so check them for + * instantiation and size. + */ + if (driver_algs[i].type == CRYPTO_ALG_TYPE_AEAD) { + /* If no MD instantiated, or MD too small, skip */ + if ((!md_inst) || + (driver_algs[i].template_aead.maxauthsize > + md_limit)) + continue; + } + /* If DES alg, and CHA not instantiated, skip */ + if ((driver_algs[i].class1_alg_type & OP_ALG_ALGSEL_3DES) || + (driver_algs[i].class1_alg_type & OP_ALG_ALGSEL_DES)) + if (!des_inst) + continue; + /* If AES alg, and CHA not instantiated, skip */ + if (driver_algs[i].class1_alg_type & OP_ALG_ALGSEL_AES) + if (!aes_inst) + continue; t_alg = caam_alg_alloc(ctrldev, &driver_algs[i]); if (IS_ERR(t_alg)) { @@ -2233,13 +2433,35 @@ dev_warn(ctrldev, "%s alg registration failed\n", t_alg->crypto_alg.cra_driver_name); kfree(t_alg); - } else + } else { list_add_tail(&t_alg->entry, &priv->alg_list); + dev_info(ctrldev, "%s\n", + t_alg->crypto_alg.cra_driver_name); + + if (driver_algs[i].type == CRYPTO_ALG_TYPE_AEAD && + !memcmp(driver_algs[i].name, "authenc", 7) && + !done) { + char *name; + + name = driver_algs[i].name; + memmove(name + 10, name + 7, strlen(name) - 7); + memcpy(name + 7, "esn", 3); + + name = driver_algs[i].driver_name; + memmove(name + 10, name + 7, strlen(name) - 7); + memcpy(name + 7, "esn", 3); + + done = true; + goto authencesn; + } + } } + if (!list_empty(&priv->alg_list)) dev_info(ctrldev, "%s algorithms registered in /proc/crypto\n", (char *)of_get_property(dev_node, "compatible", NULL)); + of_node_put(dev_node); return err; } diff -Nur linux-3.10.30/drivers/crypto/caam/caamhash.c linux-3.10.30-cubox-i/drivers/crypto/caam/caamhash.c --- linux-3.10.30/drivers/crypto/caam/caamhash.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/caamhash.c 2014-03-08 20:33:30.000000000 +0100 @@ -1,7 +1,7 @@ /* * caam - Freescale FSL CAAM support for ahash functions of crypto API * - * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. * * Based on caamalg.c crypto API driver. * @@ -62,6 +62,7 @@ #include "error.h" #include "sg_sw_sec4.h" #include "key_gen.h" +#include #define CAAM_CRA_PRIORITY 3000 @@ -116,6 +117,7 @@ u8 key[CAAM_MAX_HASH_KEY_SIZE]; dma_addr_t key_dma; int ctx_len; + unsigned int key_len; unsigned int split_key_len; unsigned int split_key_pad_len; }; @@ -167,6 +169,7 @@ dma_addr_t buf_dma; buf_dma = dma_map_single(jrdev, buf, buflen, DMA_TO_DEVICE); + dma_sync_single_for_device(jrdev, buf_dma, buflen, DMA_TO_DEVICE); dma_to_sec4_sg_one(sec4_sg, buf_dma, buflen, 0); return buf_dma; @@ -209,6 +212,9 @@ u32 flag) { state->ctx_dma = dma_map_single(jrdev, state->caam_ctx, ctx_len, flag); + if ((flag == DMA_TO_DEVICE) || (flag == DMA_BIDIRECTIONAL)) + dma_sync_single_for_device(jrdev, state->ctx_dma, ctx_len, + flag); dma_to_sec4_sg_one(sec4_sg, state->ctx_dma, ctx_len, 0); } @@ -220,6 +226,13 @@ KEY_DEST_MDHA_SPLIT | KEY_ENC); } +static inline void append_key_axcbc(u32 *desc, struct caam_hash_ctx *ctx) +{ + append_key_as_imm(desc, ctx->key, ctx->key_len, + ctx->key_len, CLASS_1 | + KEY_DEST_CLASS_REG); +} + /* Append key if it has been set */ static inline void init_sh_desc_key_ahash(u32 *desc, struct caam_hash_ctx *ctx) { @@ -241,6 +254,25 @@ append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD); } +static inline void init_sh_desc_key_axcbc(u32 *desc, struct caam_hash_ctx *ctx) +{ + u32 *key_jump_cmd; + + init_sh_desc(desc, HDR_SHARE_SERIAL); + + if (ctx->key_len) { + key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL | + JUMP_COND_SHRD); + + append_key_axcbc(desc, ctx); + + set_jump_tgt_here(desc, key_jump_cmd); + } + + /* Propagate errors from shared to job descriptor */ + append_cmd(desc, SET_OK_NO_PROP_ERRORS | CMD_LOAD); + +} /* * For ahash read data from seqin following state->caam_ctx, * and write resulting class2 context to seqout, which may be state->caam_ctx @@ -260,6 +292,20 @@ LDST_SRCDST_BYTE_CONTEXT); } +static inline void axcbc_append_load_str(u32 *desc, int digestsize) +{ + /* Calculate remaining bytes to read */ + append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ); + + /* Read remaining bytes */ + append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_LAST1 | + FIFOLD_TYPE_MSG | KEY_VLF); + + /* Store class1 context bytes */ + append_seq_store(desc, digestsize, LDST_CLASS_1_CCB | + LDST_SRCDST_BYTE_CONTEXT); +} + /* * For ahash update, final and finup, import context, read and write to seqout */ @@ -282,6 +328,27 @@ ahash_append_load_str(desc, digestsize); } +/* + * For ahash update, final and finup, import context, read and write to seqout + */ +static inline void axcbc_ctx_data_to_out(u32 *desc, u32 op, u32 state, + int digestsize, + struct caam_hash_ctx *ctx) +{ + init_sh_desc_key_axcbc(desc, ctx); + + /* Import context from software */ + append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT | + LDST_CLASS_1_CCB | ctx->ctx_len); + + /* Class 1 operation */ + append_operation(desc, op | state | OP_ALG_ENCRYPT); + + /* + * Load from buf and/or src and write to req->result or state->context + */ + axcbc_append_load_str(desc, digestsize); +} /* For ahash firsts and digest, read and write to seqout */ static inline void ahash_data_to_out(u32 *desc, u32 op, u32 state, int digestsize, struct caam_hash_ctx *ctx) @@ -297,6 +364,21 @@ ahash_append_load_str(desc, digestsize); } +/* For ahash firsts and digest, read and write to seqout */ +static inline void axcbc_data_to_out(u32 *desc, u32 op, u32 state, + int digestsize, struct caam_hash_ctx *ctx) +{ + init_sh_desc_key_axcbc(desc, ctx); + + /* Class 1 operation */ + append_operation(desc, op | state | OP_ALG_ENCRYPT); + + /* + * Load from buf and/or src and write to req->result or state->context + */ + axcbc_append_load_str(desc, digestsize); +} + static int ahash_set_sh_desc(struct crypto_ahash *ahash) { struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); @@ -352,6 +434,8 @@ print_hex_dump(KERN_ERR, "ahash update first shdesc@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_update_first_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* ahash_final shared descriptor */ desc = ctx->sh_desc_fin; @@ -370,6 +454,8 @@ DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_fin_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* ahash_finup shared descriptor */ desc = ctx->sh_desc_finup; @@ -388,6 +474,8 @@ DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_finup_dma, + desc_bytes(desc), DMA_TO_DEVICE); /* ahash_digest shared descriptor */ desc = ctx->sh_desc_digest; @@ -407,10 +495,130 @@ DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_digest_dma, + desc_bytes(desc), DMA_TO_DEVICE); return 0; } +static int axcbc_set_sh_desc(struct crypto_ahash *ahash) +{ + struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); + int digestsize = crypto_ahash_digestsize(ahash); + struct device *jrdev = ctx->jrdev; + u32 have_key = 0; + u32 *desc; + + /* ahash_update shared descriptor */ + desc = ctx->sh_desc_update; + + init_sh_desc(desc, HDR_SHARE_SERIAL); + + /* Import context from software */ + append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_BYTE_CONTEXT | + LDST_CLASS_1_CCB | ctx->ctx_len); + + /* Class 1 operation */ + append_operation(desc, ctx->alg_type | OP_ALG_AS_UPDATE | + OP_ALG_ENCRYPT); + + /* Load data and write to result or context */ + axcbc_append_load_str(desc, ctx->ctx_len); + + ctx->sh_desc_update_dma = dma_map_single(jrdev, desc, desc_bytes(desc), + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, ctx->sh_desc_update_dma)) { + dev_err(jrdev, "unable to map shared descriptor\n"); + return -ENOMEM; + } +#ifdef DEBUG + print_hex_dump(KERN_ERR, "ahash update shdesc@"xstr(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); +#endif + + /* ahash_update_first shared descriptor */ + desc = ctx->sh_desc_update_first; + + axcbc_data_to_out(desc, have_key | ctx->alg_type, OP_ALG_AS_INIT, + ctx->ctx_len, ctx); + + ctx->sh_desc_update_first_dma = dma_map_single(jrdev, desc, + desc_bytes(desc), + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, ctx->sh_desc_update_first_dma)) { + dev_err(jrdev, "unable to map shared descriptor\n"); + return -ENOMEM; + } +#ifdef DEBUG + print_hex_dump(KERN_ERR, "ahash update first shdesc@"xstr(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); +#endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_update_first_dma, + desc_bytes(desc), DMA_TO_DEVICE); + + /* ahash_final shared descriptor */ + desc = ctx->sh_desc_fin; + + axcbc_ctx_data_to_out(desc, have_key | ctx->alg_type, + OP_ALG_AS_FINALIZE, digestsize, ctx); + + ctx->sh_desc_fin_dma = dma_map_single(jrdev, desc, desc_bytes(desc), + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, ctx->sh_desc_fin_dma)) { + dev_err(jrdev, "unable to map shared descriptor\n"); + return -ENOMEM; + } +#ifdef DEBUG + print_hex_dump(KERN_ERR, "ahash final shdesc@"xstr(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, desc, + desc_bytes(desc), 1); +#endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_fin_dma, + desc_bytes(desc), DMA_TO_DEVICE); + + /* ahash_finup shared descriptor */ + desc = ctx->sh_desc_finup; + + axcbc_ctx_data_to_out(desc, have_key | ctx->alg_type, + OP_ALG_AS_FINALIZE, digestsize, ctx); + + ctx->sh_desc_finup_dma = dma_map_single(jrdev, desc, desc_bytes(desc), + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, ctx->sh_desc_finup_dma)) { + dev_err(jrdev, "unable to map shared descriptor\n"); + return -ENOMEM; + } +#ifdef DEBUG + print_hex_dump(KERN_ERR, "ahash finup shdesc@"xstr(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, desc, + desc_bytes(desc), 1); +#endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_finup_dma, + desc_bytes(desc), DMA_TO_DEVICE); + + /* ahash_digest shared descriptor */ + desc = ctx->sh_desc_digest; + + axcbc_data_to_out(desc, have_key | ctx->alg_type, OP_ALG_AS_INITFINAL, + digestsize, ctx); + + ctx->sh_desc_digest_dma = dma_map_single(jrdev, desc, + desc_bytes(desc), + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, ctx->sh_desc_digest_dma)) { + dev_err(jrdev, "unable to map shared descriptor\n"); + return -ENOMEM; + } +#ifdef DEBUG + print_hex_dump(KERN_ERR, "ahash digest shdesc@"xstr(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, desc, + desc_bytes(desc), 1); +#endif + dma_sync_single_for_device(jrdev, ctx->sh_desc_digest_dma, + desc_bytes(desc), DMA_TO_DEVICE); + + return 0; +} static int gen_split_hash_key(struct caam_hash_ctx *ctx, const u8 *key_in, u32 keylen) { @@ -444,6 +652,8 @@ kfree(desc); return -ENOMEM; } + dma_sync_single_for_device(jrdev, src_dma, *keylen, DMA_TO_DEVICE); + dst_dma = dma_map_single(jrdev, (void *)key_out, digestsize, DMA_FROM_DEVICE); if (dma_mapping_error(jrdev, dst_dma)) { @@ -487,6 +697,7 @@ *keylen = digestsize; dma_unmap_single(jrdev, src_dma, *keylen, DMA_TO_DEVICE); + dma_sync_single_for_cpu(jrdev, dst_dma, digestsize, DMA_FROM_DEVICE); dma_unmap_single(jrdev, dst_dma, digestsize, DMA_FROM_DEVICE); kfree(desc); @@ -544,6 +755,10 @@ dev_err(jrdev, "unable to map key i/o memory\n"); return -ENOMEM; } + + dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->split_key_pad_len, + DMA_TO_DEVICE); + #ifdef DEBUG print_hex_dump(KERN_ERR, "ctx.key@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, @@ -564,6 +779,25 @@ return -EINVAL; } +static int axcbc_setkey(struct crypto_ahash *ahash, + const u8 *key, unsigned int keylen) +{ + struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); + int ret = 0; + + ctx->key_len = keylen; + memcpy(ctx->key, key, keylen); + +#ifdef DEBUG + print_hex_dump(KERN_ERR, "ctx.key@"xstr(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, ctx->key, + ctx->key_len, 1); +#endif + + ret = axcbc_set_sh_desc(ahash); + + return ret; +} /* * ahash_edesc - s/w-extended ahash descriptor * @dst_dma: physical mapped address of req->result @@ -591,8 +825,11 @@ if (edesc->src_nents) dma_unmap_sg_chained(dev, req->src, edesc->src_nents, DMA_TO_DEVICE, edesc->chained); - if (edesc->dst_dma) + if (edesc->dst_dma) { + dma_sync_single_for_cpu(dev, edesc->dst_dma, dst_len, + DMA_FROM_DEVICE); dma_unmap_single(dev, edesc->dst_dma, dst_len, DMA_FROM_DEVICE); + } if (edesc->sec4_sg_bytes) dma_unmap_single(dev, edesc->sec4_sg_dma, @@ -607,8 +844,12 @@ struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash); struct caam_hash_state *state = ahash_request_ctx(req); - if (state->ctx_dma) + if (state->ctx_dma) { + if ((flag == DMA_FROM_DEVICE) || (flag == DMA_BIDIRECTIONAL)) + dma_sync_single_for_cpu(dev, state->ctx_dma, + ctx->ctx_len, flag); dma_unmap_single(dev, state->ctx_dma, ctx->ctx_len, flag); + } ahash_unmap(dev, edesc, req, dst_len); } @@ -802,7 +1043,7 @@ * allocate space for base edesc and hw desc commands, * link tables */ - edesc = kmalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + + edesc = kzalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, @@ -851,6 +1092,9 @@ append_seq_out_ptr(desc, state->ctx_dma, ctx->ctx_len, 0); + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, + sec4_sg_bytes, DMA_TO_DEVICE); + #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, @@ -904,7 +1148,7 @@ sec4_sg_bytes = (1 + (buflen ? 1 : 0)) * sizeof(struct sec4_sg_entry); /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kmalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + + edesc = kzalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, "could not allocate extended descriptor\n"); @@ -936,6 +1180,9 @@ edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, sec4_sg_bytes, + DMA_TO_DEVICE); + #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); @@ -980,7 +1227,7 @@ sizeof(struct sec4_sg_entry); /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kmalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + + edesc = kzalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, "could not allocate extended descriptor\n"); @@ -1015,6 +1262,9 @@ edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, sec4_sg_bytes, + DMA_TO_DEVICE); + #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); @@ -1055,7 +1305,7 @@ sec4_sg_bytes = src_nents * sizeof(struct sec4_sg_entry); /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kmalloc(sizeof(struct ahash_edesc) + sec4_sg_bytes + + edesc = kzalloc(sizeof(struct ahash_edesc) + sec4_sg_bytes + DESC_JOB_IO_LEN, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, "could not allocate extended descriptor\n"); @@ -1065,6 +1315,7 @@ DESC_JOB_IO_LEN; edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->src_nents = src_nents; edesc->chained = chained; @@ -1082,6 +1333,9 @@ } append_seq_in_ptr(desc, src_dma, req->nbytes, options); + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, + edesc->sec4_sg_bytes, DMA_TO_DEVICE); + edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); @@ -1120,7 +1374,7 @@ int sh_len; /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kmalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN, + edesc = kzalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, "could not allocate extended descriptor\n"); @@ -1139,6 +1393,8 @@ digestsize); edesc->src_nents = 0; + dma_sync_single_for_device(jrdev, state->buf_dma, buflen, + DMA_TO_DEVICE); #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); @@ -1191,7 +1447,7 @@ * allocate space for base edesc and hw desc commands, * link tables */ - edesc = kmalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + + edesc = kzalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, @@ -1227,6 +1483,8 @@ map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, + sec4_sg_bytes, DMA_TO_DEVICE); #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, @@ -1288,7 +1546,7 @@ sizeof(struct sec4_sg_entry); /* allocate space for base edesc and hw desc commands, link tables */ - edesc = kmalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + + edesc = kzalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, "could not allocate extended descriptor\n"); @@ -1320,6 +1578,9 @@ edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, sec4_sg_bytes, + DMA_TO_DEVICE); + #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); @@ -1374,7 +1635,7 @@ * allocate space for base edesc and hw desc commands, * link tables */ - edesc = kmalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + + edesc = kzalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + sec4_sg_bytes, GFP_DMA | flags); if (!edesc) { dev_err(jrdev, @@ -1413,6 +1674,8 @@ map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + dma_sync_single_for_device(jrdev, edesc->sec4_sg_dma, + sec4_sg_bytes, DMA_TO_DEVICE); #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"xstr(__LINE__)": ", DUMP_PREFIX_ADDRESS, 16, 4, desc, @@ -1460,6 +1723,8 @@ state->final = ahash_final_no_ctx; state->current_buf = 0; + state->buflen_0 = 0; + state->buflen_1 = 0; return 0; } @@ -1649,6 +1914,28 @@ .alg_type = OP_ALG_ALGSEL_MD5, .alg_op = OP_ALG_ALGSEL_MD5 | OP_ALG_AAI_HMAC, }, + { + .name = "xcbc(aes)", + .driver_name = "xcbc-aes-caam", + .hmac_name = "xcbc(aes)", + .hmac_driver_name = "xcbc-aes-caam", + .blocksize = XCBC_MAC_BLOCK_WORDS * 4, + .template_ahash = { + .init = ahash_init, + .update = ahash_update, + .final = ahash_final, + .finup = ahash_finup, + .digest = ahash_digest, + .export = ahash_export, + .import = ahash_import, + .setkey = axcbc_setkey, + .halg = { + .digestsize = XCBC_MAC_DIGEST_SIZE, + }, + }, + .alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_XCBC_MAC, + .alg_op = OP_ALG_ALGSEL_AES, + }, }; struct caam_hash_alg { @@ -1702,6 +1989,39 @@ return ret; } +static int caam_axcbc_cra_init(struct crypto_tfm *tfm) +{ + struct crypto_ahash *ahash = __crypto_ahash_cast(tfm); + struct crypto_alg *base = tfm->__crt_alg; + struct hash_alg_common *halg = + container_of(base, struct hash_alg_common, base); + struct ahash_alg *alg = + container_of(halg, struct ahash_alg, halg); + struct caam_hash_alg *caam_hash = + container_of(alg, struct caam_hash_alg, ahash_alg); + struct caam_hash_ctx *ctx = crypto_tfm_ctx(tfm); + struct caam_drv_private *priv = dev_get_drvdata(caam_hash->ctrldev); + int tgt_jr = atomic_inc_return(&priv->tfm_count); + int ret = 0; + + /* + * distribute tfms across job rings to ensure in-order + * crypto request processing per tfm + */ + ctx->jrdev = priv->jrdev[tgt_jr % priv->total_jobrs]; + + /* copy descriptor header template value */ + ctx->alg_type = OP_TYPE_CLASS1_ALG | caam_hash->alg_type; + ctx->alg_op = OP_TYPE_CLASS1_ALG | caam_hash->alg_op; + + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct caam_hash_state)); + + ret = axcbc_set_sh_desc(ahash); + + return ret; +} + static void caam_hash_cra_exit(struct crypto_tfm *tfm) { struct caam_hash_ctx *ctx = crypto_tfm_ctx(tfm); @@ -1747,21 +2067,26 @@ } pdev = of_find_device_by_node(dev_node); - if (!pdev) + if (!pdev) { + of_node_put(dev_node); return; + } ctrldev = &pdev->dev; - of_node_put(dev_node); priv = dev_get_drvdata(ctrldev); - if (!priv->hash_list.next) + if (!priv->hash_list.next) { + of_node_put(dev_node); return; + } list_for_each_entry_safe(t_alg, n, &priv->hash_list, entry) { crypto_unregister_ahash(&t_alg->ahash_alg); list_del(&t_alg->entry); kfree(t_alg); } + + of_node_put(dev_node); } static struct caam_hash_alg * @@ -1794,7 +2119,11 @@ template->driver_name); } alg->cra_module = THIS_MODULE; - alg->cra_init = caam_hash_cra_init; + + if (strstr(alg->cra_name, "xcbc") > 0) + alg->cra_init = caam_axcbc_cra_init; + else + alg->cra_init = caam_hash_cra_init; alg->cra_exit = caam_hash_cra_exit; alg->cra_ctxsize = sizeof(struct caam_hash_ctx); alg->cra_priority = CAAM_CRA_PRIORITY; @@ -1816,7 +2145,8 @@ struct platform_device *pdev; struct device *ctrldev; struct caam_drv_private *priv; - int i = 0, err = 0; + int i = 0, err = 0, md_limit = 0, md_inst; + u64 cha_inst; dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); if (!dev_node) { @@ -1826,22 +2156,36 @@ } pdev = of_find_device_by_node(dev_node); - if (!pdev) + if (!pdev) { + of_node_put(dev_node); return -ENODEV; - + } ctrldev = &pdev->dev; priv = dev_get_drvdata(ctrldev); - of_node_put(dev_node); INIT_LIST_HEAD(&priv->hash_list); atomic_set(&priv->tfm_count, -1); - /* register crypto algorithms the device supports */ + /* register algorithms the device supports */ + cha_inst = rd_reg64(&priv->ctrl->perfmon.cha_num); + md_inst = (cha_inst & CHA_ID_MD_MASK) >> CHA_ID_MD_SHIFT; + if (md_inst) { + md_limit = SHA512_DIGEST_SIZE; + if ((rd_reg64(&priv->ctrl->perfmon.cha_id) & CHA_ID_MD_MASK) + == CHA_ID_MD_LP256) /* LP256 limits digest size */ + md_limit = SHA256_DIGEST_SIZE; + } + for (i = 0; i < ARRAY_SIZE(driver_hash); i++) { - /* TODO: check if h/w supports alg */ struct caam_hash_alg *t_alg; + /* If no MD instantiated, or MD too small, skip */ + if ((!md_inst) || + (driver_hash[i].template_ahash.halg.digestsize > + md_limit)) + continue; + /* register hmac version */ t_alg = caam_hash_alloc(ctrldev, &driver_hash[i], true); if (IS_ERR(t_alg)) { @@ -1877,6 +2221,7 @@ list_add_tail(&t_alg->entry, &priv->hash_list); } + of_node_put(dev_node); return err; } diff -Nur linux-3.10.30/drivers/crypto/caam/caamrng.c linux-3.10.30-cubox-i/drivers/crypto/caam/caamrng.c --- linux-3.10.30/drivers/crypto/caam/caamrng.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/caamrng.c 2014-03-08 20:33:30.000000000 +0100 @@ -1,7 +1,7 @@ /* * caam - Freescale FSL CAAM support for hw_random * - * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. * * Based on caamalg.c crypto API driver. * @@ -76,13 +76,16 @@ struct buf_data bufs[2]; }; -static struct caam_rng_ctx rng_ctx; +static struct caam_rng_ctx *rng_ctx; static inline void rng_unmap_buf(struct device *jrdev, struct buf_data *bd) { - if (bd->addr) + if (bd->addr) { + dma_sync_single_for_cpu(jrdev, bd->addr, RN_BUF_SIZE, + DMA_FROM_DEVICE); dma_unmap_single(jrdev, bd->addr, RN_BUF_SIZE, DMA_FROM_DEVICE); + } } static inline void rng_unmap_ctx(struct caam_rng_ctx *ctx) @@ -137,7 +140,7 @@ static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait) { - struct caam_rng_ctx *ctx = &rng_ctx; + struct caam_rng_ctx *ctx = rng_ctx; struct buf_data *bd = &ctx->bufs[ctx->current_buf]; int next_buf_idx, copied_idx; int err; @@ -206,6 +209,9 @@ ctx->sh_desc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), DMA_TO_DEVICE); + dma_sync_single_for_device(jrdev, ctx->sh_desc_dma, desc_bytes(desc), + DMA_TO_DEVICE); + #ifdef DEBUG print_hex_dump(KERN_ERR, "rng shdesc@: ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); @@ -237,13 +243,56 @@ struct buf_data *bd; for (i = 0; i < 2; i++) { - bd = &rng_ctx.bufs[i]; + bd = &rng_ctx->bufs[i]; if (atomic_read(&bd->empty) == BUF_PENDING) wait_for_completion(&bd->filled); } - rng_unmap_ctx(&rng_ctx); + rng_unmap_ctx(rng_ctx); +} + +#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST +static inline void test_len(struct hwrng *rng, size_t len, bool wait) +{ + u8 *buf; + int real_len; + + buf = kzalloc(sizeof(u8) * len, GFP_KERNEL); + real_len = rng->read(rng, buf, len, wait); + if (real_len == 0 && wait) + pr_err("WAITING FAILED\n"); + pr_info("wanted %d bytes, got %d\n", len, real_len); + print_hex_dump(KERN_INFO, "random bytes@: ", DUMP_PREFIX_ADDRESS, + 16, 4, buf, real_len, 1); + kfree(buf); +} + +static inline void test_mode_once(struct hwrng *rng, bool wait) +{ +#define TEST_CHUNK (RN_BUF_SIZE / 4) + + test_len(rng, TEST_CHUNK, wait); + test_len(rng, RN_BUF_SIZE * 2, wait); + test_len(rng, RN_BUF_SIZE * 2 - TEST_CHUNK, wait); +} + +static inline void test_mode(struct hwrng *rng, bool wait) +{ +#define TEST_PASS 1 + int i; + + for (i = 0; i < TEST_PASS; i++) + test_mode_once(rng, wait); +} + +static void self_test(struct hwrng *rng) +{ + pr_info("testing without waiting\n"); + test_mode(rng, false); + pr_info("testing with waiting\n"); + test_mode(rng, true); } +#endif static void caam_init_buf(struct caam_rng_ctx *ctx, int buf_id) { @@ -298,7 +347,17 @@ priv = dev_get_drvdata(ctrldev); of_node_put(dev_node); - caam_init_rng(&rng_ctx, priv->jrdev[0]); + /* Check RNG present in hardware before registration */ + if (!(rd_reg64(&priv->ctrl->perfmon.cha_num) & CHA_ID_RNG_MASK)) + return -ENODEV; + + rng_ctx = kmalloc(sizeof(struct caam_rng_ctx), GFP_KERNEL | GFP_DMA); + + caam_init_rng(rng_ctx, priv->jrdev[0]); + +#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST + self_test(&caam_rng); +#endif dev_info(priv->jrdev[0], "registering rng-caam\n"); return hwrng_register(&caam_rng); diff -Nur linux-3.10.30/drivers/crypto/caam/compat.h linux-3.10.30-cubox-i/drivers/crypto/caam/compat.h --- linux-3.10.30/drivers/crypto/caam/compat.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/compat.h 2014-03-08 20:33:30.000000000 +0100 @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -23,6 +25,10 @@ #include #include #include + +#ifdef CONFIG_ARM /* needs the clock control subsystem */ +#include +#endif #include #include diff -Nur linux-3.10.30/drivers/crypto/caam/ctrl.c linux-3.10.30-cubox-i/drivers/crypto/caam/ctrl.c --- linux-3.10.30/drivers/crypto/caam/ctrl.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/ctrl.c 2014-03-08 20:33:30.000000000 +0100 @@ -2,7 +2,7 @@ * CAAM control-plane driver backend * Controller-level driver, kernel property detection, initialization * - * Copyright 2008-2012 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. */ #include "compat.h" @@ -12,6 +12,10 @@ #include "desc_constr.h" #include "error.h" #include "ctrl.h" +#include "sm.h" + +/* Used to capture the array of job rings */ +struct device **caam_jr_dev; static int caam_remove(struct platform_device *pdev) { @@ -40,6 +44,13 @@ /* Unmap controller region */ iounmap(&topregs->ctrl); +#ifdef CONFIG_ARM + /* shut clocks off before finalizing shutdown */ + clk_disable(ctrlpriv->caam_ipg); + clk_disable(ctrlpriv->caam_mem); + clk_disable(ctrlpriv->caam_aclk); +#endif + kfree(ctrlpriv->jrdev); kfree(ctrlpriv); @@ -70,9 +81,13 @@ */ append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW); +} + +static void generate_secure_keys_desc(u32 *desc) +{ /* generate secure keys (non-test) */ append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG | - OP_ALG_RNG4_SK); + OP_ALG_RNG4_SK); } struct instantiate_result { @@ -95,7 +110,7 @@ complete(&instantiation->completion); } -static int instantiate_rng(struct device *jrdev) +static int instantiate_rng(struct device *jrdev, u32 keys_generated) { struct instantiate_result instantiation; @@ -110,7 +125,14 @@ } build_instantiation_desc(desc); + + /* If keys have not been generated, add op code to generate key. */ + if (!keys_generated) + generate_secure_keys_desc(desc); + desc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), DMA_TO_DEVICE); + dma_sync_single_for_device(jrdev, desc_dma, desc_bytes(desc), + DMA_TO_DEVICE); init_completion(&instantiation.completion); ret = caam_jr_enqueue(jrdev, desc, rng4_init_done, &instantiation); if (!ret) { @@ -142,16 +164,18 @@ topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; r4tst = &topregs->ctrl.r4tst[0]; + val = rd_reg32(&r4tst->rtmctl); /* put RNG4 into program mode */ setbits32(&r4tst->rtmctl, RTMCTL_PRGM); - /* 1600 clocks per sample */ + /* Set clocks per sample to the default, and divider to zero */ val = rd_reg32(&r4tst->rtsdctl); - val = (val & ~RTSDCTL_ENT_DLY_MASK) | (1600 << RTSDCTL_ENT_DLY_SHIFT); + val = (val & ~RTSDCTL_ENT_DLY_MASK) | + (RNG4_ENT_CLOCKS_SAMPLE << RTSDCTL_ENT_DLY_SHIFT); wr_reg32(&r4tst->rtsdctl, val); /* min. freq. count */ - wr_reg32(&r4tst->rtfrqmin, 400); + wr_reg32(&r4tst->rtfrqmin, RNG4_ENT_CLOCKS_SAMPLE / 4); /* max. freq. count */ - wr_reg32(&r4tst->rtfrqmax, 6400); + wr_reg32(&r4tst->rtfrqmax, RNG4_ENT_CLOCKS_SAMPLE * 8); /* put RNG4 into run mode */ clrbits32(&r4tst->rtmctl, RTMCTL_PRGM); } @@ -176,7 +200,20 @@ {0x0A14, 1, 3}, {0x0A14, 2, 4}, {0x0A16, 1, 4}, - {0x0A11, 1, 4} + {0x0A11, 1, 4}, + {0x0A10, 3, 4}, + {0x0A18, 1, 4}, + {0x0A11, 2, 5}, + {0x0A12, 2, 5}, + {0x0A13, 1, 5}, + {0x0A1C, 1, 5}, + {0x0A12, 4, 6}, + {0x0A13, 2, 6}, + {0x0A16, 2, 6}, + {0x0A18, 2, 6}, + {0x0A1A, 1, 6}, + {0x0A1C, 2, 6}, + {0x0A17, 1, 6} }; int i; @@ -189,6 +226,18 @@ } EXPORT_SYMBOL(caam_get_era); +/* + * Return a job ring device. This is available so outside + * entities can gain direct access to the job ring. For now, + * this function returns the first job ring (at index 0). + */ +struct device *caam_get_jrdev(void) +{ + return caam_jr_dev[0]; +} +EXPORT_SYMBOL(caam_get_jrdev); + + /* Probe routine for CAAM top (controller) level */ static int caam_probe(struct platform_device *pdev) { @@ -198,6 +247,7 @@ struct device_node *nprop, *np; struct caam_ctrl __iomem *ctrl; struct caam_full __iomem *topregs; + struct snvs_full __iomem *snvsregs; struct caam_drv_private *ctrlpriv; #ifdef CONFIG_DEBUG_FS struct caam_perfmon *perfmon; @@ -227,6 +277,90 @@ /* Get the IRQ of the controller (for security violations only) */ ctrlpriv->secvio_irq = of_irq_to_resource(nprop, 0, NULL); + /* Get SNVS register Page */ + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-snvs"); + + if (!np) + return -ENODEV; + + snvsregs = of_iomap(np, 0); + ctrlpriv->snvs = snvsregs; + /* Get CAAM-SM node and of_iomap() and save */ + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-sm"); + + if (!np) + return -ENODEV; + + ctrlpriv->sm_base = of_iomap(np, 0); + ctrlpriv->sm_size = 0x3fff; + +/* + * ARM targets tend to have clock control subsystems that can + * enable/disable clocking to our device. Turn clocking on to proceed + */ +#ifdef CONFIG_ARM + ctrlpriv->caam_ipg = devm_clk_get(&ctrlpriv->pdev->dev, "caam_ipg"); + if (IS_ERR(ctrlpriv->caam_ipg)) { + ret = PTR_ERR(ctrlpriv->caam_ipg); + dev_err(&ctrlpriv->pdev->dev, + "can't identify CAAM ipg clk: %d\n", ret); + return -ENODEV; + } + ctrlpriv->caam_mem = devm_clk_get(&ctrlpriv->pdev->dev, "caam_mem"); + if (IS_ERR(ctrlpriv->caam_mem)) { + ret = PTR_ERR(ctrlpriv->caam_mem); + dev_err(&ctrlpriv->pdev->dev, + "can't identify CAAM secure mem clk: %d\n", ret); + return -ENODEV; + } + ctrlpriv->caam_aclk = devm_clk_get(&ctrlpriv->pdev->dev, "caam_aclk"); + if (IS_ERR(ctrlpriv->caam_aclk)) { + ret = PTR_ERR(ctrlpriv->caam_aclk); + dev_err(&ctrlpriv->pdev->dev, + "can't identify CAAM aclk clk: %d\n", ret); + return -ENODEV; + } + + ret = clk_prepare(ctrlpriv->caam_ipg); + if (ret < 0) { + dev_err(&pdev->dev, "can't prepare CAAM ipg clock: %d\n", ret); + return -ENODEV; + } + ret = clk_prepare(ctrlpriv->caam_mem); + if (ret < 0) { + dev_err(&pdev->dev, "can't prepare CAAM secure mem clock: %d\n", ret); + return -ENODEV; + } + ret = clk_prepare(ctrlpriv->caam_aclk); + if (ret < 0) { + dev_err(&pdev->dev, "can't prepare CAAM aclk clock: %d\n", ret); + return -ENODEV; + } + + ret = clk_enable(ctrlpriv->caam_ipg); + if (ret < 0) { + dev_err(&pdev->dev, "can't enable CAAM ipg clock: %d\n", ret); + return -ENODEV; + } + ret = clk_enable(ctrlpriv->caam_mem); + if (ret < 0) { + dev_err(&pdev->dev, "can't enable CAAM secure mem clock: %d\n", ret); + return -ENODEV; + } + ret = clk_enable(ctrlpriv->caam_aclk); + if (ret < 0) { + dev_err(&pdev->dev, "can't enable CAAM aclk clock: %d\n", ret); + return -ENODEV; + } + + pr_debug("%s caam_ipg clock:%d\n", __func__, + (int)clk_get_rate(ctrlpriv->caam_ipg)); + pr_debug("%s caam_mem clock:%d\n", __func__, + (int)clk_get_rate(ctrlpriv->caam_mem)); + pr_debug("%s caam_aclk clock:%d\n", __func__, + (int)clk_get_rate(ctrlpriv->caam_aclk)); +#endif + /* * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel, * long pointers in master configuration register @@ -234,6 +368,22 @@ setbits32(&topregs->ctrl.mcr, MCFGR_WDENABLE | (sizeof(dma_addr_t) == sizeof(u64) ? MCFGR_LONG_PTR : 0)); +#ifdef CONFIG_ARCH_MX6 + /* + * ERRATA: mx6 devices have an issue wherein AXI bus transactions + * may not occur in the correct order. This isn't a problem running + * single descriptors, but can be if running multiple concurrent + * descriptors. Reworking the driver to throttle to single requests + * is impractical, thus the workaround is to limit the AXI pipeline + * to a depth of 1 (from it's default of 4) to preclude this situation + * from occurring. + */ + wr_reg32(&topregs->ctrl.mcr, + (rd_reg32(&topregs->ctrl.mcr) & ~(MCFGR_AXIPIPE_MASK)) | + ((1 << MCFGR_AXIPIPE_SHIFT) & MCFGR_AXIPIPE_MASK)); +#endif + + /* Set DMA masks according to platform ranging */ if (sizeof(dma_addr_t) == sizeof(u64)) if (of_device_is_compatible(nprop, "fsl,sec-v5.0")) dma_set_mask(dev, DMA_BIT_MASK(40)); @@ -265,13 +415,36 @@ ring = 0; ctrlpriv->total_jobrs = 0; for_each_compatible_node(np, NULL, "fsl,sec-v4.0-job-ring") { - caam_jr_probe(pdev, np, ring); + ret = caam_jr_probe(pdev, np, ring); + if (ret < 0) { + /* + * Job ring not found, error out. At some + * point, we should enhance job ring handling + * to allow for non-consecutive job rings to + * be found. + */ + pr_err("fsl,sec-v4.0-job-ring not found "); + pr_err("(ring %d)\n", ring); + return ret; + } ctrlpriv->total_jobrs++; ring++; } + if (!ring) { for_each_compatible_node(np, NULL, "fsl,sec4.0-job-ring") { - caam_jr_probe(pdev, np, ring); + ret = caam_jr_probe(pdev, np, ring); + if (ret < 0) { + /* + * Job ring not found, error out. At some + * point, we should enhance job ring handling + * to allow for non-consecutive job rings to + * be found. + */ + pr_err("fsl,sec4.0-job-ring not found "); + pr_err("(ring %d)\n", ring); + return ret; + } ctrlpriv->total_jobrs++; ring++; } @@ -294,19 +467,40 @@ } /* - * RNG4 based SECs (v5+) need special initialization prior - * to executing any descriptors + * RNG4 based SECs (v5+ | >= i.MX6) need special initialization prior + * to executing any descriptors. If there's a problem with init, + * remove other subsystems and return; internal padding functions + * cannot run without an RNG. This procedure assumes a single RNG4 + * instance. */ - if (of_device_is_compatible(nprop, "fsl,sec-v5.0")) { - kick_trng(pdev); - ret = instantiate_rng(ctrlpriv->jrdev[0]); - if (ret) { - caam_remove(pdev); - return ret; + if ((rd_reg64(&topregs->ctrl.perfmon.cha_id) & CHA_ID_RNG_MASK) + == CHA_ID_RNG_4) { + struct rng4tst __iomem *r4tst; + u32 rdsta, rng_if, rng_skvn; + + /* + * Check to see if the RNG has already been instantiated. + * If either the state 0 or 1 instantiated flags are set, + * then don't continue on and try to instantiate the RNG + * again. + */ + r4tst = &topregs->ctrl.r4tst[0]; + rdsta = rd_reg32(&r4tst->rdsta); /* Read RDSTA register */ + + /* Check IF bit for non-deterministic instantiation */ + rng_if = rdsta & RDSTA_IF; + + /* Check SKVN bit for non-deterministic key generation */ + rng_skvn = rdsta & RDSTA_SKVN; + if (!rng_if) { + kick_trng(pdev); + ret = instantiate_rng(ctrlpriv->jrdev[0], rng_skvn); + if (ret) { + caam_remove(pdev); + return -ENODEV; + } + ctrlpriv->rng_inst++; } - - /* Enable RDB bit so that RNG works faster */ - setbits32(&topregs->ctrl.scfgr, SCFGR_RDBENABLE); } /* NOTE: RTIC detection ought to go here, around Si time */ diff -Nur linux-3.10.30/drivers/crypto/caam/desc.h linux-3.10.30-cubox-i/drivers/crypto/caam/desc.h --- linux-3.10.30/drivers/crypto/caam/desc.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/desc.h 2014-03-08 20:33:30.000000000 +0100 @@ -2,19 +2,35 @@ * CAAM descriptor composition header * Definitions to support CAAM descriptor instruction generation * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. */ #ifndef DESC_H #define DESC_H +/* + * 16-byte hardware scatter/gather table + * An 8-byte table exists in the hardware spec, but has never been + * implemented to date. The 8/16 option is selected at RTL-compile-time. + * and this selection is visible in the Compile Time Parameters Register + */ + +#define SEC4_SG_LEN_EXT 0x80000000 /* Entry points to table */ +#define SEC4_SG_LEN_FIN 0x40000000 /* Last ent in table */ +#define SEC4_SG_BPID_MASK 0x000000ff +#define SEC4_SG_BPID_SHIFT 16 +#define SEC4_SG_LEN_MASK 0x3fffffff /* Excludes EXT and FINAL */ +#define SEC4_SG_OFFS_MASK 0x00001fff + struct sec4_sg_entry { +#ifdef CONFIG_64BIT u64 ptr; -#define SEC4_SG_LEN_FIN 0x40000000 -#define SEC4_SG_LEN_EXT 0x80000000 +#else + u32 reserved; + u32 ptr; +#endif u32 len; - u8 reserved; - u8 buf_pool_id; + u16 buf_pool_id; u16 offset; }; @@ -1087,6 +1103,23 @@ #define OP_PCL_PKPROT_ECC 0x0002 #define OP_PCL_PKPROT_F2M 0x0001 +/* Blob protocol protinfo bits */ +#define OP_PCL_BLOB_TK 0x0200 +#define OP_PCL_BLOB_EKT 0x0100 + +#define OP_PCL_BLOB_K2KR_MEM 0x0000 +#define OP_PCL_BLOB_K2KR_C1KR 0x0010 +#define OP_PCL_BLOB_K2KR_C2KR 0x0030 +#define OP_PCL_BLOB_K2KR_AFHAS 0x0050 +#define OP_PCL_BLOB_K2KR_C2KR_SPLIT 0x0070 + +#define OP_PCL_BLOB_PTXT_SECMEM 0x0008 +#define OP_PCL_BLOB_BLACK 0x0004 + +#define OP_PCL_BLOB_FMT_NORMAL 0x0000 +#define OP_PCL_BLOB_FMT_MSTR 0x0002 +#define OP_PCL_BLOB_FMT_TEST 0x0003 + /* For non-protocol/alg-only op commands */ #define OP_ALG_TYPE_SHIFT 24 #define OP_ALG_TYPE_MASK (0x7 << OP_ALG_TYPE_SHIFT) @@ -1600,4 +1633,28 @@ #define NFIFOENTRY_PLEN_SHIFT 0 #define NFIFOENTRY_PLEN_MASK (0xFF << NFIFOENTRY_PLEN_SHIFT) +/* + * PDB internal definitions + */ + +/* IPSec ESP CBC Encap/Decap Options */ +#define PDBOPTS_ESPCBC_ARSNONE 0x00 /* no antireplay window */ +#define PDBOPTS_ESPCBC_ARS32 0x40 /* 32-entry antireplay window */ +#define PDBOPTS_ESPCBC_ARS64 0xc0 /* 64-entry antireplay window */ +#define PDBOPTS_ESPCBC_IVSRC 0x20 /* IV comes from internal random gen */ +#define PDBOPTS_ESPCBC_ESN 0x10 /* extended sequence included */ +#define PDBOPTS_ESPCBC_OUTFMT 0x08 /* output only decapsulation (decap) */ +#define PDBOPTS_ESPCBC_IPHDRSRC 0x08 /* IP header comes from PDB (encap) */ +#define PDBOPTS_ESPCBC_INCIPHDR 0x04 /* Prepend IP header to output frame */ +#define PDBOPTS_ESPCBC_IPVSN 0x02 /* process IPv6 header */ +#define PDBOPTS_ESPCBC_TUNNEL 0x01 /* tunnel mode next-header byte */ + +#define ARC4_BLOCK_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_MIN_KEY_SIZE 1 + +#define XCBC_MAC_DIGEST_SIZE 16 +#define XCBC_MAC_BLOCK_WORDS 16 + + #endif /* DESC_H */ diff -Nur linux-3.10.30/drivers/crypto/caam/intern.h linux-3.10.30-cubox-i/drivers/crypto/caam/intern.h --- linux-3.10.30/drivers/crypto/caam/intern.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/intern.h 2014-03-08 20:33:30.000000000 +0100 @@ -2,7 +2,7 @@ * CAAM/SEC 4.x driver backend * Private/internal definitions between modules * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. * */ @@ -12,6 +12,9 @@ #define JOBR_UNASSIGNED 0 #define JOBR_ASSIGNED 1 +/* Default clock/sample settings for an RNG4 entropy source */ +#define RNG4_ENT_CLOCKS_SAMPLE 1600 + /* Currently comes from Kconfig param as a ^2 (driver-required) */ #define JOBR_DEPTH (1 << CONFIG_CRYPTO_DEV_FSL_CAAM_RINGSIZE) @@ -67,6 +70,8 @@ struct caam_drv_private { struct device *dev; + struct device *smdev; + struct device *secviodev; struct device **jrdev; /* Alloc'ed array per sub-device */ spinlock_t jr_alloc_lock; struct platform_device *pdev; @@ -76,6 +81,9 @@ struct caam_deco **deco; /* DECO/CCB views */ struct caam_assurance *ac; struct caam_queue_if *qi; /* QI control region */ + struct snvs_full __iomem *snvs; /* SNVS HP+LP register space */ + dma_addr_t __iomem *sm_base; /* Secure memory storage base */ + u32 sm_size; /* * Detected geometry block. Filled in from device tree if powerpc, @@ -84,14 +92,22 @@ u8 total_jobrs; /* Total Job Rings in device */ u8 qi_present; /* Nonzero if QI present in device */ int secvio_irq; /* Security violation interrupt number */ + int rng_inst; /* Total instantiated RNGs */ /* which jr allocated to scatterlist crypto */ atomic_t tfm_count ____cacheline_aligned; + int num_jrs_for_algapi; + struct device **algapi_jr; /* list of registered crypto algorithms (mk generic context handle?) */ struct list_head alg_list; /* list of registered hash algorithms (mk generic context handle?) */ struct list_head hash_list; +#ifdef CONFIG_ARM + struct clk *caam_ipg; + struct clk *caam_mem; + struct clk *caam_aclk; +#endif /* * debugfs entries for developer view into driver/device * variables at runtime. diff -Nur linux-3.10.30/drivers/crypto/caam/jr.c linux-3.10.30-cubox-i/drivers/crypto/caam/jr.c --- linux-3.10.30/drivers/crypto/caam/jr.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/jr.c 2014-03-08 20:33:30.000000000 +0100 @@ -2,7 +2,7 @@ * CAAM/SEC 4.x transport/backend driver * JobR backend functionality * - * Copyright 2008-2012 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. */ #include "compat.h" @@ -58,6 +58,9 @@ void (*usercall)(struct device *dev, u32 *desc, u32 status, void *arg); u32 *userdesc, userstatus; void *userarg; + dma_addr_t outbusaddr; + + outbusaddr = rd_reg64(&jrp->rregs->outring_base); while (rd_reg32(&jrp->rregs->outring_used)) { @@ -67,6 +70,9 @@ sw_idx = tail = jrp->tail; hw_idx = jrp->out_ring_read_index; + dma_sync_single_for_cpu(dev, outbusaddr, + sizeof(struct jr_outentry) * JOBR_DEPTH, + DMA_FROM_DEVICE); for (i = 0; CIRC_CNT(head, tail + i, JOBR_DEPTH) >= 1; i++) { sw_idx = (tail + i) & (JOBR_DEPTH - 1); @@ -94,6 +100,8 @@ userdesc = jrp->entinfo[sw_idx].desc_addr_virt; userstatus = jrp->outring[hw_idx].jrstatus; + smp_mb(); + /* set done */ wr_reg32(&jrp->rregs->outring_rmvd, 1); @@ -227,7 +235,7 @@ struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); struct caam_jrentry_info *head_entry; int head, tail, desc_size; - dma_addr_t desc_dma; + dma_addr_t desc_dma, inpbusaddr; desc_size = (*desc & HDR_JD_LENGTH_MASK) * sizeof(u32); desc_dma = dma_map_single(dev, desc, desc_size, DMA_TO_DEVICE); @@ -236,6 +244,13 @@ return -EIO; } + dma_sync_single_for_device(dev, desc_dma, desc_size, DMA_TO_DEVICE); + + inpbusaddr = rd_reg64(&jrp->rregs->inpring_base); + dma_sync_single_for_device(dev, inpbusaddr, + sizeof(dma_addr_t) * JOBR_DEPTH, + DMA_TO_DEVICE); + spin_lock_bh(&jrp->inplock); head = jrp->head; @@ -257,12 +272,18 @@ jrp->inpring[jrp->inp_ring_write_index] = desc_dma; + dma_sync_single_for_device(dev, inpbusaddr, + sizeof(dma_addr_t) * JOBR_DEPTH, + DMA_TO_DEVICE); + smp_wmb(); jrp->inp_ring_write_index = (jrp->inp_ring_write_index + 1) & (JOBR_DEPTH - 1); jrp->head = (head + 1) & (JOBR_DEPTH - 1); + wmb(); + wr_reg32(&jrp->rregs->inpring_jobadd, 1); spin_unlock_bh(&jrp->inplock); @@ -423,7 +444,8 @@ struct platform_device *jr_pdev; struct caam_drv_private *ctrlpriv; struct caam_drv_private_jr *jrpriv; - u32 *jroffset; + const __be32 *jroffset_addr; + u32 jroffset; int error; ctrldev = &pdev->dev; @@ -445,9 +467,21 @@ * need to add in the offset to this JobR. Don't know if I * like this long-term, but it'll run */ - jroffset = (u32 *)of_get_property(np, "reg", NULL); + jroffset_addr = of_get_property(np, "reg", NULL); + + if (jroffset_addr == NULL) { + kfree(jrpriv); + return -EINVAL; + } + + /* + * Fix the endianness of this value read from the device + * tree if running on ARM. + */ + jroffset = be32_to_cpup(jroffset_addr); + jrpriv->rregs = (struct caam_job_ring __iomem *)((void *)ctrlpriv->ctrl - + *jroffset); + + jroffset); /* Build a local dev for each detected queue */ jr_pdev = of_platform_device_create(np, NULL, ctrldev); @@ -471,6 +505,10 @@ /* Identify the interrupt */ jrpriv->irq = of_irq_to_resource(np, 0, NULL); + if (jrpriv->irq <= 0) { + kfree(jrpriv); + return -EINVAL; + } /* Now do the platform independent part */ error = caam_jr_init(jrdev); /* now turn on hardware */ diff -Nur linux-3.10.30/drivers/crypto/caam/jr.h linux-3.10.30-cubox-i/drivers/crypto/caam/jr.h --- linux-3.10.30/drivers/crypto/caam/jr.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/jr.h 2014-03-08 20:33:30.000000000 +0100 @@ -1,7 +1,7 @@ /* * CAAM public-level include definitions for the JobR backend * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. */ #ifndef JR_H @@ -18,4 +18,5 @@ extern int caam_jr_probe(struct platform_device *pdev, struct device_node *np, int ring); extern int caam_jr_shutdown(struct device *dev); +extern struct device *caam_get_jrdev(void); #endif /* JR_H */ diff -Nur linux-3.10.30/drivers/crypto/caam/key_gen.c linux-3.10.30-cubox-i/drivers/crypto/caam/key_gen.c --- linux-3.10.30/drivers/crypto/caam/key_gen.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/key_gen.c 2014-03-08 20:33:30.000000000 +0100 @@ -1,7 +1,7 @@ /* * CAAM/SEC 4.x functions for handling key-generation jobs * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. * */ #include "compat.h" @@ -68,6 +68,7 @@ kfree(desc); return -ENOMEM; } + dma_sync_single_for_device(jrdev, dma_addr_in, keylen, DMA_TO_DEVICE); append_key(desc, dma_addr_in, keylen, CLASS_2 | KEY_DEST_CLASS_REG); /* Sets MDHA up into an HMAC-INIT */ @@ -115,7 +116,8 @@ split_key_pad_len, 1); #endif } - + dma_sync_single_for_cpu(jrdev, dma_addr_out, split_key_pad_len, + DMA_FROM_DEVICE); dma_unmap_single(jrdev, dma_addr_out, split_key_pad_len, DMA_FROM_DEVICE); dma_unmap_single(jrdev, dma_addr_in, keylen, DMA_TO_DEVICE); diff -Nur linux-3.10.30/drivers/crypto/caam/regs.h linux-3.10.30-cubox-i/drivers/crypto/caam/regs.h --- linux-3.10.30/drivers/crypto/caam/regs.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/regs.h 2014-03-08 20:33:30.000000000 +0100 @@ -1,7 +1,7 @@ /* * CAAM hardware register-level view * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. */ #ifndef REGS_H @@ -74,15 +74,21 @@ #endif #else #ifdef __LITTLE_ENDIAN -#define wr_reg32(reg, data) __raw_writel(reg, data) -#define rd_reg32(reg) __raw_readl(reg) +#define wr_reg32(reg, data) writel(data, reg) +#define rd_reg32(reg) readl(reg) #ifdef CONFIG_64BIT -#define wr_reg64(reg, data) __raw_writeq(reg, data) -#define rd_reg64(reg) __raw_readq(reg) +#define wr_reg64(reg, data) writeq(data, reg) +#define rd_reg64(reg) readq(reg) #endif #endif #endif +#ifdef CONFIG_ARM +/* These are common macros for Power, put here for ARMs */ +#define setbits32(_addr, _v) writel((readl(_addr) | (_v)), (_addr)) +#define clrbits32(_addr, _v) writel((readl(_addr) & ~(_v)), (_addr)) +#endif + #ifndef CONFIG_64BIT static inline void wr_reg64(u64 __iomem *reg, u64 data) { @@ -107,6 +113,98 @@ } __packed; /* + * CHA version ID / instantiation bitfields + * Defined for use within cha_id in perfmon + * Note that the same shift/mask selectors can be used to pull out number + * of instantiated blocks within cha_num in perfmon, the locations are + * the same. + */ + +/* Job Ring */ +#define CHA_ID_JR_SHIFT 60 +#define CHA_ID_JR_MASK (0xfull << CHA_ID_JR_SHIFT) + +/* DEscriptor COntroller */ +#define CHA_ID_DECO_SHIFT 56 +#define CHA_ID_DECO_MASK (0xfull << CHA_ID_DECO_SHIFT) +#define CHA_NUM_DECONUM_SHIFT 56 /* legacy definition */ +#define CHA_NUM_DECONUM_MASK (0xfull << CHA_NUM_DECONUM_SHIFT) + +/* ZUC-Authentication */ +#define CHA_ID_ZA_SHIFT 44 +#define CHA_ID_ZA_MASK (0xfull << CHA_ID_ZA_SHIFT) + +/* ZUC-Encryption */ +#define CHA_ID_ZE_SHIFT 40 +#define CHA_ID_ZE_MASK (0xfull << CHA_ID_ZE_SHIFT) + +/* SNOW f9 */ +#define CHA_ID_SNW9_SHIFT 36 +#define CHA_ID_SNW9_MASK (0xfull << CHA_ID_SNW9_SHIFT) + +/* CRC */ +#define CHA_ID_CRC_SHIFT 32 +#define CHA_ID_CRC_MASK (0xfull << CHA_ID_CRC_SHIFT) + +/* Public Key */ +#define CHA_ID_PK_SHIFT 28 +#define CHA_ID_PK_MASK (0xfull << CHA_ID_PK_SHIFT) + +/* Kasumi */ +#define CHA_ID_KAS_SHIFT 24 +#define CHA_ID_KAS_MASK (0xfull << CHA_ID_KAS_SHIFT) + +/* SNOW f8 */ +#define CHA_ID_SNW8_SHIFT 20 +#define CHA_ID_SNW8_MASK (0xfull << CHA_ID_SNW8_SHIFT) + +/* + * Random Generator + * RNG4 = FIPS-verification-compliant, requires init kickstart for use + */ +#define CHA_ID_RNG_SHIFT 16 +#define CHA_ID_RNG_MASK (0xfull << CHA_ID_RNG_SHIFT) +#define CHA_ID_RNG_A (0x1ull << CHA_ID_RNG_SHIFT) +#define CHA_ID_RNG_B (0x2ull << CHA_ID_RNG_SHIFT) +#define CHA_ID_RNG_C (0x3ull << CHA_ID_RNG_SHIFT) +#define CHA_ID_RNG_4 (0x4ull << CHA_ID_RNG_SHIFT) + +/* + * Message Digest + * LP256 = Low Power (MD5/SHA1/SHA224/SHA256 + HMAC) + * LP512 = Low Power (LP256 + SHA384/SHA512) + * HP = High Power (LP512 + SMAC) + */ +#define CHA_ID_MD_SHIFT 12 +#define CHA_ID_MD_MASK (0xfull << CHA_ID_MD_SHIFT) +#define CHA_ID_MD_LP256 (0x0ull << CHA_ID_MD_SHIFT) +#define CHA_ID_MD_LP512 (0x1ull << CHA_ID_MD_SHIFT) +#define CHA_ID_MD_HP (0x2ull << CHA_ID_MD_SHIFT) + +/* ARC4 Streamcipher */ +#define CHA_ID_ARC4_SHIFT 8 +#define CHA_ID_ARC4_MASK (0xfull << CHA_ID_ARC4_SHIFT) +#define CHA_ID_ARC4_LP (0x0ull << CHA_ID_ARC4_SHIFT) +#define CHA_ID_ARC4_HP (0x1ull << CHA_ID_ARC4_SHIFT) + +/* DES Blockcipher Accelerator */ +#define CHA_ID_DES_SHIFT 4 +#define CHA_ID_DES_MASK (0xfull << CHA_ID_DES_SHIFT) + +/* + * AES Blockcipher + Combo Mode Accelerator + * LP = Low Power (includes ECB/CBC/CFB128/OFB/CTR/CCM/CMAC/XCBC-MAC) + * HP = High Power (LP + CBCXCBC/CTRXCBC/XTS/GCM) + * DIFFPWR = ORed in if differential-power-analysis resistance implemented + */ +#define CHA_ID_AES_SHIFT 0 +#define CHA_ID_AES_MASK (0xfull << CHA_ID_AES_SHIFT) +#define CHA_ID_AES_LP (0x3ull << CHA_ID_AES_SHIFT) +#define CHA_ID_AES_HP (0x4ull << CHA_ID_AES_SHIFT) +#define CHA_ID_AES_DIFFPWR (0x1ull << CHA_ID_AES_SHIFT) + + +/* * caam_perfmon - Performance Monitor/Secure Memory Status/ * CAAM Global Status/Component Version IDs * @@ -123,6 +221,10 @@ u8 min_rev; }; +#define SEC_VID_IPID_SHIFT 16 +#define SEC_VID_MAJ_SHIFT 8 +#define SEC_VID_MAJ_MASK 0xFF00 + struct caam_perfmon { /* Performance Monitor Registers f00-f9f */ u64 req_dequeued; /* PC_REQ_DEQ - Dequeued Requests */ @@ -139,15 +241,21 @@ #define CTPR_QI_SHIFT 57 #define CTPR_QI_MASK (0x1ull << CTPR_QI_SHIFT) u64 comp_parms; /* CTPR - Compile Parameters Register */ - u64 rsvd1[2]; + + /* Secure Memory State Visibility */ + u32 rsvd1; + u32 smstatus; /* Secure memory status */ + u32 rsvd2; + u32 smpartown; /* Secure memory partition owner */ /* CAAM Global Status fc0-fdf */ u64 faultaddr; /* FAR - Fault Address */ u32 faultliodn; /* FALR - Fault Address LIODN */ u32 faultdetail; /* FADR - Fault Addr Detail */ - u32 rsvd2; + u32 rsvd3; u32 status; /* CSTA - CAAM Status */ - u64 rsvd3; + u32 smpart; /* Secure Memory Partition Parameters */ + u32 smvid; /* Secure Memory Version ID */ /* Component Instantiation Parameters fe0-fff */ u32 rtic_id; /* RVID - RTIC Version ID */ @@ -157,6 +265,62 @@ u64 caam_id; /* CAAMVID - CAAM Version ID */ }; +#define SMSTATUS_PART_SHIFT 28 +#define SMSTATUS_PART_MASK (0xf << SMSTATUS_PART_SHIFT) +#define SMSTATUS_PAGE_SHIFT 16 +#define SMSTATUS_PAGE_MASK (0x7ff << SMSTATUS_PAGE_SHIFT) +#define SMSTATUS_MID_SHIFT 8 +#define SMSTATUS_MID_MASK (0x3f << SMSTATUS_MID_SHIFT) +#define SMSTATUS_ACCERR_SHIFT 4 +#define SMSTATUS_ACCERR_MASK (0xf << SMSTATUS_ACCERR_SHIFT) +#define SMSTATUS_ACCERR_NONE 0 +#define SMSTATUS_ACCERR_ALLOC 1 /* Page not allocated */ +#define SMSTATUS_ACCESS_ID 2 /* Not granted by ID */ +#define SMSTATUS_ACCESS_WRITE 3 /* Writes not allowed */ +#define SMSTATUS_ACCESS_READ 4 /* Reads not allowed */ +#define SMSTATUS_ACCESS_NONKEY 6 /* Non-key reads not allowed */ +#define SMSTATUS_ACCESS_BLOB 9 /* Blob access not allowed */ +#define SMSTATUS_ACCESS_DESCB 10 /* Descriptor Blob access spans pages */ +#define SMSTATUS_ACCESS_NON_SM 11 /* Outside Secure Memory range */ +#define SMSTATUS_ACCESS_XPAGE 12 /* Access crosses pages */ +#define SMSTATUS_ACCESS_INITPG 13 /* Page still initializing */ +#define SMSTATUS_STATE_SHIFT 0 +#define SMSTATUS_STATE_MASK (0xf << SMSTATUS_STATE_SHIFT) +#define SMSTATUS_STATE_RESET 0 +#define SMSTATUS_STATE_INIT 1 +#define SMSTATUS_STATE_NORMAL 2 +#define SMSTATUS_STATE_FAIL 3 + +/* up to 15 rings, 2 bits shifted by ring number */ +#define SMPARTOWN_RING_SHIFT 2 +#define SMPARTOWN_RING_MASK 3 +#define SMPARTOWN_AVAILABLE 0 +#define SMPARTOWN_NOEXIST 1 +#define SMPARTOWN_UNAVAILABLE 2 +#define SMPARTOWN_OURS 3 + +/* Maximum number of pages possible */ +#define SMPART_MAX_NUMPG_SHIFT 16 +#define SMPART_MAX_NUMPG_MASK (0x3f << SMPART_MAX_NUMPG_SHIFT) + +/* Maximum partition number */ +#define SMPART_MAX_PNUM_SHIFT 12 +#define SMPART_MAX_PNUM_MASK (0xf << SMPART_MAX_PNUM_SHIFT) + +/* Highest possible page number */ +#define SMPART_MAX_PG_SHIFT 0 +#define SMPART_MAX_PG_MASK (0x3f << SMPART_MAX_PG_SHIFT) + +/* Max size of a page */ +#define SMVID_PG_SIZE_SHIFT 16 +#define SMVID_PG_SIZE_MASK (0x7 << SMVID_PG_SIZE_SHIFT) + +/* Major/Minor Version ID */ +#define SMVID_MAJ_VERS_SHIFT 8 +#define SMVID_MAJ_VERS (0xf << SMVID_MAJ_VERS_SHIFT) +#define SMVID_MIN_VERS_SHIFT 0 +#define SMVID_MIN_VERS (0xf << SMVID_MIN_VERS_SHIFT) + /* LIODN programming for DMA configuration */ #define MSTRID_LOCK_LIODN 0x80000000 #define MSTRID_LOCK_MAKETRUSTED 0x00010000 /* only for JR masterid */ @@ -228,7 +392,13 @@ u32 rtfrqmax; /* PRGM=1: freq. count max. limit register */ u32 rtfrqcnt; /* PRGM=0: freq. count register */ }; - u32 rsvd1[56]; + u32 rsvd1[40]; +#define RDSTA_IF 0x00000003 /* state handle instantiated flags 0 and 1 */ +#define RDSTA_SKVN 0x40000000 /* Secure Key Valid Non-Test mode */ +#define RDSTA_SKVT 0x80000000 /* Secure Key Valid Test. non-test mode */ +#define RDSTA_TF 0x00000300 /* State handle instantiated Test-mode */ + u32 rdsta; /* DRNG status register */ + u32 rsvd2[15]; }; /* @@ -359,7 +529,18 @@ u32 rsvd11; u32 jrcommand; /* JRCRx - JobR command */ - u32 rsvd12[932]; + u32 rsvd12[33]; + + /* Secure Memory Configuration - if you have it */ + u32 sm_cmd; /* SMCJRx - Secure memory command */ + u32 rsvd13; + u32 sm_status; /* SMCSJRx - Secure memory status */ + u32 rsvd14; + u32 sm_perm; /* SMAPJRx - Secure memory access perms */ + u32 sm_group2; /* SMAP2JRx - Secure memory access group 2 */ + u32 sm_group1; /* SMAP1JRx - Secure memory access group 1 */ + + u32 rsvd15[891]; /* Performance Monitor f00-fff */ struct caam_perfmon perfmon; @@ -482,6 +663,62 @@ #define JRCR_RESET 0x01 +/* secure memory command */ +#define SMC_PAGE_SHIFT 16 +#define SMC_PAGE_MASK (0xffff << SMC_PAGE_SHIFT) +#define SMC_PART_SHIFT 8 +#define SMC_PART_MASK (0x0f << SMC_PART_SHIFT) +#define SMC_CMD_SHIFT 0 +#define SMC_CMD_MASK (0x0f << SMC_CMD_SHIFT) + +#define SMC_CMD_ALLOC_PAGE 0x01 /* allocate page to this partition */ +#define SMC_CMD_DEALLOC_PAGE 0x02 /* deallocate page from partition */ +#define SMC_CMD_DEALLOC_PART 0x03 /* deallocate partition */ +#define SMC_CMD_PAGE_INQUIRY 0x05 /* find partition associate with page */ + +/* secure memory (command) status */ +#define SMCS_PAGE_SHIFT 16 +#define SMCS_PAGE_MASK (0x0fff << SMCS_PAGE_SHIFT) +#define SMCS_CMDERR_SHIFT 14 +#define SMCS_CMDERR_MASK (3 << SMCS_CMDERR_SHIFT) +#define SMCS_ALCERR_SHIFT 12 +#define SMCS_ALCERR_MASK (3 << SMCS_ALCERR_SHIFT) +#define SMCS_PGOWN_SHIFT 6 +#define SMCS_PGWON_MASK (3 << SMCS_PGOWN_SHIFT) +#define SMCS_PART_SHIFT 0 +#define SMCS_PART_MASK (0xf << SMCS_PART_SHIFT) + +#define SMCS_CMDERR_NONE 0 +#define SMCS_CMDERR_INCOMP 1 /* Command not yet complete */ +#define SMCS_CMDERR_SECFAIL 2 /* Security failure occurred */ +#define SMCS_CMDERR_OVERFLOW 3 /* Command overflow */ + +#define SMCS_ALCERR_NONE 0 +#define SMCS_ALCERR_PSPERR 1 /* Partion marked PSP (dealloc only) */ +#define SMCS_ALCERR_PAGEAVAIL 2 /* Page not available */ +#define SMCS_ALCERR_PARTOWN 3 /* Partition ownership error */ + +#define SMCS_PGOWN_AVAIL 0 /* Page is available */ +#define SMCS_PGOWN_NOEXIST 1 /* Page initializing or nonexistent */ +#define SMCS_PGOWN_NOOWN 2 /* Page owned by another processor */ +#define SMCS_PGOWN_OWNED 3 /* Page belongs to this processor */ + +/* secure memory access permissions */ +#define SMCS_PERM_KEYMOD_SHIFT 16 +#define SMCA_PERM_KEYMOD_MASK (0xff << SMCS_PERM_KEYMOD_SHIFT) +#define SMCA_PERM_CSP_ZERO 0x8000 /* Zero when deallocated or released */ +#define SMCA_PERM_PSP_LOCK 0x4000 /* Part./pages can't be deallocated */ +#define SMCA_PERM_PERM_LOCK 0x2000 /* Lock permissions */ +#define SMCA_PERM_GRP_LOCK 0x1000 /* Lock access groups */ +#define SMCA_PERM_RINGID_SHIFT 10 +#define SMCA_PERM_RINGID_MASK (3 << SMCA_PERM_RINGID_SHIFT) +#define SMCA_PERM_G2_BLOB 0x0080 /* Group 2 blob import/export */ +#define SMCA_PERM_G2_WRITE 0x0020 /* Group 2 write */ +#define SMCA_PERM_G2_READ 0x0010 /* Group 2 read */ +#define SMCA_PERM_G1_BLOB 0x0008 /* Group 1... */ +#define SMCA_PERM_G1_WRITE 0x0002 +#define SMCA_PERM_G1_READ 0x0001 + /* * caam_assurance - Assurance Controller View * base + 0x6000 padded out to 0x1000 diff -Nur linux-3.10.30/drivers/crypto/caam/secvio.c linux-3.10.30-cubox-i/drivers/crypto/caam/secvio.c --- linux-3.10.30/drivers/crypto/caam/secvio.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/secvio.c 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,333 @@ + +/* + * CAAM/SEC 4.x Security Violation Handler + * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved + */ + +#include "compat.h" +#include "intern.h" +#include "secvio.h" +#include "regs.h" + +/* + * These names are associated with each violation handler. + * The source names were taken from MX6, and are based on recommendations + * for most common SoCs. + */ +static const u8 *violation_src_name[] = { + "CAAM Security Violation", + "JTAG Alarm", + "Watchdog", + "(reserved)", + "External Boot", + "Tamper Detect", +}; + +/* Top-level security violation interrupt */ +static irqreturn_t caam_secvio_interrupt(int irq, void *snvsdev) +{ + struct device *dev = snvsdev; + struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev); + u32 irqstate; + + /* Check the HP secvio status register */ + irqstate = rd_reg32(&svpriv->svregs->hp.secvio_status) | + HP_SECVIOST_SECVIOMASK; + + if (!irqstate) + return IRQ_NONE; + + /* Mask out one or more causes for deferred service */ + clrbits32(&svpriv->svregs->hp.secvio_int_ctl, irqstate); + + /* Now ACK causes */ + setbits32(&svpriv->svregs->hp.secvio_status, irqstate); + + /* And run deferred service */ + preempt_disable(); + tasklet_schedule(&svpriv->irqtask[smp_processor_id()]); + preempt_enable(); + + return IRQ_HANDLED; +} + +/* Deferred service handler. Tasklet arg is simply the SNVS dev */ +static void caam_secvio_dispatch(unsigned long indev) +{ + struct device *dev = (struct device *)indev; + struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev); + unsigned long flags, cause; + int i; + + + /* + * Capture the interrupt cause, using masked interrupts as + * identification. This only works if all are enabled; if + * this changes in the future, a "cause queue" will have to + * be built + */ + cause = rd_reg32(&svpriv->svregs->hp.secvio_int_ctl) & + (HP_SECVIO_INTEN_SRC5 | HP_SECVIO_INTEN_SRC4 | + HP_SECVIO_INTEN_SRC3 | HP_SECVIO_INTEN_SRC2 | + HP_SECVIO_INTEN_SRC1 | HP_SECVIO_INTEN_SRC0); + + /* Look through causes, call each handler if exists */ + for (i = 0; i < MAX_SECVIO_SOURCES; i++) + if (cause & (1 << i)) { + spin_lock_irqsave(&svpriv->svlock, flags); + svpriv->intsrc[i].handler(dev, i, + svpriv->intsrc[i].ext); + spin_unlock_irqrestore(&svpriv->svlock, flags); + }; + + /* Re-enable now-serviced interrupts */ + setbits32(&svpriv->svregs->hp.secvio_int_ctl, cause); +} + +/* + * Default cause handler, used in lieu of an application-defined handler. + * All it does at this time is print a console message. It could force a halt. + */ +static void caam_secvio_default(struct device *dev, u32 cause, void *ext) +{ + struct caam_drv_private_secvio *svpriv = dev_get_drvdata(dev); + + dev_err(dev, "Unhandled Security Violation Interrupt %d = %s\n", + cause, svpriv->intsrc[cause].intname); +} + +/* + * Install an application-defined handler for a specified cause + * Arguments: + * - dev points to SNVS-owning device + * - cause interrupt source cause + * - handler application-defined handler, gets called with dev + * source cause, and locally-defined handler argument + * - cause_description points to a string to override the default cause + * name, this can be used as an alternate for error + * messages and such. If left NULL, the default + * description string is used. + * - ext pointer to any extra data needed by the handler. + */ +int caam_secvio_install_handler(struct device *dev, enum secvio_cause cause, + void (*handler)(struct device *dev, u32 cause, + void *ext), + u8 *cause_description, void *ext) +{ + unsigned long flags; + struct caam_drv_private_secvio *svpriv; + + svpriv = dev_get_drvdata(dev); + + if ((handler == NULL) || (cause > SECVIO_CAUSE_SOURCE_5)) + return -EINVAL; + + spin_lock_irqsave(&svpriv->svlock, flags); + svpriv->intsrc[cause].handler = handler; + if (cause_description != NULL) + svpriv->intsrc[cause].intname = cause_description; + if (ext != NULL) + svpriv->intsrc[cause].ext = ext; + spin_unlock_irqrestore(&svpriv->svlock, flags); + + return 0; +} +EXPORT_SYMBOL(caam_secvio_install_handler); + +/* + * Remove an application-defined handler for a specified cause (and, by + * implication, restore the "default". + * Arguments: + * - dev points to SNVS-owning device + * - cause interrupt source cause + */ +int caam_secvio_remove_handler(struct device *dev, enum secvio_cause cause) +{ + unsigned long flags; + struct caam_drv_private_secvio *svpriv; + + svpriv = dev_get_drvdata(dev); + + if (cause > SECVIO_CAUSE_SOURCE_5) + return -EINVAL; + + spin_lock_irqsave(&svpriv->svlock, flags); + svpriv->intsrc[cause].intname = violation_src_name[cause]; + svpriv->intsrc[cause].handler = caam_secvio_default; + svpriv->intsrc[cause].ext = NULL; + spin_unlock_irqrestore(&svpriv->svlock, flags); + return 0; +} +EXPORT_SYMBOL(caam_secvio_remove_handler); + +int caam_secvio_startup(struct platform_device *pdev) +{ + struct device *ctrldev, *svdev; + struct caam_drv_private *ctrlpriv; + struct caam_drv_private_secvio *svpriv; + struct platform_device *svpdev; + struct device_node *np; + const void *prop; + int i, error, secvio_inten_src; + + ctrldev = &pdev->dev; + ctrlpriv = dev_get_drvdata(ctrldev); + /* + * Set up the private block for secure memory + * Only one instance is possible + */ + svpriv = kzalloc(sizeof(struct caam_drv_private_secvio), GFP_KERNEL); + if (svpriv == NULL) { + dev_err(ctrldev, "can't alloc private mem for secvio\n"); + return -ENOMEM; + } + svpriv->parentdev = ctrldev; + + /* Create the security violation dev */ +#ifdef CONFIG_OF + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-secvio"); + if (!np) + return -ENODEV; + + ctrlpriv->secvio_irq = of_irq_to_resource(np, 0, NULL); + + prop = of_get_property(np, "secvio_src", NULL); + if (prop) + secvio_inten_src = of_read_ulong(prop, 1); + else + secvio_inten_src = HP_SECVIO_INTEN_ALL; + + svpdev = of_platform_device_create(np, NULL, ctrldev); + if (!svpdev) + return -ENODEV; + +#else + svpdev = platform_device_register_data(ctrldev, "caam_secvio", 0, + svpriv, + sizeof(struct caam_drv_private_secvio)); + + secvio_inten_src = HP_SECVIO_INTEN_ALL; +#endif + if (svpdev == NULL) { + kfree(svpriv); + return -EINVAL; + } + svdev = &svpdev->dev; + dev_set_drvdata(svdev, svpriv); + ctrlpriv->secviodev = svdev; + svpriv->svregs = ctrlpriv->snvs; + + /* + * Now we have all the dev data set up. Init interrupt + * source descriptions + */ + for (i = 0; i < MAX_SECVIO_SOURCES; i++) { + svpriv->intsrc[i].intname = violation_src_name[i]; + svpriv->intsrc[i].handler = caam_secvio_default; + } + + /* Connect main handler */ + for_each_possible_cpu(i) + tasklet_init(&svpriv->irqtask[i], caam_secvio_dispatch, + (unsigned long)svdev); + + error = request_irq(ctrlpriv->secvio_irq, caam_secvio_interrupt, + IRQF_SHARED, "caam_secvio", svdev); + if (error) { + dev_err(svdev, "can't connect secvio interrupt\n"); + irq_dispose_mapping(ctrlpriv->secvio_irq); + ctrlpriv->secvio_irq = 0; + return -EINVAL; + } + + /* Enable all sources */ + wr_reg32(&svpriv->svregs->hp.secvio_int_ctl, secvio_inten_src); + + dev_info(svdev, "security violation service handlers armed\n"); + + return 0; +} + +void caam_secvio_shutdown(struct platform_device *pdev) +{ + struct device *ctrldev, *svdev; + struct caam_drv_private *priv; + struct caam_drv_private_secvio *svpriv; + int i; + + ctrldev = &pdev->dev; + priv = dev_get_drvdata(ctrldev); + svdev = priv->secviodev; + svpriv = dev_get_drvdata(svdev); + + /* Shut off all sources */ + + wr_reg32(&svpriv->svregs->hp.secvio_int_ctl, 0); + + /* Remove tasklets and release interrupt */ + for_each_possible_cpu(i) + tasklet_kill(&svpriv->irqtask[i]); + + free_irq(priv->secvio_irq, svdev); + + kfree(svpriv); +} + + +#ifdef CONFIG_OF +static void __exit caam_secvio_exit(void) +{ + struct device_node *dev_node; + struct platform_device *pdev; + + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); + if (!dev_node) { + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); + if (!dev_node) + return; + } + + pdev = of_find_device_by_node(dev_node); + if (!pdev) + return; + + of_node_get(dev_node); + + caam_secvio_shutdown(pdev); + +} + +static int __init caam_secvio_init(void) +{ + struct device_node *dev_node; + struct platform_device *pdev; + + /* + * Do of_find_compatible_node() then of_find_device_by_node() + * once a functional device tree is available + */ + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); + if (!dev_node) { + dev_node = of_find_compatible_node(NULL, NULL, + "arm,imx6-caam-secvio"); + if (!dev_node) + return -ENODEV; + } + + pdev = of_find_device_by_node(dev_node); + if (!pdev) + return -ENODEV; + + of_node_put(dev_node); + + return caam_secvio_startup(pdev); +} + +module_init(caam_secvio_init); +module_exit(caam_secvio_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("FSL CAAM/SNVS Security Violation Handler"); +MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD"); +#endif diff -Nur linux-3.10.30/drivers/crypto/caam/secvio.h linux-3.10.30-cubox-i/drivers/crypto/caam/secvio.h --- linux-3.10.30/drivers/crypto/caam/secvio.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/secvio.h 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,64 @@ + +/* + * CAAM Security Violation Handler + * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved + */ + +#ifndef SECVIO_H +#define SECVIO_H + +#include "snvsregs.h" + + +/* + * Defines the published interfaces to install/remove application-specified + * handlers for catching violations + */ + +#define MAX_SECVIO_SOURCES 6 + +/* these are the untranslated causes */ +enum secvio_cause { + SECVIO_CAUSE_SOURCE_0, + SECVIO_CAUSE_SOURCE_1, + SECVIO_CAUSE_SOURCE_2, + SECVIO_CAUSE_SOURCE_3, + SECVIO_CAUSE_SOURCE_4, + SECVIO_CAUSE_SOURCE_5 +}; + +/* These are common "recommended" cause definitions for most devices */ +#define SECVIO_CAUSE_CAAM_VIOLATION SECVIO_CAUSE_SOURCE_0 +#define SECVIO_CAUSE JTAG_ALARM SECVIO_CAUSE_SOURCE_1 +#define SECVIO_CAUSE_WATCHDOG SECVIO_CAUSE_SOURCE_2 +#define SECVIO_CAUSE_EXTERNAL_BOOT SECVIO_CAUSE_SOURCE_4 +#define SECVIO_CAUSE_TAMPER_DETECT SECVIO_CAUSE_SOURCE_5 + +int caam_secvio_install_handler(struct device *dev, enum secvio_cause cause, + void (*handler)(struct device *dev, u32 cause, + void *ext), + u8 *cause_description, void *ext); +int caam_secvio_remove_handler(struct device *dev, enum secvio_cause cause); + +/* + * Private data definitions for the secvio "driver" + */ + +struct secvio_int_src { + const u8 *intname; /* Points to a descriptive name for source */ + void *ext; /* Extended data to pass to the handler */ + void (*handler)(struct device *dev, u32 cause, void *ext); +}; + +struct caam_drv_private_secvio { + struct device *parentdev; /* points back to the controller */ + spinlock_t svlock ____cacheline_aligned; + struct tasklet_struct irqtask[NR_CPUS]; + struct snvs_full __iomem *svregs; /* both HP and LP domains */ + + /* Registered handlers for each violation */ + struct secvio_int_src intsrc[MAX_SECVIO_SOURCES]; + +}; + +#endif /* SECVIO_H */ diff -Nur linux-3.10.30/drivers/crypto/caam/sg_sw_sec4.h linux-3.10.30-cubox-i/drivers/crypto/caam/sg_sw_sec4.h --- linux-3.10.30/drivers/crypto/caam/sg_sw_sec4.h 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/sg_sw_sec4.h 2014-03-08 20:33:30.000000000 +0100 @@ -1,7 +1,7 @@ /* * CAAM/SEC 4.x functions for using scatterlists in caam driver * - * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. * */ @@ -91,13 +91,22 @@ { if (unlikely(chained)) { int i; + struct scatterlist *tsg = sg; + + /* We use a local copy of the sg pointer to avoid moving the + * head of the list pointed to by sg as we wall the list. + */ for (i = 0; i < nents; i++) { - dma_map_sg(dev, sg, 1, dir); - sg = scatterwalk_sg_next(sg); + dma_map_sg(dev, tsg, 1, dir); + tsg = scatterwalk_sg_next(tsg); } } else { dma_map_sg(dev, sg, nents, dir); } + + if ((dir == DMA_TO_DEVICE) || (dir == DMA_BIDIRECTIONAL)) + dma_sync_sg_for_device(dev, sg, nents, dir); + return nents; } @@ -105,6 +114,9 @@ unsigned int nents, enum dma_data_direction dir, bool chained) { + if ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)) + dma_sync_sg_for_cpu(dev, sg, nents, dir); + if (unlikely(chained)) { int i; for (i = 0; i < nents; i++) { diff -Nur linux-3.10.30/drivers/crypto/caam/sm.h linux-3.10.30-cubox-i/drivers/crypto/caam/sm.h --- linux-3.10.30/drivers/crypto/caam/sm.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/sm.h 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,88 @@ + +/* + * CAAM Secure Memory/Keywrap API Definitions + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. + */ + +#ifndef SM_H +#define SM_H + + +/* Storage access permissions */ +#define SM_PERM_READ 0x01 +#define SM_PERM_WRITE 0x02 +#define SM_PERM_BLOB 0x03 + + +/* Keystore maintenance functions */ +void sm_init_keystore(struct device *dev); +u32 sm_detect_keystore_units(struct device *dev); +int sm_establish_keystore(struct device *dev, u32 unit); +void sm_release_keystore(struct device *dev, u32 unit); +void caam_sm_shutdown(struct platform_device *pdev); +int caam_sm_example_init(struct platform_device *pdev); + +/* Keystore accessor functions */ +extern int sm_keystore_slot_alloc(struct device *dev, u32 unit, u32 size, + u32 *slot); +extern int sm_keystore_slot_dealloc(struct device *dev, u32 unit, u32 slot); +extern int sm_keystore_slot_load(struct device *dev, u32 unit, u32 slot, + const u8 *key_data, u32 key_length); +extern int sm_keystore_slot_read(struct device *dev, u32 unit, u32 slot, + u32 key_length, u8 *key_data); +extern int sm_keystore_slot_encapsulate(struct device *dev, u32 unit, + u32 inslot, u32 outslot, u16 secretlen, + u8 *keymod, u16 keymodlen); +extern int sm_keystore_slot_decapsulate(struct device *dev, u32 unit, + u32 inslot, u32 outslot, u16 secretlen, + u8 *keymod, u16 keymodlen); + +/* Data structure to hold per-slot information */ +struct keystore_data_slot_info { + u8 allocated; /* Track slot assignments */ + u32 key_length; /* Size of the key */ +}; + +/* Data structure to hold keystore information */ +struct keystore_data { + void *base_address; /* Base of the Secure Partition */ + u32 slot_count; /* Number of slots in the keystore */ + struct keystore_data_slot_info *slot; /* Per-slot information */ +}; + +/* store the detected attributes of a secure memory page */ +struct sm_page_descriptor { + u16 phys_pagenum; /* may be discontiguous */ + u16 own_part; /* Owning partition */ + void *pg_base; /* Calculated virtual address */ + struct keystore_data *ksdata; +}; + +struct caam_drv_private_sm { + struct device *parentdev; /* this ends up as the controller */ + struct device *smringdev; /* ring that owns this instance */ + spinlock_t kslock ____cacheline_aligned; + + /* Default parameters for geometry */ + u32 max_pages; /* maximum pages this instance can support */ + u32 top_partition; /* highest partition number in this instance */ + u32 top_page; /* highest page number in this instance */ + u32 page_size; /* page size */ + u32 slot_size; /* selected size of each storage block */ + + /* Partition/Page Allocation Map */ + u32 localpages; /* Number of pages we can access */ + struct sm_page_descriptor *pagedesc; /* Allocated per-page */ + + /* Installed handlers for keystore access */ + int (*data_init)(struct device *dev, u32 unit); + void (*data_cleanup)(struct device *dev, u32 unit); + int (*slot_alloc)(struct device *dev, u32 unit, u32 size, u32 *slot); + int (*slot_dealloc)(struct device *dev, u32 unit, u32 slot); + void *(*slot_get_address)(struct device *dev, u32 unit, u32 handle); + u32 (*slot_get_base)(struct device *dev, u32 unit, u32 handle); + u32 (*slot_get_offset)(struct device *dev, u32 unit, u32 handle); + u32 (*slot_get_slot_size)(struct device *dev, u32 unit, u32 handle); +}; + +#endif /* SM_H */ diff -Nur linux-3.10.30/drivers/crypto/caam/sm_store.c linux-3.10.30-cubox-i/drivers/crypto/caam/sm_store.c --- linux-3.10.30/drivers/crypto/caam/sm_store.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/sm_store.c 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,896 @@ + +/* + * CAAM Secure Memory Storage Interface + * Copyright (C) 2008-2013 Freescale Semiconductor, Inc. + * + * Loosely based on the SHW Keystore API for SCC/SCC2 + * Experimental implementation and NOT intended for upstream use. Expect + * this interface to be amended significantly in the future once it becomes + * integrated into live applications. + * + * Known issues: + * + * - Executes one instance of an secure memory "driver". This is tied to the + * fact that job rings can't run as standalone instances in the present + * configuration. + * + * - It does not expose a userspace interface. The value of a userspace + * interface for access to secrets is a point for further architectural + * discussion. + * + * - Partition/permission management is not part of this interface. It + * depends on some level of "knowledge" agreed upon between bootloader, + * provisioning applications, and OS-hosted software (which uses this + * driver). + * + * - No means of identifying the location or purpose of secrets managed by + * this interface exists; "slot location" and format of a given secret + * needs to be agreed upon between bootloader, provisioner, and OS-hosted + * application. + */ + +#include "compat.h" +#include "regs.h" +#include "jr.h" +#include "desc.h" +#include "intern.h" +#include "error.h" +#include "sm.h" + +#ifdef SM_DEBUG_CONT +void sm_show_page(struct device *dev, struct sm_page_descriptor *pgdesc) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + u32 i, *smdata; + + dev_info(dev, "physical page %d content at 0x%08x\n", + pgdesc->phys_pagenum, pgdesc->pg_base); + smdata = pgdesc->pg_base; + for (i = 0; i < (smpriv->page_size / sizeof(u32)); i += 4) + dev_info(dev, "[0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n", + (u32)&smdata[i], smdata[i], smdata[i+1], smdata[i+2], + smdata[i+3]); +} +#endif + +/* + * Construct a secure memory blob encapsulation job descriptor + * + * - desc pointer to hold new (to be allocated) pointer to the generated + * descriptor for later use. Calling thread can kfree the + * descriptor after execution. + * - keymod Physical pointer to key modifier (contiguous piece). + * - keymodsz Size of key modifier in bytes (should normally be 8). + * - secretbuf Physical pointer (within an accessible secure memory page) + * of the secret to be encapsulated. + * - outbuf Physical pointer (within an accessible secure memory page) + * of the encapsulated output. This will be larger than the + * input secret because of the added encapsulation data. + * - secretsz Size of input secret, in bytes. + * - auth If nonzero, use AES-CCM for encapsulation, else use ECB + * + * Note: this uses 32-bit pointers at present + */ +#define INITIAL_DESCSZ 16 /* size of tmp buffer for descriptor const. */ +static int blob_encap_desc(u32 **desc, dma_addr_t keymod, u16 keymodsz, + dma_addr_t secretbuf, dma_addr_t outbuf, + u16 secretsz, bool auth) +{ + u32 *tdesc, tmpdesc[INITIAL_DESCSZ]; + u16 dsize, idx; + + memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32)); + idx = 1; + + /* Load key modifier */ + tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY | + ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK) | + (keymodsz & LDST_LEN_MASK); + + tmpdesc[idx++] = (u32)keymod; + + /* Encapsulate to secure memory */ + tmpdesc[idx++] = CMD_SEQ_IN_PTR | secretsz; + tmpdesc[idx++] = (u32)secretbuf; + + /* Add space for BKEK and MAC tag */ + tmpdesc[idx++] = CMD_SEQ_IN_PTR | (secretsz + (32 + 16)); + + tmpdesc[idx++] = (u32)outbuf; + tmpdesc[idx] = CMD_OPERATION | OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB | + OP_PCL_BLOB_PTXT_SECMEM; + if (auth) + tmpdesc[idx] |= OP_PCL_BLOB_EKT; + + idx++; + tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK); + dsize = idx * sizeof(u32); + + tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA); + if (tdesc == NULL) + return 0; + + memcpy(tdesc, tmpdesc, dsize); + *desc = tdesc; + return dsize; +} + +/* + * Construct a secure memory blob decapsulation job descriptor + * + * - desc pointer to hold new (to be allocated) pointer to the generated + * descriptor for later use. Calling thread can kfree the + * descriptor after execution. + * - keymod Physical pointer to key modifier (contiguous piece). + * - keymodsz Size of key modifier in bytes (should normally be 16). + * - blobbuf Physical pointer (within an accessible secure memory page) + * of the blob to be decapsulated. + * - outbuf Physical pointer (within an accessible secure memory page) + * of the decapsulated output. + * - secretsz Size of input blob, in bytes. + * - auth If nonzero, assume AES-CCM for decapsulation, else use ECB + * + * Note: this uses 32-bit pointers at present + */ +static int blob_decap_desc(u32 **desc, dma_addr_t keymod, u16 keymodsz, + dma_addr_t blobbuf, dma_addr_t outbuf, + u16 blobsz, bool auth) +{ + u32 *tdesc, tmpdesc[INITIAL_DESCSZ]; + u16 dsize, idx; + + memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32)); + idx = 1; + + /* Load key modifier */ + tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY | + ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK) | + (keymodsz & LDST_LEN_MASK); + + tmpdesc[idx++] = (u32)keymod; + + /* Compensate BKEK + MAC tag */ + tmpdesc[idx++] = CMD_SEQ_IN_PTR | (blobsz + 32 + 16); + + tmpdesc[idx++] = (u32)blobbuf; + tmpdesc[idx++] = CMD_SEQ_OUT_PTR | blobsz; + tmpdesc[idx++] = (u32)outbuf; + + /* Decapsulate from secure memory partition to black blob */ + tmpdesc[idx] = CMD_OPERATION | OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB | + OP_PCL_BLOB_PTXT_SECMEM | OP_PCL_BLOB_BLACK; + if (auth) + tmpdesc[idx] |= OP_PCL_BLOB_EKT; + + idx++; + tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK); + dsize = idx * sizeof(u32); + + tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA); + if (tdesc == NULL) + return 0; + + memcpy(tdesc, tmpdesc, dsize); + *desc = tdesc; + return dsize; +} + +/* + * Pseudo-synchronous ring access functions for carrying out key + * encapsulation and decapsulation + */ + +struct sm_key_job_result { + int error; + struct completion completion; +}; + +void sm_key_job_done(struct device *dev, u32 *desc, u32 err, void *context) +{ + struct sm_key_job_result *res = context; + + res->error = err; /* save off the error for postprocessing */ + complete(&res->completion); /* mark us complete */ +} + +static int sm_key_job(struct device *ksdev, u32 *jobdesc) +{ + struct sm_key_job_result testres; + struct caam_drv_private_sm *kspriv; + int rtn = 0; + + kspriv = dev_get_drvdata(ksdev); + + init_completion(&testres.completion); + + rtn = caam_jr_enqueue(kspriv->smringdev, jobdesc, sm_key_job_done, + &testres); + if (!rtn) { + wait_for_completion_interruptible(&testres.completion); + rtn = testres.error; + } + return rtn; +} + +/* + * Following section establishes the default methods for keystore access + * They are NOT intended for use external to this module + * + * In the present version, these are the only means for the higher-level + * interface to deal with the mechanics of accessing the phyiscal keystore + */ + + +int slot_alloc(struct device *dev, u32 unit, u32 size, u32 *slot) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; + u32 i; +#ifdef SM_DEBUG + dev_info(dev, "slot_alloc(): requesting slot for %d bytes\n", size); +#endif + + if (size > smpriv->slot_size) + return -EKEYREJECTED; + + for (i = 0; i < ksdata->slot_count; i++) { + if (ksdata->slot[i].allocated == 0) { + ksdata->slot[i].allocated = 1; + (*slot) = i; +#ifdef SM_DEBUG + dev_info(dev, "slot_alloc(): new slot %d allocated\n", + *slot); +#endif + return 0; + } + } + + return -ENOSPC; +} +EXPORT_SYMBOL(slot_alloc); + +int slot_dealloc(struct device *dev, u32 unit, u32 slot) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; + u8 __iomem *slotdata; + +#ifdef SM_DEBUG + dev_info(dev, "slot_dealloc(): releasing slot %d\n", slot); +#endif + if (slot >= ksdata->slot_count) + return -EINVAL; + slotdata = ksdata->base_address + slot * smpriv->slot_size; + + if (ksdata->slot[slot].allocated == 1) { + /* Forcibly overwrite the data from the keystore */ + memset(ksdata->base_address + slot * smpriv->slot_size, 0, + smpriv->slot_size); + + ksdata->slot[slot].allocated = 0; +#ifdef SM_DEBUG + dev_info(dev, "slot_dealloc(): slot %d released\n", slot); +#endif + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL(slot_dealloc); + +void *slot_get_address(struct device *dev, u32 unit, u32 slot) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; + + if (slot >= ksdata->slot_count) + return NULL; + +#ifdef SM_DEBUG + dev_info(dev, "slot_get_address(): slot %d is 0x%08x\n", slot, + (u32)ksdata->base_address + slot * smpriv->slot_size); +#endif + + return ksdata->base_address + slot * smpriv->slot_size; +} + +u32 slot_get_base(struct device *dev, u32 unit, u32 slot) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; + + /* + * There could potentially be more than one secure partition object + * associated with this keystore. For now, there is just one. + */ + + (void)slot; + +#ifdef SM_DEBUG + dev_info(dev, "slot_get_base(): slot %d = 0x%08x\n", + slot, (u32)ksdata->base_address); +#endif + + return (u32)(ksdata->base_address); +} + +u32 slot_get_offset(struct device *dev, u32 unit, u32 slot) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata; + + if (slot >= ksdata->slot_count) + return -EINVAL; + +#ifdef SM_DEBUG + dev_info(dev, "slot_get_offset(): slot %d = %d\n", slot, + slot * smpriv->slot_size); +#endif + + return slot * smpriv->slot_size; +} + +u32 slot_get_slot_size(struct device *dev, u32 unit, u32 slot) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + + +#ifdef SM_DEBUG + dev_info(dev, "slot_get_slot_size(): slot %d = %d\n", slot, + smpriv->slot_size); +#endif + /* All slots are the same size in the default implementation */ + return smpriv->slot_size; +} + + + +int kso_init_data(struct device *dev, u32 unit) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + int retval = -EINVAL; + struct keystore_data *keystore_data = NULL; + u32 slot_count; + u32 keystore_data_size; + + /* + * Calculate the required size of the keystore data structure, based + * on the number of keys that can fit in the partition. + */ + slot_count = smpriv->page_size / smpriv->slot_size; +#ifdef SM_DEBUG + dev_info(dev, "kso_init_data: %d slots initializing\n", slot_count); +#endif + + keystore_data_size = sizeof(struct keystore_data) + + slot_count * + sizeof(struct keystore_data_slot_info); + + keystore_data = kzalloc(keystore_data_size, GFP_KERNEL); + + if (keystore_data == NULL) { + retval = -ENOSPC; + goto out; + } + +#ifdef SM_DEBUG + dev_info(dev, "kso_init_data: keystore data size = %d\n", + keystore_data_size); +#endif + + /* + * Place the slot information structure directly after the keystore data + * structure. + */ + keystore_data->slot = (struct keystore_data_slot_info *) + (keystore_data + 1); + keystore_data->slot_count = slot_count; + + smpriv->pagedesc[unit].ksdata = keystore_data; + smpriv->pagedesc[unit].ksdata->base_address = + smpriv->pagedesc[unit].pg_base; + + retval = 0; + +out: + if (retval != 0) + if (keystore_data != NULL) + kfree(keystore_data); + + + return retval; +} + +void kso_cleanup_data(struct device *dev, u32 unit) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + struct keystore_data *keystore_data = NULL; + + if (smpriv->pagedesc[unit].ksdata != NULL) + keystore_data = smpriv->pagedesc[unit].ksdata; + + /* Release the allocated keystore management data */ + kfree(smpriv->pagedesc[unit].ksdata); + + return; +} + + + +/* + * Keystore management section + */ + +void sm_init_keystore(struct device *dev) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + + smpriv->data_init = kso_init_data; + smpriv->data_cleanup = kso_cleanup_data; + smpriv->slot_alloc = slot_alloc; + smpriv->slot_dealloc = slot_dealloc; + smpriv->slot_get_address = slot_get_address; + smpriv->slot_get_base = slot_get_base; + smpriv->slot_get_offset = slot_get_offset; + smpriv->slot_get_slot_size = slot_get_slot_size; +#ifdef SM_DEBUG + dev_info(dev, "sm_init_keystore(): handlers installed\n"); +#endif +} +EXPORT_SYMBOL(sm_init_keystore); + +/* Return available pages/units */ +u32 sm_detect_keystore_units(struct device *dev) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + + return smpriv->localpages; +} +EXPORT_SYMBOL(sm_detect_keystore_units); + +/* + * Do any keystore specific initializations + */ +int sm_establish_keystore(struct device *dev, u32 unit) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + +#ifdef SM_DEBUG + dev_info(dev, "sm_establish_keystore(): unit %d initializing\n", unit); +#endif + + if (smpriv->data_init == NULL) + return -EINVAL; + + /* Call the data_init function for any user setup */ + return smpriv->data_init(dev, unit); +} +EXPORT_SYMBOL(sm_establish_keystore); + +void sm_release_keystore(struct device *dev, u32 unit) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + +#ifdef SM_DEBUG + dev_info(dev, "sm_establish_keystore(): unit %d releasing\n", unit); +#endif + if ((smpriv != NULL) && (smpriv->data_cleanup != NULL)) + smpriv->data_cleanup(dev, unit); + + return; +} +EXPORT_SYMBOL(sm_release_keystore); + +/* + * Subsequent interfacce (sm_keystore_*) forms the accessor interfacce to + * the keystore + */ +int sm_keystore_slot_alloc(struct device *dev, u32 unit, u32 size, u32 *slot) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + int retval = -EINVAL; + + spin_lock(&smpriv->kslock); + + if ((smpriv->slot_alloc == NULL) || + (smpriv->pagedesc[unit].ksdata == NULL)) + goto out; + + retval = smpriv->slot_alloc(dev, unit, size, slot); + +out: + spin_unlock(&smpriv->kslock); + return retval; +} +EXPORT_SYMBOL(sm_keystore_slot_alloc); + +int sm_keystore_slot_dealloc(struct device *dev, u32 unit, u32 slot) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + int retval = -EINVAL; + + spin_lock(&smpriv->kslock); + + if ((smpriv->slot_alloc == NULL) || + (smpriv->pagedesc[unit].ksdata == NULL)) + goto out; + + retval = smpriv->slot_dealloc(dev, unit, slot); +out: + spin_unlock(&smpriv->kslock); + return retval; +} +EXPORT_SYMBOL(sm_keystore_slot_dealloc); + +int sm_keystore_slot_load(struct device *dev, u32 unit, u32 slot, + const u8 *key_data, u32 key_length) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + int retval = -EINVAL; + u32 slot_size; + u32 i; + u8 __iomem *slot_location; + + spin_lock(&smpriv->kslock); + + slot_size = smpriv->slot_get_slot_size(dev, unit, slot); + + if (key_length > slot_size) { + retval = -EFBIG; + goto out; + } + + slot_location = smpriv->slot_get_address(dev, unit, slot); + + for (i = 0; i < key_length; i++) + slot_location[i] = key_data[i]; + + retval = 0; + +out: + spin_unlock(&smpriv->kslock); + return retval; +} +EXPORT_SYMBOL(sm_keystore_slot_load); + +int sm_keystore_slot_read(struct device *dev, u32 unit, u32 slot, + u32 key_length, u8 *key_data) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + int retval = -EINVAL; + u8 __iomem *slot_addr; + u32 slot_size; + + spin_lock(&smpriv->kslock); + + slot_addr = smpriv->slot_get_address(dev, unit, slot); + slot_size = smpriv->slot_get_slot_size(dev, unit, slot); + + if (key_length > slot_size) { + retval = -EKEYREJECTED; + goto out; + } + + memcpy(key_data, slot_addr, key_length); + retval = 0; + +out: + spin_unlock(&smpriv->kslock); + return retval; +} +EXPORT_SYMBOL(sm_keystore_slot_read); + +int sm_keystore_slot_encapsulate(struct device *dev, u32 unit, u32 inslot, + u32 outslot, u16 secretlen, u8 *keymod, + u16 keymodlen) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + int retval = 0; + u32 slot_length, dsize, jstat; + u32 __iomem *encapdesc = NULL; + u8 __iomem *lkeymod, *inpslotaddr, *outslotaddr; + dma_addr_t keymod_dma; + + /* Ensure that the full blob will fit in the key slot */ + slot_length = smpriv->slot_get_slot_size(dev, unit, outslot); + if ((secretlen + 48) > slot_length) + goto out; + + /* Get the base addresses of both keystore slots */ + inpslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, inslot); + outslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, outslot); + + /* Build the key modifier */ + lkeymod = kmalloc(keymodlen, GFP_KERNEL | GFP_DMA); + memcpy(lkeymod, keymod, keymodlen); + keymod_dma = dma_map_single(dev, lkeymod, keymodlen, DMA_TO_DEVICE); + dma_sync_single_for_device(dev, keymod_dma, keymodlen, DMA_TO_DEVICE); + + /* Build the encapsulation job descriptor */ + dsize = blob_encap_desc(&encapdesc, keymod_dma, keymodlen, + __pa(inpslotaddr), __pa(outslotaddr), + secretlen, 0); + if (!dsize) { + dev_err(dev, "can't alloc an encap descriptor\n"); + retval = -ENOMEM; + goto out; + } + jstat = sm_key_job(dev, encapdesc); + + dma_unmap_single(dev, keymod_dma, keymodlen, DMA_TO_DEVICE); + kfree(encapdesc); + +out: + return retval; + +} +EXPORT_SYMBOL(sm_keystore_slot_encapsulate); + +int sm_keystore_slot_decapsulate(struct device *dev, u32 unit, u32 inslot, + u32 outslot, u16 secretlen, u8 *keymod, + u16 keymodlen) +{ + struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev); + int retval = 0; + u32 slot_length, dsize, jstat; + u32 __iomem *decapdesc = NULL; + u8 __iomem *lkeymod, *inpslotaddr, *outslotaddr; + dma_addr_t keymod_dma; + + /* Ensure that the decap data will fit in the key slot */ + slot_length = smpriv->slot_get_slot_size(dev, unit, outslot); + if (secretlen > slot_length) + goto out; + + /* Get the base addresses of both keystore slots */ + inpslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, inslot); + outslotaddr = (u8 *)smpriv->slot_get_address(dev, unit, outslot); + + /* Build the key modifier */ + lkeymod = kmalloc(keymodlen, GFP_KERNEL | GFP_DMA); + memcpy(lkeymod, keymod, keymodlen); + keymod_dma = dma_map_single(dev, lkeymod, keymodlen, DMA_TO_DEVICE); + dma_sync_single_for_device(dev, keymod_dma, keymodlen, DMA_TO_DEVICE); + + /* Build the decapsulation job descriptor */ + dsize = blob_decap_desc(&decapdesc, keymod_dma, keymodlen, + __pa(inpslotaddr), __pa(outslotaddr), + secretlen, 0); + if (!dsize) { + dev_err(dev, "can't alloc a decap descriptor\n"); + retval = -ENOMEM; + goto out; + } + jstat = sm_key_job(dev, decapdesc); + + dma_unmap_single(dev, keymod_dma, keymodlen, DMA_TO_DEVICE); + kfree(decapdesc); + +out: + return retval; + +} +EXPORT_SYMBOL(sm_keystore_slot_decapsulate); + + +/* + * Initialization/shutdown subsystem + * Assumes statically-invoked startup/shutdown from the controller driver + * for the present time, to be reworked when a device tree becomes + * available. This code will not modularize in present form. + * + * Also, simply uses ring 0 for execution at the present + */ + +int caam_sm_startup(struct platform_device *pdev) +{ + struct device *ctrldev, *smdev; + struct caam_drv_private *ctrlpriv; + struct caam_drv_private_sm *smpriv; + struct caam_drv_private_jr *jrpriv; /* need this for reg page */ + struct platform_device *sm_pdev; + struct sm_page_descriptor *lpagedesc; + u32 page, pgstat, lpagect, detectedpage; + + struct device_node *np; + ctrldev = &pdev->dev; + ctrlpriv = dev_get_drvdata(ctrldev); + + /* + * Set up the private block for secure memory + * Only one instance is possible + */ + smpriv = kzalloc(sizeof(struct caam_drv_private_sm), GFP_KERNEL); + if (smpriv == NULL) { + dev_err(ctrldev, "can't alloc private mem for secure memory\n"); + return -ENOMEM; + } + smpriv->parentdev = ctrldev; /* copy of parent dev is handy */ + + /* Create the dev */ +#ifdef CONFIG_OF + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-sm"); + sm_pdev = of_platform_device_create(np, "caam_sm", ctrldev); +#else + sm_pdev = platform_device_register_data(ctrldev, "caam_sm", 0, + smpriv, + sizeof(struct caam_drv_private_sm)); +#endif + if (sm_pdev == NULL) { + kfree(smpriv); + return -EINVAL; + } + smdev = &sm_pdev->dev; + dev_set_drvdata(smdev, smpriv); + ctrlpriv->smdev = smdev; + + /* + * Collect configuration limit data for reference + * This batch comes from the partition data/vid registers in perfmon + */ + smpriv->max_pages = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart) + & SMPART_MAX_NUMPG_MASK) >> + SMPART_MAX_NUMPG_SHIFT) + 1; + smpriv->top_partition = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart) + & SMPART_MAX_PNUM_MASK) >> + SMPART_MAX_PNUM_SHIFT) + 1; + smpriv->top_page = ((rd_reg32(&ctrlpriv->ctrl->perfmon.smpart) + & SMPART_MAX_PG_MASK) >> SMPART_MAX_PG_SHIFT) + 1; + smpriv->page_size = 1024 << ((rd_reg32(&ctrlpriv->ctrl->perfmon.smvid) + & SMVID_PG_SIZE_MASK) >> SMVID_PG_SIZE_SHIFT); + smpriv->slot_size = 1 << CONFIG_CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE; + +#ifdef SM_DEBUG + dev_info(smdev, "max pages = %d, top partition = %d\n", + smpriv->max_pages, smpriv->top_partition); + dev_info(smdev, "top page = %d, page size = %d (total = %d)\n", + smpriv->top_page, smpriv->page_size, + smpriv->top_page * smpriv->page_size); + dev_info(smdev, "selected slot size = %d\n", smpriv->slot_size); +#endif + + /* + * Now probe for partitions/pages to which we have access. Note that + * these have likely been set up by a bootloader or platform + * provisioning application, so we have to assume that we "inherit" + * a configuration and work within the constraints of what it might be. + * + * Assume use of the zeroth ring in the present iteration (until + * we can divorce the controller and ring drivers, and then assign + * an SM instance to any ring instance). + */ + smpriv->smringdev = ctrlpriv->jrdev[0]; + jrpriv = dev_get_drvdata(smpriv->smringdev); + lpagect = 0; + lpagedesc = kzalloc(sizeof(struct sm_page_descriptor) + * smpriv->max_pages, GFP_KERNEL); + if (lpagedesc == NULL) { + kfree(smpriv); + return -ENOMEM; + } + + for (page = 0; page < smpriv->max_pages; page++) { + wr_reg32(&jrpriv->rregs->sm_cmd, + ((page << SMC_PAGE_SHIFT) & SMC_PAGE_MASK) | + (SMC_CMD_PAGE_INQUIRY & SMC_CMD_MASK)); + pgstat = rd_reg32(&jrpriv->rregs->sm_status); + if (((pgstat & SMCS_PGWON_MASK) >> SMCS_PGOWN_SHIFT) + == SMCS_PGOWN_OWNED) { /* our page? */ + lpagedesc[page].phys_pagenum = + (pgstat & SMCS_PAGE_MASK) >> SMCS_PAGE_SHIFT; + lpagedesc[page].own_part = + (pgstat & SMCS_PART_SHIFT) >> SMCS_PART_MASK; + lpagedesc[page].pg_base = ctrlpriv->sm_base + + ((smpriv->page_size * page) / sizeof(u32)); + lpagect++; +#ifdef SM_DEBUG + dev_info(smdev, + "physical page %d, owning partition = %d\n", + lpagedesc[page].phys_pagenum, + lpagedesc[page].own_part); +#endif + } + } + + smpriv->pagedesc = kzalloc(sizeof(struct sm_page_descriptor) * lpagect, + GFP_KERNEL); + if (smpriv->pagedesc == NULL) { + kfree(lpagedesc); + kfree(smpriv); + return -ENOMEM; + } + smpriv->localpages = lpagect; + + detectedpage = 0; + for (page = 0; page < smpriv->max_pages; page++) { + if (lpagedesc[page].pg_base != NULL) { /* e.g. live entry */ + memcpy(&smpriv->pagedesc[detectedpage], + &lpagedesc[page], + sizeof(struct sm_page_descriptor)); +#ifdef SM_DEBUG_CONT + sm_show_page(smdev, &smpriv->pagedesc[detectedpage]); +#endif + detectedpage++; + } + } + + kfree(lpagedesc); + + sm_init_keystore(smdev); + + return 0; +} + +void caam_sm_shutdown(struct platform_device *pdev) +{ + struct device *ctrldev, *smdev; + struct caam_drv_private *priv; + struct caam_drv_private_sm *smpriv; + + ctrldev = &pdev->dev; + priv = dev_get_drvdata(ctrldev); + smdev = priv->smdev; + smpriv = dev_get_drvdata(smdev); + + kfree(smpriv->pagedesc); + kfree(smpriv); +} +EXPORT_SYMBOL(caam_sm_shutdown); +#ifdef CONFIG_OF +static void __exit caam_sm_exit(void) +{ + struct device_node *dev_node; + struct platform_device *pdev; + + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); + if (!dev_node) { + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); + if (!dev_node) + return; + } + + pdev = of_find_device_by_node(dev_node); + if (!pdev) + return; + + of_node_put(dev_node); + + caam_sm_shutdown(pdev); + + return; +} + +static int __init caam_sm_init(void) +{ + struct device_node *dev_node; + struct platform_device *pdev; + + /* + * Do of_find_compatible_node() then of_find_device_by_node() + * once a functional device tree is available + */ + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); + if (!dev_node) { + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); + if (!dev_node) + return -ENODEV; + } + + pdev = of_find_device_by_node(dev_node); + if (!pdev) + return -ENODEV; + + of_node_get(dev_node); + + caam_sm_startup(pdev); + + return 0; +} + +module_init(caam_sm_init); +module_exit(caam_sm_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("FSL CAAM Secure Memory / Keystore"); +MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD"); +#endif diff -Nur linux-3.10.30/drivers/crypto/caam/sm_test.c linux-3.10.30-cubox-i/drivers/crypto/caam/sm_test.c --- linux-3.10.30/drivers/crypto/caam/sm_test.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/sm_test.c 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,844 @@ +/* + * Secure Memory / Keystore Exemplification Module + * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved + * + * Serves as a functional example, and as a self-contained unit test for + * the functionality contained in sm_store.c. + * + * The example function, caam_sm_example_init(), runs a thread that: + * + * - initializes a set of fixed keys + * - stores one copy in clear buffers + * - stores them again in secure memory + * - extracts stored keys back out for use + * - intializes 3 data buffers for a test: + * (1) containing cleartext + * (2) to hold ciphertext encrypted with an extracted black key + * (3) to hold extracted cleartext decrypted with an equivalent clear key + * + * The function then builds simple job descriptors that reference the key + * material and buffers as initialized, and executes an encryption job + * with a black key, and a decryption job using a the same key held in the + * clear. The output of the decryption job is compared to the original + * cleartext; if they don't compare correctly, one can assume a key problem + * exists, where the function will exit with an error. + * + * This module can use a substantial amount of refactoring, which may occur + * after the API gets some mileage. Furthermore, expect this module to + * eventually disappear once the API is integrated into "real" software. + */ + +#include "compat.h" +#include "intern.h" +#include "desc.h" +#include "error.h" +#include "jr.h" +#include "sm.h" + +static u8 skeymod[] = { + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 +}; +static u8 symkey[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f +}; + +static u8 symdata[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x0f, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +static int mk_job_desc(u32 *desc, dma_addr_t key, u16 keysz, dma_addr_t indata, + dma_addr_t outdata, u16 sz, u32 cipherdir, u32 keymode) +{ + desc[1] = CMD_KEY | CLASS_1 | (keysz & KEY_LENGTH_MASK) | keymode; + desc[2] = (u32)key; + desc[3] = CMD_OPERATION | OP_TYPE_CLASS1_ALG | OP_ALG_AAI_ECB | + cipherdir; + desc[4] = CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | + FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1 | sz; + desc[5] = (u32)indata; + desc[6] = CMD_FIFO_STORE | FIFOST_TYPE_MESSAGE_DATA | sz; + desc[7] = (u32)outdata; + + desc[0] = CMD_DESC_HDR | HDR_ONE | (8 & HDR_DESCLEN_MASK); + return 8 * sizeof(u32); +} + +struct exec_test_result { + int error; + struct completion completion; +}; + +void exec_test_done(struct device *dev, u32 *desc, u32 err, void *context) +{ + struct exec_test_result *res = context; + + if (err) { + char tmp[CAAM_ERROR_STR_MAX]; + dev_err(dev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err)); + } + + res->error = err; + complete(&res->completion); +} + +static int exec_test_job(struct device *ksdev, u32 *jobdesc) +{ + struct exec_test_result testres; + struct caam_drv_private_sm *kspriv; + int rtn = 0; + + kspriv = dev_get_drvdata(ksdev); + + init_completion(&testres.completion); + + rtn = caam_jr_enqueue(kspriv->smringdev, jobdesc, exec_test_done, + &testres); + if (!rtn) { + wait_for_completion_interruptible(&testres.completion); + rtn = testres.error; + } + return rtn; +} + + +int caam_sm_example_init(struct platform_device *pdev) +{ + struct device *ctrldev, *ksdev; + struct caam_drv_private *ctrlpriv; + struct caam_drv_private_sm *kspriv; + u32 unit, units, jdescsz; + int stat, jstat, rtnval = 0; + u8 __iomem *syminp, *symint, *symout = NULL; + dma_addr_t syminp_dma, symint_dma, symout_dma; + u8 __iomem *black_key_des, *black_key_aes128; + u8 __iomem *black_key_aes256; + dma_addr_t black_key_des_dma, black_key_aes128_dma; + dma_addr_t black_key_aes256_dma; + u8 __iomem *clear_key_des, *clear_key_aes128, *clear_key_aes256; + dma_addr_t clear_key_des_dma, clear_key_aes128_dma; + dma_addr_t clear_key_aes256_dma; + u32 __iomem *jdesc; + u32 keyslot_des, keyslot_aes128, keyslot_aes256 = 0; + + jdesc = NULL; + black_key_des = black_key_aes128 = black_key_aes256 = NULL; + clear_key_des = clear_key_aes128 = clear_key_aes256 = NULL; + + /* We can lose this cruft once we can get a pdev by name */ + ctrldev = &pdev->dev; + ctrlpriv = dev_get_drvdata(ctrldev); + ksdev = ctrlpriv->smdev; + kspriv = dev_get_drvdata(ksdev); + if (kspriv == NULL) + return -ENODEV; + + /* Now that we have the dev for the single SM instance, connect */ +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test_init() running\n"); +#endif + /* Probe to see what keystores are available to us */ + units = sm_detect_keystore_units(ksdev); + if (!units) + dev_err(ksdev, "caam_sm_test: no keystore units available\n"); + + /* + * MX6 bootloader stores some stuff in unit 0, so let's + * use 1 or above + */ + if (units < 2) { + dev_err(ksdev, "caam_sm_test: insufficient keystore units\n"); + return -ENODEV; + } + unit = 1; + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: %d keystore units available\n", units); +#endif + + /* Initialize/Establish Keystore */ + sm_establish_keystore(ksdev, unit); /* Initalize store in #1 */ + + /* + * Top of main test thread + */ + + /* Allocate test data blocks (input, intermediate, output) */ + syminp = kmalloc(256, GFP_KERNEL | GFP_DMA); + symint = kmalloc(256, GFP_KERNEL | GFP_DMA); + symout = kmalloc(256, GFP_KERNEL | GFP_DMA); + if ((syminp == NULL) || (symint == NULL) || (symout == NULL)) { + rtnval = -ENOMEM; + dev_err(ksdev, "caam_sm_test: can't get test data buffers\n"); + goto freemem; + } + + /* Allocate storage for 3 black keys: encapsulated 8, 16, 32 */ + black_key_des = kmalloc(16, GFP_KERNEL | GFP_DMA); /* padded to 16... */ + black_key_aes128 = kmalloc(16, GFP_KERNEL | GFP_DMA); + black_key_aes256 = kmalloc(16, GFP_KERNEL | GFP_DMA); + if ((black_key_des == NULL) || (black_key_aes128 == NULL) || + (black_key_aes256 == NULL)) { + rtnval = -ENOMEM; + dev_err(ksdev, "caam_sm_test: can't black key buffers\n"); + goto freemem; + } + + clear_key_des = kmalloc(8, GFP_KERNEL | GFP_DMA); + clear_key_aes128 = kmalloc(16, GFP_KERNEL | GFP_DMA); + clear_key_aes256 = kmalloc(32, GFP_KERNEL | GFP_DMA); + if ((clear_key_des == NULL) || (clear_key_aes128 == NULL) || + (clear_key_aes256 == NULL)) { + rtnval = -ENOMEM; + dev_err(ksdev, "caam_sm_test: can't get clear key buffers\n"); + goto freemem; + } + + /* Allocate storage for job descriptor */ + jdesc = kmalloc(8 * sizeof(u32), GFP_KERNEL | GFP_DMA); + if (jdesc == NULL) { + rtnval = -ENOMEM; + dev_err(ksdev, "caam_sm_test: can't get descriptor buffers\n"); + goto freemem; + } + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: all buffers allocated\n"); +#endif + + /* Load up input data block, clear outputs */ + memcpy(syminp, symdata, 256); + memset(symint, 0, 256); + memset(symout, 0, 256); +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + syminp[0], syminp[1], syminp[2], syminp[3], + syminp[4], syminp[5], syminp[6], syminp[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[0], symint[1], symint[2], symint[3], + symint[4], symint[5], symint[6], symint[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symout[0], symout[1], symout[2], symout[3], + symout[4], symout[5], symout[6], symout[7]); + + dev_info(ksdev, "caam_sm_test: data buffers initialized\n"); +#endif + + /* Load up clear keys */ + memcpy(clear_key_des, symkey, 8); + memcpy(clear_key_aes128, symkey, 16); + memcpy(clear_key_aes256, symkey, 32); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: all clear keys loaded\n"); +#endif + + /* + * Place clear keys in keystore. + * All the interesting stuff happens here. + */ + /* 8 bit DES key */ + stat = sm_keystore_slot_alloc(ksdev, unit, 8, &keyslot_des); + if (stat) + goto freemem; +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: 8 byte key slot in %d\n", keyslot_des); +#endif + stat = sm_keystore_slot_load(ksdev, unit, keyslot_des, clear_key_des, + 8); + if (stat) { +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: can't load 8 byte key in %d\n", + keyslot_des); +#endif + sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); + goto freemem; + } + + /* 16 bit AES key */ + stat = sm_keystore_slot_alloc(ksdev, unit, 16, &keyslot_aes128); + if (stat) { + sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); + goto freemem; + } +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: 16 byte key slot in %d\n", + keyslot_aes128); +#endif + stat = sm_keystore_slot_load(ksdev, unit, keyslot_aes128, + clear_key_aes128, 16); + if (stat) { +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: can't load 16 byte key in %d\n", + keyslot_aes128); +#endif + sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128); + sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); + goto freemem; + } + + /* 32 bit AES key */ + stat = sm_keystore_slot_alloc(ksdev, unit, 32, &keyslot_aes256); + if (stat) { + sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128); + sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); + goto freemem; + } +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: 32 byte key slot in %d\n", + keyslot_aes256); +#endif + stat = sm_keystore_slot_load(ksdev, unit, keyslot_aes256, + clear_key_aes256, 32); + if (stat) { +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: can't load 32 byte key in %d\n", + keyslot_aes128); +#endif + sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes256); + sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128); + sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); + goto freemem; + } + + /* Encapsulate all keys as SM blobs */ + stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_des, + keyslot_des, 8, skeymod, 8); + if (stat) { + dev_info(ksdev, "caam_sm_test: can't encapsulate DES key\n"); + goto freekeys; + } + + stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_aes128, + keyslot_aes128, 16, skeymod, 8); + if (stat) { + dev_info(ksdev, "caam_sm_test: can't encapsulate AES128 key\n"); + goto freekeys; + } + + stat = sm_keystore_slot_encapsulate(ksdev, unit, keyslot_aes256, + keyslot_aes256, 32, skeymod, 8); + if (stat) { + dev_info(ksdev, "caam_sm_test: can't encapsulate AES256 key\n"); + goto freekeys; + } + + /* Now decapsulate as black key blobs */ + stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_des, + keyslot_des, 8, skeymod, 8); + if (stat) { + dev_info(ksdev, "caam_sm_test: can't decapsulate DES key\n"); + goto freekeys; + } + + stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_aes128, + keyslot_aes128, 16, skeymod, 8); + if (stat) { + dev_info(ksdev, "caam_sm_test: can't decapsulate AES128 key\n"); + goto freekeys; + } + + stat = sm_keystore_slot_decapsulate(ksdev, unit, keyslot_aes256, + keyslot_aes256, 32, skeymod, 8); + if (stat) { + dev_info(ksdev, "caam_sm_test: can't decapsulate AES128 key\n"); + goto freekeys; + } + + /* Extract 8/16/32 byte black keys */ + sm_keystore_slot_read(ksdev, unit, keyslot_des, 8, black_key_des); + sm_keystore_slot_read(ksdev, unit, keyslot_aes128, 16, + black_key_aes128); + sm_keystore_slot_read(ksdev, unit, keyslot_aes256, 32, + black_key_aes256); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: all black keys extracted\n"); +#endif + + /* DES encrypt using 8 byte black key */ + black_key_des_dma = dma_map_single(ksdev, black_key_des, 8, + DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, black_key_des_dma, 8, DMA_TO_DEVICE); + syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE); + symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE); + + jdescsz = mk_job_desc(jdesc, black_key_des_dma, 8, syminp_dma, + symint_dma, 256, + OP_ALG_ENCRYPT | OP_ALG_ALGSEL_DES, 0); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "jobdesc:\n"); + dev_info(ksdev, "0x%08x\n", jdesc[0]); + dev_info(ksdev, "0x%08x\n", jdesc[1]); + dev_info(ksdev, "0x%08x\n", jdesc[2]); + dev_info(ksdev, "0x%08x\n", jdesc[3]); + dev_info(ksdev, "0x%08x\n", jdesc[4]); + dev_info(ksdev, "0x%08x\n", jdesc[5]); + dev_info(ksdev, "0x%08x\n", jdesc[6]); + dev_info(ksdev, "0x%08x\n", jdesc[7]); +#endif + + jstat = exec_test_job(ksdev, jdesc); + + dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE); + dma_unmap_single(ksdev, black_key_des_dma, 8, DMA_TO_DEVICE); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "input block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + syminp[0], syminp[1], syminp[2], syminp[3], + syminp[4], syminp[5], syminp[6], syminp[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + syminp[8], syminp[9], syminp[10], syminp[11], + syminp[12], syminp[13], syminp[14], syminp[15]); + dev_info(ksdev, "intermediate block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[0], symint[1], symint[2], symint[3], + symint[4], symint[5], symint[6], symint[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[8], symint[9], symint[10], symint[11], + symint[12], symint[13], symint[14], symint[15]); + dev_info(ksdev, "caam_sm_test: encrypt cycle with 8 byte key\n"); +#endif + + /* DES decrypt using 8 byte clear key */ + clear_key_des_dma = dma_map_single(ksdev, clear_key_des, 8, + DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, clear_key_des_dma, 8, DMA_TO_DEVICE); + symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE); + symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE); + + jdescsz = mk_job_desc(jdesc, clear_key_des_dma, 8, symint_dma, + symout_dma, 256, + OP_ALG_DECRYPT | OP_ALG_ALGSEL_DES, 0); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "jobdesc:\n"); + dev_info(ksdev, "0x%08x\n", jdesc[0]); + dev_info(ksdev, "0x%08x\n", jdesc[1]); + dev_info(ksdev, "0x%08x\n", jdesc[2]); + dev_info(ksdev, "0x%08x\n", jdesc[3]); + dev_info(ksdev, "0x%08x\n", jdesc[4]); + dev_info(ksdev, "0x%08x\n", jdesc[5]); + dev_info(ksdev, "0x%08x\n", jdesc[6]); + dev_info(ksdev, "0x%08x\n", jdesc[7]); +#endif + + jstat = exec_test_job(ksdev, jdesc); + + dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE); + dma_unmap_single(ksdev, clear_key_des_dma, 8, DMA_TO_DEVICE); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "intermediate block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[0], symint[1], symint[2], symint[3], + symint[4], symint[5], symint[6], symint[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[8], symint[9], symint[10], symint[11], + symint[12], symint[13], symint[14], symint[15]); + dev_info(ksdev, "decrypted block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symout[0], symout[1], symout[2], symout[3], + symout[4], symout[5], symout[6], symout[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symout[8], symout[9], symout[10], symout[11], + symout[12], symout[13], symout[14], symout[15]); + dev_info(ksdev, "caam_sm_test: decrypt cycle with 8 byte key\n"); +#endif + + /* Check result */ + if (memcmp(symout, syminp, 256)) { + dev_info(ksdev, "caam_sm_test: 8-byte key test mismatch\n"); + rtnval = -1; + goto freekeys; + } else + dev_info(ksdev, "caam_sm_test: 8-byte key test match OK\n"); + + /* AES-128 encrypt using 16 byte black key */ + black_key_aes128_dma = dma_map_single(ksdev, black_key_aes128, 16, + DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, black_key_aes128_dma, 16, + DMA_TO_DEVICE); + syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE); + symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE); + + jdescsz = mk_job_desc(jdesc, black_key_aes128_dma, 16, syminp_dma, + symint_dma, 256, + OP_ALG_ENCRYPT | OP_ALG_ALGSEL_AES, 0); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "jobdesc:\n"); + dev_info(ksdev, "0x%08x\n", jdesc[0]); + dev_info(ksdev, "0x%08x\n", jdesc[1]); + dev_info(ksdev, "0x%08x\n", jdesc[2]); + dev_info(ksdev, "0x%08x\n", jdesc[3]); + dev_info(ksdev, "0x%08x\n", jdesc[4]); + dev_info(ksdev, "0x%08x\n", jdesc[5]); + dev_info(ksdev, "0x%08x\n", jdesc[6]); + dev_info(ksdev, "0x%08x\n", jdesc[7]); +#endif + + jstat = exec_test_job(ksdev, jdesc); + + dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE); + dma_unmap_single(ksdev, black_key_aes128_dma, 16, DMA_TO_DEVICE); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "input block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + syminp[0], syminp[1], syminp[2], syminp[3], + syminp[4], syminp[5], syminp[6], syminp[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + syminp[8], syminp[9], syminp[10], syminp[11], + syminp[12], syminp[13], syminp[14], syminp[15]); + dev_info(ksdev, "intermediate block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[0], symint[1], symint[2], symint[3], + symint[4], symint[5], symint[6], symint[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[8], symint[9], symint[10], symint[11], + symint[12], symint[13], symint[14], symint[15]); + dev_info(ksdev, "caam_sm_test: encrypt cycle with 16 byte key\n"); +#endif + + /* AES-128 decrypt using 16 byte clear key */ + clear_key_aes128_dma = dma_map_single(ksdev, clear_key_aes128, 16, + DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, clear_key_aes128_dma, 16, + DMA_TO_DEVICE); + symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE); + symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE); + + jdescsz = mk_job_desc(jdesc, clear_key_aes128_dma, 16, symint_dma, + symout_dma, 256, + OP_ALG_DECRYPT | OP_ALG_ALGSEL_AES, 0); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "jobdesc:\n"); + dev_info(ksdev, "0x%08x\n", jdesc[0]); + dev_info(ksdev, "0x%08x\n", jdesc[1]); + dev_info(ksdev, "0x%08x\n", jdesc[2]); + dev_info(ksdev, "0x%08x\n", jdesc[3]); + dev_info(ksdev, "0x%08x\n", jdesc[4]); + dev_info(ksdev, "0x%08x\n", jdesc[5]); + dev_info(ksdev, "0x%08x\n", jdesc[6]); + dev_info(ksdev, "0x%08x\n", jdesc[7]); +#endif + jstat = exec_test_job(ksdev, jdesc); + + dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE); + dma_unmap_single(ksdev, clear_key_aes128_dma, 16, DMA_TO_DEVICE); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "intermediate block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[0], symint[1], symint[2], symint[3], + symint[4], symint[5], symint[6], symint[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[8], symint[9], symint[10], symint[11], + symint[12], symint[13], symint[14], symint[15]); + dev_info(ksdev, "decrypted block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symout[0], symout[1], symout[2], symout[3], + symout[4], symout[5], symout[6], symout[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symout[8], symout[9], symout[10], symout[11], + symout[12], symout[13], symout[14], symout[15]); + dev_info(ksdev, "caam_sm_test: decrypt cycle with 16 byte key\n"); +#endif + + /* Check result */ + if (memcmp(symout, syminp, 256)) { + dev_info(ksdev, "caam_sm_test: 16-byte key test mismatch\n"); + rtnval = -1; + goto freekeys; + } else + dev_info(ksdev, "caam_sm_test: 16-byte key test match OK\n"); + + /* AES-256 encrypt using 32 byte black key */ + black_key_aes256_dma = dma_map_single(ksdev, black_key_aes256, 32, + DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, black_key_aes256_dma, 32, + DMA_TO_DEVICE); + syminp_dma = dma_map_single(ksdev, syminp, 256, DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, syminp_dma, 256, DMA_TO_DEVICE); + symint_dma = dma_map_single(ksdev, symint, 256, DMA_FROM_DEVICE); + + jdescsz = mk_job_desc(jdesc, black_key_aes256_dma, 32, syminp_dma, + symint_dma, 256, + OP_ALG_ENCRYPT | OP_ALG_ALGSEL_AES, 0); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "jobdesc:\n"); + dev_info(ksdev, "0x%08x\n", jdesc[0]); + dev_info(ksdev, "0x%08x\n", jdesc[1]); + dev_info(ksdev, "0x%08x\n", jdesc[2]); + dev_info(ksdev, "0x%08x\n", jdesc[3]); + dev_info(ksdev, "0x%08x\n", jdesc[4]); + dev_info(ksdev, "0x%08x\n", jdesc[5]); + dev_info(ksdev, "0x%08x\n", jdesc[6]); + dev_info(ksdev, "0x%08x\n", jdesc[7]); +#endif + + jstat = exec_test_job(ksdev, jdesc); + + dma_sync_single_for_cpu(ksdev, symint_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symint_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, syminp_dma, 256, DMA_TO_DEVICE); + dma_unmap_single(ksdev, black_key_aes256_dma, 32, DMA_TO_DEVICE); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "input block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + syminp[0], syminp[1], syminp[2], syminp[3], + syminp[4], syminp[5], syminp[6], syminp[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + syminp[8], syminp[9], syminp[10], syminp[11], + syminp[12], syminp[13], syminp[14], syminp[15]); + dev_info(ksdev, "intermediate block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[0], symint[1], symint[2], symint[3], + symint[4], symint[5], symint[6], symint[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[8], symint[9], symint[10], symint[11], + symint[12], symint[13], symint[14], symint[15]); + dev_info(ksdev, "caam_sm_test: encrypt cycle with 32 byte key\n"); +#endif + + /* AES-256 decrypt using 32-byte black key */ + clear_key_aes256_dma = dma_map_single(ksdev, clear_key_aes256, 32, + DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, clear_key_aes256_dma, 32, + DMA_TO_DEVICE); + symint_dma = dma_map_single(ksdev, symint, 256, DMA_TO_DEVICE); + dma_sync_single_for_device(ksdev, symint_dma, 256, DMA_TO_DEVICE); + symout_dma = dma_map_single(ksdev, symout, 256, DMA_FROM_DEVICE); + + jdescsz = mk_job_desc(jdesc, clear_key_aes256_dma, 32, symint_dma, + symout_dma, 256, + OP_ALG_DECRYPT | OP_ALG_ALGSEL_AES, 0); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "jobdesc:\n"); + dev_info(ksdev, "0x%08x\n", jdesc[0]); + dev_info(ksdev, "0x%08x\n", jdesc[1]); + dev_info(ksdev, "0x%08x\n", jdesc[2]); + dev_info(ksdev, "0x%08x\n", jdesc[3]); + dev_info(ksdev, "0x%08x\n", jdesc[4]); + dev_info(ksdev, "0x%08x\n", jdesc[5]); + dev_info(ksdev, "0x%08x\n", jdesc[6]); + dev_info(ksdev, "0x%08x\n", jdesc[7]); +#endif + + jstat = exec_test_job(ksdev, jdesc); + + dma_sync_single_for_cpu(ksdev, symout_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symout_dma, 256, DMA_FROM_DEVICE); + dma_unmap_single(ksdev, symint_dma, 256, DMA_TO_DEVICE); + dma_unmap_single(ksdev, clear_key_aes256_dma, 32, DMA_TO_DEVICE); + +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "intermediate block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[0], symint[1], symint[2], symint[3], + symint[4], symint[5], symint[6], symint[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symint[8], symint[9], symint[10], symint[11], + symint[12], symint[13], symint[14], symint[15]); + dev_info(ksdev, "decrypted block:\n"); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symout[0], symout[1], symout[2], symout[3], + symout[4], symout[5], symout[6], symout[7]); + dev_info(ksdev, "0x%02x 0x%02x 0x%02x 0x%02x " \ + "0x%02x 0x%02x 0x%02x 0x%02x\n", + symout[8], symout[9], symout[10], symout[11], + symout[12], symout[13], symout[14], symout[15]); + dev_info(ksdev, "caam_sm_test: decrypt cycle with 32 byte key\n"); +#endif + + /* Check result */ + if (memcmp(symout, syminp, 256)) { + dev_info(ksdev, "caam_sm_test: 32-byte key test mismatch\n"); + rtnval = -1; + goto freekeys; + } else + dev_info(ksdev, "caam_sm_test: 32-byte key test match OK\n"); + + + /* Remove 8/16/32 byte keys from keystore */ +freekeys: + stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_des); + if (stat) + dev_info(ksdev, "caam_sm_test: can't release slot %d\n", + keyslot_des); + + stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes128); + if (stat) + dev_info(ksdev, "caam_sm_test: can't release slot %d\n", + keyslot_aes128); + + stat = sm_keystore_slot_dealloc(ksdev, unit, keyslot_aes256); + if (stat) + dev_info(ksdev, "caam_sm_test: can't release slot %d\n", + keyslot_aes256); + + + /* Free resources */ +freemem: +#ifdef SM_TEST_DETAIL + dev_info(ksdev, "caam_sm_test: cleaning up\n"); +#endif + kfree(syminp); + kfree(symint); + kfree(symout); + kfree(clear_key_des); + kfree(clear_key_aes128); + kfree(clear_key_aes256); + kfree(black_key_des); + kfree(black_key_aes128); + kfree(black_key_aes256); + kfree(jdesc); + + /* Disconnect from keystore and leave */ + sm_release_keystore(ksdev, unit); + + return rtnval; +} +EXPORT_SYMBOL(caam_sm_example_init); + +void caam_sm_example_shutdown(void) +{ + /* unused in present version */ + struct device_node *dev_node; + struct platform_device *pdev; + + /* + * Do of_find_compatible_node() then of_find_device_by_node() + * once a functional device tree is available + */ + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); + if (!dev_node) { + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); + if (!dev_node) + return; + } + + pdev = of_find_device_by_node(dev_node); + if (!pdev) + return; + + of_node_get(dev_node); + +} + +static int __init caam_sm_test_init(void) +{ + struct device_node *dev_node; + struct platform_device *pdev; + + /* + * Do of_find_compatible_node() then of_find_device_by_node() + * once a functional device tree is available + */ + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); + if (!dev_node) { + dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0"); + if (!dev_node) + return -ENODEV; + } + + pdev = of_find_device_by_node(dev_node); + if (!pdev) + return -ENODEV; + + of_node_put(dev_node); + + caam_sm_example_init(pdev); + + return 0; +} + + +/* Module-based initialization needs to wait for dev tree */ +#ifdef CONFIG_OF +module_init(caam_sm_test_init); +module_exit(caam_sm_example_shutdown); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("FSL CAAM Keystore Usage Example"); +MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD"); +#endif diff -Nur linux-3.10.30/drivers/crypto/caam/snvsregs.h linux-3.10.30-cubox-i/drivers/crypto/caam/snvsregs.h --- linux-3.10.30/drivers/crypto/caam/snvsregs.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/crypto/caam/snvsregs.h 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,237 @@ +/* + * SNVS hardware register-level view + * + * Copyright (C) 2013 Freescale Semiconductor, Inc., All Rights Reserved + */ + +#ifndef SNVSREGS_H +#define SNVSREGS_H + +#include +#include + +/* + * SNVS High Power Domain + * Includes security violations, HA counter, RTC, alarm + */ +struct snvs_hp { + u32 lock; + u32 cmd; + u32 ctl; + u32 secvio_int_en; /* Security Violation Interrupt Enable */ + u32 secvio_int_ctl; /* Security Violation Interrupt Control */ + u32 status; + u32 secvio_status; /* Security Violation Status */ + u32 ha_counteriv; /* High Assurance Counter IV */ + u32 ha_counter; /* High Assurance Counter */ + u32 rtc_msb; /* Real Time Clock/Counter MSB */ + u32 rtc_lsb; /* Real Time Counter LSB */ + u32 time_alarm_msb; /* Time Alarm MSB */ + u32 time_alarm_lsb; /* Time Alarm LSB */ +}; + +#define HP_LOCK_HAC_LCK 0x00040000 +#define HP_LOCK_HPSICR_LCK 0x00020000 +#define HP_LOCK_HPSVCR_LCK 0x00010000 +#define HP_LOCK_MKEYSEL_LCK 0x00000200 +#define HP_LOCK_TAMPCFG_LCK 0x00000100 +#define HP_LOCK_TAMPFLT_LCK 0x00000080 +#define HP_LOCK_SECVIO_LCK 0x00000040 +#define HP_LOCK_GENP_LCK 0x00000020 +#define HP_LOCK_MONOCTR_LCK 0x00000010 +#define HP_LOCK_CALIB_LCK 0x00000008 +#define HP_LOCK_SRTC_LCK 0x00000004 +#define HP_LOCK_ZMK_RD_LCK 0x00000002 +#define HP_LOCK_ZMK_WT_LCK 0x00000001 + +#define HP_CMD_NONPRIV_AXS 0x80000000 +#define HP_CMD_HAC_STOP 0x00080000 +#define HP_CMD_HAC_CLEAR 0x00040000 +#define HP_CMD_HAC_LOAD 0x00020000 +#define HP_CMD_HAC_CFG_EN 0x00010000 +#define HP_CMD_SNVS_MSTR_KEY 0x00002000 +#define HP_CMD_PROG_ZMK 0x00001000 +#define HP_CMD_SW_LPSV 0x00000400 +#define HP_CMD_SW_FSV 0x00000200 +#define HP_CMD_SW_SV 0x00000100 +#define HP_CMD_LP_SWR_DIS 0x00000020 +#define HP_CMD_LP_SWR 0x00000010 +#define HP_CMD_SSM_SFNS_DIS 0x00000004 +#define HP_CMD_SSM_ST_DIS 0x00000002 +#define HP_CMD_SMM_ST 0x00000001 + +#define HP_CTL_TIME_SYNC 0x00010000 +#define HP_CTL_CAL_VAL_SHIFT 10 +#define HP_CTL_CAL_VAL_MASK (0x1f << HP_CTL_CALIB_SHIFT) +#define HP_CTL_CALIB_EN 0x00000100 +#define HP_CTL_PI_FREQ_SHIFT 4 +#define HP_CTL_PI_FREQ_MASK (0xf << HP_CTL_PI_FREQ_SHIFT) +#define HP_CTL_PI_EN 0x00000008 +#define HP_CTL_TIMEALARM_EN 0x00000002 +#define HP_CTL_RTC_EN 0x00000001 + +#define HP_SECVIO_INTEN_EN 0x10000000 +#define HP_SECVIO_INTEN_SRC5 0x00000020 +#define HP_SECVIO_INTEN_SRC4 0x00000010 +#define HP_SECVIO_INTEN_SRC3 0x00000008 +#define HP_SECVIO_INTEN_SRC2 0x00000004 +#define HP_SECVIO_INTEN_SRC1 0x00000002 +#define HP_SECVIO_INTEN_SRC0 0x00000001 +#define HP_SECVIO_INTEN_ALL 0x8000003f + +#define HP_SECVIO_ICTL_CFG_SHIFT 30 +#define HP_SECVIO_ICTL_CFG_MASK (0x3 << HP_SECVIO_ICTL_CFG_SHIFT) +#define HP_SECVIO_ICTL_CFG5_SHIFT 5 +#define HP_SECVIO_ICTL_CFG5_MASK (0x3 << HP_SECVIO_ICTL_CFG5_SHIFT) +#define HP_SECVIO_ICTL_CFG_DISABLE 0 +#define HP_SECVIO_ICTL_CFG_NONFATAL 1 +#define HP_SECVIO_ICTL_CFG_FATAL 2 +#define HP_SECVIO_ICTL_CFG4_FATAL 0x00000010 +#define HP_SECVIO_ICTL_CFG3_FATAL 0x00000008 +#define HP_SECVIO_ICTL_CFG2_FATAL 0x00000004 +#define HP_SECVIO_ICTL_CFG1_FATAL 0x00000002 +#define HP_SECVIO_ICTL_CFG0_FATAL 0x00000001 + +#define HP_STATUS_ZMK_ZERO 0x80000000 +#define HP_STATUS_OTPMK_ZERO 0x08000000 +#define HP_STATUS_OTPMK_SYN_SHIFT 16 +#define HP_STATUS_OTPMK_SYN_MASK (0x1ff << HP_STATUS_OTPMK_SYN_SHIFT) +#define HP_STATUS_SSM_ST_SHIFT 8 +#define HP_STATUS_SSM_ST_MASK (0xf << HP_STATUS_SSM_ST_SHIFT) +#define HP_STATUS_SSM_ST_INIT 0 +#define HP_STATUS_SSM_ST_HARDFAIL 1 +#define HP_STATUS_SSM_ST_SOFTFAIL 3 +#define HP_STATUS_SSM_ST_INITINT 8 +#define HP_STATUS_SSM_ST_CHECK 9 +#define HP_STATUS_SSM_ST_NONSECURE 11 +#define HP_STATUS_SSM_ST_TRUSTED 13 +#define HP_STATUS_SSM_ST_SECURE 15 + +#define HP_SECVIOST_ZMK_ECC_FAIL 0x08000000 /* write to clear */ +#define HP_SECVIOST_ZMK_SYN_SHIFT 16 +#define HP_SECVIOST_ZMK_SYN_MASK (0x1ff << HP_SECVIOST_ZMK_SYN_SHIFT) +#define HP_SECVIOST_SECVIO5 0x00000020 +#define HP_SECVIOST_SECVIO4 0x00000010 +#define HP_SECVIOST_SECVIO3 0x00000008 +#define HP_SECVIOST_SECVIO2 0x00000004 +#define HP_SECVIOST_SECVIO1 0x00000002 +#define HP_SECVIOST_SECVIO0 0x00000001 +#define HP_SECVIOST_SECVIOMASK 0x0000003f + +/* + * SNVS Low Power Domain + * Includes glitch detector, SRTC, alarm, monotonic counter, ZMK + */ +struct snvs_lp { + u32 lock; + u32 ctl; + u32 mstr_key_ctl; /* Master Key Control */ + u32 secvio_ctl; /* Security Violation Control */ + u32 tamper_filt_cfg; /* Tamper Glitch Filters Configuration */ + u32 tamper_det_cfg; /* Tamper Detectors Configuration */ + u32 status; + u32 srtc_msb; /* Secure Real Time Clock/Counter MSB */ + u32 srtc_lsb; /* Secure Real Time Clock/Counter LSB */ + u32 time_alarm; /* Time Alarm */ + u32 smc_msb; /* Secure Monotonic Counter MSB */ + u32 smc_lsb; /* Secure Monotonic Counter LSB */ + u32 pwr_glitch_det; /* Power Glitch Detector */ + u32 gen_purpose; + u32 zmk[8]; /* Zeroizable Master Key */ +}; + +#define LP_LOCK_MKEYSEL_LCK 0x00000200 +#define LP_LOCK_TAMPDET_LCK 0x00000100 +#define LP_LOCK_TAMPFLT_LCK 0x00000080 +#define LP_LOCK_SECVIO_LCK 0x00000040 +#define LP_LOCK_GENP_LCK 0x00000020 +#define LP_LOCK_MONOCTR_LCK 0x00000010 +#define LP_LOCK_CALIB_LCK 0x00000008 +#define LP_LOCK_SRTC_LCK 0x00000004 +#define LP_LOCK_ZMK_RD_LCK 0x00000002 +#define LP_LOCK_ZMK_WT_LCK 0x00000001 + +#define LP_CTL_CAL_VAL_SHIFT 10 +#define LP_CTL_CAL_VAL_MASK (0x1f << LP_CTL_CAL_VAL_SHIFT) +#define LP_CTL_CALIB_EN 0x00000100 +#define LP_CTL_SRTC_INVAL_EN 0x00000010 +#define LP_CTL_WAKE_INT_EN 0x00000008 +#define LP_CTL_MONOCTR_EN 0x00000004 +#define LP_CTL_TIMEALARM_EN 0x00000002 +#define LP_CTL_SRTC_EN 0x00000001 + +#define LP_MKEYCTL_ZMKECC_SHIFT 8 +#define LP_MKEYCTL_ZMKECC_MASK (0xff << LP_MKEYCTL_ZMKECC_SHIFT) +#define LP_MKEYCTL_ZMKECC_EN 0x00000010 +#define LP_MKEYCTL_ZMKECC_VAL 0x00000008 +#define LP_MKEYCTL_ZMKECC_PROG 0x00000004 +#define LP_MKEYCTL_MKSEL_SHIFT 0 +#define LP_MKEYCTL_MKSEL_MASK (3 << LP_MKEYCTL_MKSEL_SHIFT) +#define LP_MKEYCTL_MK_OTP 0 +#define LP_MKEYCTL_MK_ZMK 2 +#define LP_MKEYCTL_MK_COMB 3 + +#define LP_SECVIO_CTL_SRC5 0x20 +#define LP_SECVIO_CTL_SRC4 0x10 +#define LP_SECVIO_CTL_SRC3 0x08 +#define LP_SECVIO_CTL_SRC2 0x04 +#define LP_SECVIO_CTL_SRC1 0x02 +#define LP_SECVIO_CTL_SRC0 0x01 + +#define LP_TAMPFILT_EXT2_EN 0x80000000 +#define LP_TAMPFILT_EXT2_SHIFT 24 +#define LP_TAMPFILT_EXT2_MASK (0x1f << LP_TAMPFILT_EXT2_SHIFT) +#define LP_TAMPFILT_EXT1_EN 0x00800000 +#define LP_TAMPFILT_EXT1_SHIFT 16 +#define LP_TAMPFILT_EXT1_MASK (0x1f << LP_TAMPFILT_EXT1_SHIFT) +#define LP_TAMPFILT_WM_EN 0x00000080 +#define LP_TAMPFILT_WM_SHIFT 0 +#define LP_TAMPFILT_WM_MASK (0x1f << LP_TAMPFILT_WM_SHIFT) + +#define LP_TAMPDET_OSC_BPS 0x10000000 +#define LP_TAMPDET_VRC_SHIFT 24 +#define LP_TAMPDET_VRC_MASK (3 << LP_TAMPFILT_VRC_SHIFT) +#define LP_TAMPDET_HTDC_SHIFT 20 +#define LP_TAMPDET_HTDC_MASK (3 << LP_TAMPFILT_HTDC_SHIFT) +#define LP_TAMPDET_LTDC_SHIFT 16 +#define LP_TAMPDET_LTDC_MASK (3 << LP_TAMPFILT_LTDC_SHIFT) +#define LP_TAMPDET_POR_OBS 0x00008000 +#define LP_TAMPDET_PFD_OBS 0x00004000 +#define LP_TAMPDET_ET2_EN 0x00000400 +#define LP_TAMPDET_ET1_EN 0x00000200 +#define LP_TAMPDET_WMT2_EN 0x00000100 +#define LP_TAMPDET_WMT1_EN 0x00000080 +#define LP_TAMPDET_VT_EN 0x00000040 +#define LP_TAMPDET_TT_EN 0x00000020 +#define LP_TAMPDET_CT_EN 0x00000010 +#define LP_TAMPDET_MCR_EN 0x00000004 +#define LP_TAMPDET_SRTCR_EN 0x00000002 + +#define LP_STATUS_SECURE +#define LP_STATUS_NONSECURE +#define LP_STATUS_SCANEXIT 0x00100000 /* all write 1 clear here on */ +#define LP_STATUS_EXT_SECVIO 0x00010000 +#define LP_STATUS_ET2 0x00000400 +#define LP_STATUS_ET1 0x00000200 +#define LP_STATUS_WMT2 0x00000100 +#define LP_STATUS_WMT1 0x00000080 +#define LP_STATUS_VTD 0x00000040 +#define LP_STATUS_TTD 0x00000020 +#define LP_STATUS_CTD 0x00000010 +#define LP_STATUS_PGD 0x00000008 +#define LP_STATUS_MCR 0x00000004 +#define LP_STATUS_SRTCR 0x00000002 +#define LP_STATUS_LPTA 0x00000001 + +/* Full SNVS register page, including version/options */ +struct snvs_full { + struct snvs_hp hp; + struct snvs_lp lp; + u32 rsvd[731]; /* deadspace 0x08c-0xbf7 */ + + /* Version / Revision / Option ID space - end of register page */ + u32 vid; /* 0xbf8 HP Version ID (VID 1) */ + u32 opt_rev; /* 0xbfc HP Options / Revision (VID 2) */ +}; + +#endif /* SNVSREGS_H */ diff -Nur linux-3.10.30/drivers/dma/Kconfig linux-3.10.30-cubox-i/drivers/dma/Kconfig --- linux-3.10.30/drivers/dma/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/dma/Kconfig 2014-03-08 20:33:30.000000000 +0100 @@ -152,6 +152,19 @@ To avoid bloating the irq_desc[] array we allocate a sufficient number of IRQ slots and map them dynamically to specific sources. +config MXC_PXP_V2 + bool "MXC PxP V2 support" + depends on ARM + select DMA_ENGINE + help + Support the PxP (Pixel Pipeline) on i.MX6 DualLite and i.MX6 SoloLite. + If unsure, select N. + +config MXC_PXP_CLIENT_DEVICE + bool "MXC PxP Client Device" + default y + depends on MXC_PXP_V2 + config TXX9_DMAC tristate "Toshiba TXx9 SoC DMA support" depends on MACH_TX49XX || MACH_TX39XX diff -Nur linux-3.10.30/drivers/dma/Makefile linux-3.10.30-cubox-i/drivers/dma/Makefile --- linux-3.10.30/drivers/dma/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/dma/Makefile 2014-03-08 20:33:30.000000000 +0100 @@ -18,6 +18,7 @@ obj-$(CONFIG_DW_DMAC) += dw_dmac.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_MX3_IPU) += ipu/ +obj-$(CONFIG_MXC_PXP_V2) += pxp/ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o obj-$(CONFIG_SH_DMAE_BASE) += sh/ obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o diff -Nur linux-3.10.30/drivers/dma/imx-sdma.c linux-3.10.30-cubox-i/drivers/dma/imx-sdma.c --- linux-3.10.30/drivers/dma/imx-sdma.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/dma/imx-sdma.c 2014-03-08 20:33:30.000000000 +0100 @@ -7,7 +7,7 @@ * * Based on code from Freescale: * - * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include @@ -231,6 +233,14 @@ struct sdma_engine; +enum sdma_mode { + SDMA_MODE_INVALID = 0, + SDMA_MODE_LOOP, + SDMA_MODE_NORMAL, + SDMA_MODE_P2P, + SDMA_MODE_NO_BD, +}; + /** * struct sdma_channel - housekeeping for a SDMA channel * @@ -244,6 +254,7 @@ * @buf_tail ID of the buffer that was processed * @done channel completion * @num_bd max NUM_BD. number of descriptors currently handling + * @bd_iram flag indicating the memory location of buffer descriptor */ struct sdma_channel { struct sdma_engine *sdma; @@ -258,12 +269,16 @@ unsigned int num_bd; struct sdma_buffer_descriptor *bd; dma_addr_t bd_phys; + bool bd_iram; unsigned int pc_from_device, pc_to_device; - unsigned long flags; - dma_addr_t per_address; + unsigned int device_to_device; + unsigned int other_script; + enum sdma_mode mode; + dma_addr_t per_address, per_address2; unsigned long event_mask[2]; unsigned long watermark_level; u32 shp_addr, per_addr; + u32 data_addr1, data_addr2; struct dma_chan chan; spinlock_t lock; struct dma_async_tx_descriptor desc; @@ -273,8 +288,6 @@ struct tasklet_struct tasklet; }; -#define IMX_DMA_SG_LOOP BIT(0) - #define MAX_DMA_CHANNELS 32 #define MXC_SDMA_DEFAULT_PRIORITY 1 #define MXC_SDMA_MIN_PRIORITY 1 @@ -326,6 +339,7 @@ struct clk *clk_ahb; spinlock_t channel_0_lock; struct sdma_script_start_addrs *script_addrs; + struct gen_pool *iram_pool; }; static struct platform_device_id sdma_devtypes[] = { @@ -435,12 +449,14 @@ dma_addr_t buf_phys; int ret; unsigned long flags; + bool use_iram = true; - buf_virt = dma_alloc_coherent(NULL, - size, - &buf_phys, GFP_KERNEL); + buf_virt = gen_pool_dma_alloc(sdma->iram_pool, size, &buf_phys); if (!buf_virt) { - return -ENOMEM; + use_iram = false; + buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL); + if (!buf_virt) + return -ENOMEM; } spin_lock_irqsave(&sdma->channel_0_lock, flags); @@ -457,7 +473,10 @@ spin_unlock_irqrestore(&sdma->channel_0_lock, flags); - dma_free_coherent(NULL, size, buf_virt, buf_phys); + if (use_iram) + gen_pool_free(sdma->iram_pool, (unsigned long)buf_virt, size); + else + dma_free_coherent(NULL, size, buf_virt, buf_phys); return ret; } @@ -542,16 +561,33 @@ sdmac->desc.callback(sdmac->desc.callback_param); } +static void sdma_handle_other_intr(struct sdma_channel *sdmac) +{ + if (sdmac->desc.callback) + sdmac->desc.callback(sdmac->desc.callback_param); +} + static void sdma_tasklet(unsigned long data) { struct sdma_channel *sdmac = (struct sdma_channel *) data; + struct sdma_engine *sdma = sdmac->sdma; complete(&sdmac->done); - if (sdmac->flags & IMX_DMA_SG_LOOP) + switch (sdmac->mode) { + case SDMA_MODE_LOOP: sdma_handle_channel_loop(sdmac); - else + break; + case SDMA_MODE_NORMAL: mxc_sdma_handle_channel_normal(sdmac); + break; + case SDMA_MODE_NO_BD: + sdma_handle_other_intr(sdmac); + break; + default: + dev_err(sdma->dev, "invalid SDMA MODE!\n"); + break; + } } static irqreturn_t sdma_int_handler(int irq, void *dev_id) @@ -589,9 +625,12 @@ * two peripherals or memory-to-memory transfers */ int per_2_per = 0, emi_2_emi = 0; + int other = 0; sdmac->pc_from_device = 0; sdmac->pc_to_device = 0; + sdmac->device_to_device = 0; + sdmac->other_script = 0; switch (peripheral_type) { case IMX_DMATYPE_MEMORY: @@ -624,6 +663,9 @@ emi_2_per = sdma->script_addrs->mcu_2_app_addr; break; case IMX_DMATYPE_SSI_SP: + per_2_emi = sdma->script_addrs->ssish_2_mcu_addr; + emi_2_per = sdma->script_addrs->mcu_2_ssish_addr; + break; case IMX_DMATYPE_MMC: case IMX_DMATYPE_SDHC: case IMX_DMATYPE_CSPI_SP: @@ -633,8 +675,8 @@ emi_2_per = sdma->script_addrs->mcu_2_shp_addr; break; case IMX_DMATYPE_ASRC: - per_2_emi = sdma->script_addrs->asrc_2_mcu_addr; - emi_2_per = sdma->script_addrs->asrc_2_mcu_addr; + per_2_emi = sdma->script_addrs->shp_2_mcu_addr; + emi_2_per = sdma->script_addrs->mcu_2_shp_addr; per_2_per = sdma->script_addrs->per_2_per_addr; break; case IMX_DMATYPE_MSHC: @@ -651,12 +693,17 @@ case IMX_DMATYPE_IPU_MEMORY: emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr; break; + case IMX_DMATYPE_HDMI: + other = sdma->script_addrs->hdmi_dma_addr; + break; default: break; } sdmac->pc_from_device = per_2_emi; sdmac->pc_to_device = emi_2_per; + sdmac->device_to_device = per_2_per; + sdmac->other_script = other; } static int sdma_load_context(struct sdma_channel *sdmac) @@ -669,11 +716,14 @@ int ret; unsigned long flags; - if (sdmac->direction == DMA_DEV_TO_MEM) { + if (sdmac->direction == DMA_DEV_TO_MEM) load_address = sdmac->pc_from_device; - } else { + else if (sdmac->direction == DMA_DEV_TO_DEV) + load_address = sdmac->device_to_device; + else if (sdmac->direction == DMA_MEM_TO_DEV) load_address = sdmac->pc_to_device; - } + else + load_address = sdmac->other_script; if (load_address < 0) return load_address; @@ -693,11 +743,16 @@ /* Send by context the event mask,base address for peripheral * and watermark level */ - context->gReg[0] = sdmac->event_mask[1]; - context->gReg[1] = sdmac->event_mask[0]; - context->gReg[2] = sdmac->per_addr; - context->gReg[6] = sdmac->shp_addr; - context->gReg[7] = sdmac->watermark_level; + if (sdmac->peripheral_type == IMX_DMATYPE_HDMI) { + context->gReg[4] = sdmac->data_addr1; + context->gReg[6] = sdmac->data_addr2; + } else { + context->gReg[0] = sdmac->event_mask[1]; + context->gReg[1] = sdmac->event_mask[0]; + context->gReg[2] = sdmac->per_addr; + context->gReg[6] = sdmac->shp_addr; + context->gReg[7] = sdmac->watermark_level; + } bd0->mode.command = C0_SETDM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; @@ -722,6 +777,7 @@ static int sdma_config_channel(struct sdma_channel *sdmac) { + struct imx_dma_data *data = sdmac->chan.private; int ret; sdma_disable_channel(sdmac); @@ -730,12 +786,19 @@ sdmac->event_mask[1] = 0; sdmac->shp_addr = 0; sdmac->per_addr = 0; + sdmac->data_addr1 = 0; + sdmac->data_addr2 = 0; if (sdmac->event_id0) { if (sdmac->event_id0 >= sdmac->sdma->num_events) return -EINVAL; sdma_event_enable(sdmac, sdmac->event_id0); } + if (sdmac->event_id1) { + if (sdmac->event_id1 >= sdmac->sdma->num_events) + return -EINVAL; + sdma_event_enable(sdmac, sdmac->event_id1); + } switch (sdmac->peripheral_type) { case IMX_DMATYPE_DSP: @@ -755,19 +818,75 @@ (sdmac->peripheral_type != IMX_DMATYPE_DSP)) { /* Handle multiple event channels differently */ if (sdmac->event_id1) { - sdmac->event_mask[1] = BIT(sdmac->event_id1 % 32); - if (sdmac->event_id1 > 31) - __set_bit(31, &sdmac->watermark_level); - sdmac->event_mask[0] = BIT(sdmac->event_id0 % 32); - if (sdmac->event_id0 > 31) - __set_bit(30, &sdmac->watermark_level); + if (sdmac->event_id0 > 31) { + sdmac->event_mask[0] |= 0; + __set_bit(28, &sdmac->watermark_level); + sdmac->event_mask[1] |= + BIT(sdmac->event_id0 % 32); + } else { + sdmac->event_mask[1] |= 0; + sdmac->event_mask[0] |= + BIT(sdmac->event_id0 % 32); + } + if (sdmac->event_id1 > 31) { + sdmac->event_mask[0] |= 0; + __set_bit(29, &sdmac->watermark_level); + sdmac->event_mask[1] |= + BIT(sdmac->event_id1 % 32); + } else { + sdmac->event_mask[1] |= 0; + sdmac->event_mask[0] |= + BIT(sdmac->event_id1 % 32); + } + /* BIT 11: + * 1 : Source on SPBA + * 0 : Source on AIPS + */ + __set_bit(11, &sdmac->watermark_level); + /* BIT 12: + * 1 : Destination on SPBA + * 0 : Destination on AIPS + */ + __set_bit(12, &sdmac->watermark_level); + __set_bit(31, &sdmac->watermark_level); + /* BIT 31: + * 1 : Amount of samples to be transferred is + * unknown and script will keep on transferring + * samples as long as both events are detected + * and script must be manually stopped by the + * application. + * 0 : The amount of samples to be is equal to + * the count field of mode word + * */ + __set_bit(25, &sdmac->watermark_level); + __clear_bit(24, &sdmac->watermark_level); } else { - __set_bit(sdmac->event_id0, sdmac->event_mask); + if (sdmac->event_id0 > 31) { + sdmac->event_mask[0] = 0; + sdmac->event_mask[1] |= + BIT(sdmac->event_id0 % 32); + } else { + sdmac->event_mask[0] |= + BIT(sdmac->event_id0 % 32); + sdmac->event_mask[1] = 0; + } } /* Watermark Level */ sdmac->watermark_level |= sdmac->watermark_level; /* Address */ - sdmac->shp_addr = sdmac->per_address; + if (sdmac->direction == DMA_DEV_TO_DEV) { + sdmac->shp_addr = sdmac->per_address2; + sdmac->per_addr = sdmac->per_address; + } else if (sdmac->direction == DMA_TRANS_NONE) { + if (sdmac->peripheral_type != IMX_DMATYPE_HDMI || + !data->data_addr1 || !data->data_addr2) + return -EINVAL; + sdmac->data_addr1 = *(u32 *)data->data_addr1; + sdmac->data_addr2 = *(u32 *)data->data_addr2; + sdmac->watermark_level = 0; + } else { + sdmac->shp_addr = sdmac->per_address; + } } else { sdmac->watermark_level = 0; /* FIXME: M3_BASE_ADDRESS */ } @@ -799,10 +918,15 @@ int channel = sdmac->channel; int ret = -EBUSY; - sdmac->bd = dma_alloc_coherent(NULL, PAGE_SIZE, &sdmac->bd_phys, GFP_KERNEL); + sdmac->bd_iram = true; + sdmac->bd = gen_pool_dma_alloc(sdma->iram_pool, PAGE_SIZE, &sdmac->bd_phys); if (!sdmac->bd) { - ret = -ENOMEM; - goto out; + sdmac->bd_iram = false; + sdmac->bd = dma_alloc_coherent(NULL, PAGE_SIZE, &sdmac->bd_phys, GFP_KERNEL); + if (!sdmac->bd) { + ret = -ENOMEM; + goto out; + } } memset(sdmac->bd, 0, PAGE_SIZE); @@ -863,7 +987,8 @@ } sdmac->peripheral_type = data->peripheral_type; - sdmac->event_id0 = data->dma_request; + sdmac->event_id0 = data->dma_request0; + sdmac->event_id1 = data->dma_request1; clk_enable(sdmac->sdma->clk_ipg); clk_enable(sdmac->sdma->clk_ahb); @@ -881,6 +1006,9 @@ /* txd.flags will be overwritten in prep funcs */ sdmac->desc.flags = DMA_CTRL_ACK; + /* Set SDMA channel mode to unvalid to avoid misconfig */ + sdmac->mode = SDMA_MODE_INVALID; + return 0; } @@ -901,7 +1029,10 @@ sdma_set_channel_priority(sdmac, 0); - dma_free_coherent(NULL, PAGE_SIZE, sdmac->bd, sdmac->bd_phys); + if (sdmac->bd_iram) + gen_pool_free(sdma->iram_pool, (unsigned long)sdmac->bd, PAGE_SIZE); + else + dma_free_coherent(NULL, PAGE_SIZE, sdmac->bd, sdmac->bd_phys); clk_disable(sdma->clk_ipg); clk_disable(sdma->clk_ahb); @@ -922,7 +1053,7 @@ return NULL; sdmac->status = DMA_IN_PROGRESS; - sdmac->flags = 0; + sdmac->mode = SDMA_MODE_NORMAL; sdmac->buf_tail = 0; @@ -1015,9 +1146,9 @@ { struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_engine *sdma = sdmac->sdma; - int num_periods = buf_len / period_len; int channel = sdmac->channel; int ret, i = 0, buf = 0; + int num_periods; dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel); @@ -1028,12 +1159,33 @@ sdmac->buf_tail = 0; - sdmac->flags |= IMX_DMA_SG_LOOP; sdmac->direction = direction; + + switch (sdmac->direction) { + case DMA_DEV_TO_DEV: + sdmac->mode = SDMA_MODE_P2P; + break; + case DMA_TRANS_NONE: + sdmac->mode = SDMA_MODE_NO_BD; + break; + case DMA_MEM_TO_DEV: + case DMA_DEV_TO_MEM: + sdmac->mode = SDMA_MODE_LOOP; + break; + default: + dev_err(sdma->dev, "invalid SDMA direction %d\n", direction); + return NULL; + } + ret = sdma_load_context(sdmac); if (ret) goto err_out; + if (period_len) + num_periods = buf_len / period_len; + else + return &sdmac->desc; + if (num_periods > NUM_BD) { dev_err(sdma->dev, "SDMA channel %d: maximum number of sg exceeded: %d > %d\n", channel, num_periods, NUM_BD); @@ -1098,18 +1250,31 @@ sdma_disable_channel(sdmac); return 0; case DMA_SLAVE_CONFIG: - if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + if (dmaengine_cfg->direction == DMA_DEV_TO_DEV) { + sdmac->per_address = dmaengine_cfg->src_addr; + sdmac->per_address2 = dmaengine_cfg->dst_addr; + sdmac->watermark_level = 0; + sdmac->watermark_level |= + dmaengine_cfg->src_maxburst; + sdmac->watermark_level |= + dmaengine_cfg->dst_maxburst << 16; + sdmac->word_size = dmaengine_cfg->dst_addr_width; + } else if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { sdmac->per_address = dmaengine_cfg->src_addr; sdmac->watermark_level = dmaengine_cfg->src_maxburst * dmaengine_cfg->src_addr_width; sdmac->word_size = dmaengine_cfg->src_addr_width; - } else { + } else if (dmaengine_cfg->direction == DMA_MEM_TO_DEV) { sdmac->per_address = dmaengine_cfg->dst_addr; sdmac->watermark_level = dmaengine_cfg->dst_maxburst * dmaengine_cfg->dst_addr_width; sdmac->word_size = dmaengine_cfg->dst_addr_width; } sdmac->direction = dmaengine_cfg->direction; + if (dmaengine_cfg->dma_request0) + sdmac->event_id0 = dmaengine_cfg->dma_request0; + if (dmaengine_cfg->dma_request1) + sdmac->event_id1 = dmaengine_cfg->dma_request1; return sdma_config_channel(sdmac); default: return -ENOSYS; @@ -1142,7 +1307,7 @@ sdma_enable_channel(sdma, sdmac->channel); } -#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 34 +#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1 38 static void sdma_add_scripts(struct sdma_engine *sdma, const struct sdma_script_start_addrs *addr) @@ -1214,7 +1379,7 @@ static int __init sdma_init(struct sdma_engine *sdma) { - int i, ret; + int i, ret, ccbsize; dma_addr_t ccb_phys; switch (sdma->devtype) { @@ -1236,14 +1401,17 @@ /* Be sure SDMA has not started yet */ writel_relaxed(0, sdma->regs + SDMA_H_C0PTR); - sdma->channel_control = dma_alloc_coherent(NULL, - MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) + - sizeof(struct sdma_context_data), - &ccb_phys, GFP_KERNEL); + ccbsize = MAX_DMA_CHANNELS * sizeof (struct sdma_channel_control) + + sizeof(struct sdma_context_data); + sdma->channel_control = gen_pool_dma_alloc(sdma->iram_pool, ccbsize, &ccb_phys); if (!sdma->channel_control) { - ret = -ENOMEM; - goto err_dma_alloc; + sdma->channel_control = dma_alloc_coherent(NULL, ccbsize, + &ccb_phys, GFP_KERNEL); + if (!sdma->channel_control) { + ret = -ENOMEM; + goto err_dma_alloc; + } } sdma->context = (void *)sdma->channel_control + @@ -1296,6 +1464,36 @@ return ret; } +static bool sdma_filter_fn(struct dma_chan *chan, void *fn_param) +{ + struct imx_dma_data *data = fn_param; + + if (!imx_dma_is_general_purpose(chan)) + return false; + + chan->private = data; + + return true; +} + +static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct sdma_engine *sdma = ofdma->of_dma_data; + dma_cap_mask_t mask = sdma->dma_device.cap_mask; + struct imx_dma_data data; + + if (dma_spec->args_count != 3) + return NULL; + + data.dma_request0 = dma_spec->args[0]; + data.peripheral_type = dma_spec->args[1]; + data.priority = dma_spec->args[2]; + data.dma_request1 = 0; + + return dma_request_channel(mask, sdma_filter_fn, &data); +} + static int __init sdma_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -1397,6 +1595,11 @@ &sdma->dma_device.channels); } + if (np) + sdma->iram_pool = of_get_named_gen_pool(np, "iram", 0); + if (!sdma->iram_pool) + dev_warn(&pdev->dev, "no iram assigned, using external mem\n"); + ret = sdma_init(sdma); if (ret) goto err_init; @@ -1443,10 +1646,20 @@ goto err_init; } + if (np) { + ret = of_dma_controller_register(np, sdma_xlate, sdma); + if (ret) { + dev_err(&pdev->dev, "failed to register controller\n"); + goto err_register; + } + } + dev_info(sdma->dev, "initialized\n"); return 0; +err_register: + dma_async_device_unregister(&sdma->dma_device); err_init: kfree(sdma->script_addrs); err_alloc: diff -Nur linux-3.10.30/drivers/dma/pxp/Makefile linux-3.10.30-cubox-i/drivers/dma/pxp/Makefile --- linux-3.10.30/drivers/dma/pxp/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/dma/pxp/Makefile 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,2 @@ +obj-$(CONFIG_MXC_PXP_V2) += pxp_dma_v2.o +obj-$(CONFIG_MXC_PXP_CLIENT_DEVICE) += pxp_device.o diff -Nur linux-3.10.30/drivers/dma/pxp/pxp_device.c linux-3.10.30-cubox-i/drivers/dma/pxp/pxp_device.c --- linux-3.10.30/drivers/dma/pxp/pxp_device.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/dma/pxp/pxp_device.c 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static atomic_t open_count = ATOMIC_INIT(0); + +static DEFINE_SPINLOCK(pxp_mem_lock); +static DEFINE_SPINLOCK(pxp_chan_lock); +static LIST_HEAD(head); +static LIST_HEAD(list); +static struct pxp_irq_info irq_info[NR_PXP_VIRT_CHANNEL]; + +struct pxp_chan_handle { + int chan_id; + int hist_status; +}; + +/* To track the allocated memory buffer */ +struct memalloc_record { + struct list_head list; + struct pxp_mem_desc mem; +}; + +struct pxp_chan_info { + int chan_id; + struct dma_chan *dma_chan; + struct list_head list; +}; + +static int pxp_alloc_dma_buffer(struct pxp_mem_desc *mem) +{ + mem->cpu_addr = (unsigned long) + dma_alloc_coherent(NULL, PAGE_ALIGN(mem->size), + (dma_addr_t *) (&mem->phys_addr), + GFP_DMA | GFP_KERNEL); + pr_debug("[ALLOC] mem alloc phys_addr = 0x%x\n", mem->phys_addr); + if ((void *)(mem->cpu_addr) == NULL) { + printk(KERN_ERR "Physical memory allocation error!\n"); + return -1; + } + return 0; +} + +static void pxp_free_dma_buffer(struct pxp_mem_desc *mem) +{ + if (mem->cpu_addr != 0) { + dma_free_coherent(0, PAGE_ALIGN(mem->size), + (void *)mem->cpu_addr, mem->phys_addr); + } +} + +static int pxp_free_buffers(void) +{ + struct memalloc_record *rec, *n; + struct pxp_mem_desc mem; + + list_for_each_entry_safe(rec, n, &head, list) { + mem = rec->mem; + if (mem.cpu_addr != 0) { + pxp_free_dma_buffer(&mem); + pr_debug("[FREE] freed paddr=0x%08X\n", mem.phys_addr); + /* delete from list */ + list_del(&rec->list); + kfree(rec); + } + } + + return 0; +} + +/* Callback function triggered after PxP receives an EOF interrupt */ +static void pxp_dma_done(void *arg) +{ + struct pxp_tx_desc *tx_desc = to_tx_desc(arg); + struct dma_chan *chan = tx_desc->txd.chan; + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + int chan_id = pxp_chan->dma_chan.chan_id; + + pr_debug("DMA Done ISR, chan_id %d\n", chan_id); + + irq_info[chan_id].irq_pending++; + irq_info[chan_id].hist_status = tx_desc->hist_status; + + wake_up_interruptible(&(irq_info[chan_id].waitq)); +} + +static int pxp_ioc_config_chan(unsigned long arg) +{ + struct scatterlist sg[3]; + struct pxp_tx_desc *desc; + struct dma_async_tx_descriptor *txd; + struct pxp_chan_info *info; + struct pxp_config_data pxp_conf; + dma_cookie_t cookie; + int chan_id; + int i, length, ret; + + ret = copy_from_user(&pxp_conf, + (struct pxp_config_data *)arg, + sizeof(struct pxp_config_data)); + if (ret) + return -EFAULT; + + chan_id = pxp_conf.chan_id; + if (chan_id < 0 || chan_id >= NR_PXP_VIRT_CHANNEL) + return -ENODEV; + + init_waitqueue_head(&(irq_info[chan_id].waitq)); + + /* find the channel */ + spin_lock(&pxp_chan_lock); + list_for_each_entry(info, &list, list) { + if (info->dma_chan->chan_id == chan_id) + break; + } + spin_unlock(&pxp_chan_lock); + + sg_init_table(sg, 3); + + txd = + info->dma_chan->device->device_prep_slave_sg(info->dma_chan, + sg, 3, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT, + NULL); + if (!txd) { + pr_err("Error preparing a DMA transaction descriptor.\n"); + return -EIO; + } + + txd->callback_param = txd; + txd->callback = pxp_dma_done; + + desc = to_tx_desc(txd); + + length = desc->len; + for (i = 0; i < length; i++) { + if (i == 0) { /* S0 */ + memcpy(&desc->proc_data, + &pxp_conf.proc_data, + sizeof(struct pxp_proc_data)); + memcpy(&desc->layer_param.s0_param, + &pxp_conf.s0_param, + sizeof(struct pxp_layer_param)); + } else if (i == 1) { /* Output */ + memcpy(&desc->layer_param.out_param, + &pxp_conf.out_param, + sizeof(struct pxp_layer_param)); + } else { + /* OverLay */ + memcpy(&desc->layer_param.ol_param, + &pxp_conf.ol_param, + sizeof(struct pxp_layer_param)); + } + + desc = desc->next; + } + + cookie = txd->tx_submit(txd); + if (cookie < 0) { + pr_err("Error tx_submit\n"); + return -EIO; + } + + return 0; +} + +static int pxp_device_open(struct inode *inode, struct file *filp) +{ + atomic_inc(&open_count); + + return 0; +} + +static int pxp_device_release(struct inode *inode, struct file *filp) +{ + if (atomic_dec_and_test(&open_count)) + pxp_free_buffers(); + + return 0; +} + +static int pxp_device_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct memalloc_record *rec, *n; + int request_size, found; + + request_size = vma->vm_end - vma->vm_start; + found = 0; + + pr_debug("start=0x%x, pgoff=0x%x, size=0x%x\n", + (unsigned int)(vma->vm_start), (unsigned int)(vma->vm_pgoff), + request_size); + + spin_lock(&pxp_mem_lock); + list_for_each_entry_safe(rec, n, &head, list) { + if (rec->mem.phys_addr == (vma->vm_pgoff << PAGE_SHIFT) && + (rec->mem.size <= request_size)) { + found = 1; + break; + } + } + spin_unlock(&pxp_mem_lock); + + if (found == 0) + return -ENOMEM; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + request_size, vma->vm_page_prot) ? -EAGAIN : 0; +} + +static bool chan_filter(struct dma_chan *chan, void *arg) +{ + if (imx_dma_is_pxp(chan)) + return true; + else + return false; +} + +static long pxp_device_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case PXP_IOC_GET_CHAN: + { + struct pxp_chan_info *info; + dma_cap_mask_t mask; + + pr_debug("drv: PXP_IOC_GET_CHAN Line %d\n", __LINE__); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + pr_err("%d: alloc err\n", __LINE__); + return -ENOMEM; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + info->dma_chan = + dma_request_channel(mask, chan_filter, NULL); + if (!info->dma_chan) { + pr_err("Unsccessfully received channel!\n"); + kfree(info); + return -EBUSY; + } + pr_debug("Successfully received channel." + "chan_id %d\n", info->dma_chan->chan_id); + + spin_lock(&pxp_chan_lock); + list_add_tail(&info->list, &list); + spin_unlock(&pxp_chan_lock); + + if (put_user + (info->dma_chan->chan_id, (u32 __user *) arg)) + return -EFAULT; + + break; + } + case PXP_IOC_PUT_CHAN: + { + int chan_id; + struct pxp_chan_info *info; + + if (get_user(chan_id, (u32 __user *) arg)) + return -EFAULT; + + if (chan_id < 0 || chan_id >= NR_PXP_VIRT_CHANNEL) + return -ENODEV; + + spin_lock(&pxp_chan_lock); + list_for_each_entry(info, &list, list) { + if (info->dma_chan->chan_id == chan_id) + break; + } + spin_unlock(&pxp_chan_lock); + + pr_debug("%d release chan_id %d\n", __LINE__, + info->dma_chan->chan_id); + /* REVISIT */ + dma_release_channel(info->dma_chan); + spin_lock(&pxp_chan_lock); + list_del_init(&info->list); + spin_unlock(&pxp_chan_lock); + kfree(info); + + break; + } + case PXP_IOC_CONFIG_CHAN: + { + + int ret; + + ret = pxp_ioc_config_chan(arg); + if (ret) + return ret; + + break; + } + case PXP_IOC_START_CHAN: + { + struct pxp_chan_info *info; + int chan_id; + + if (get_user(chan_id, (u32 __user *) arg)) + return -EFAULT; + + /* find the channel */ + spin_lock(&pxp_chan_lock); + list_for_each_entry(info, &list, list) { + if (info->dma_chan->chan_id == chan_id) + break; + } + spin_unlock(&pxp_chan_lock); + + dma_async_issue_pending(info->dma_chan); + + break; + } + case PXP_IOC_GET_PHYMEM: + { + struct memalloc_record *rec; + + rec = kzalloc(sizeof(*rec), GFP_KERNEL); + if (!rec) + return -ENOMEM; + + ret = copy_from_user(&(rec->mem), + (struct pxp_mem_desc *)arg, + sizeof(struct pxp_mem_desc)); + if (ret) { + kfree(rec); + return -EFAULT; + } + + pr_debug("[ALLOC] mem alloc size = 0x%x\n", + rec->mem.size); + + ret = pxp_alloc_dma_buffer(&(rec->mem)); + if (ret == -1) { + kfree(rec); + printk(KERN_ERR + "Physical memory allocation error!\n"); + break; + } + ret = copy_to_user((void __user *)arg, &(rec->mem), + sizeof(struct pxp_mem_desc)); + if (ret) { + kfree(rec); + ret = -EFAULT; + break; + } + + spin_lock(&pxp_mem_lock); + list_add(&rec->list, &head); + spin_unlock(&pxp_mem_lock); + + break; + } + case PXP_IOC_PUT_PHYMEM: + { + struct memalloc_record *rec, *n; + struct pxp_mem_desc pxp_mem; + + ret = copy_from_user(&pxp_mem, + (struct pxp_mem_desc *)arg, + sizeof(struct pxp_mem_desc)); + if (ret) + return -EACCES; + + pr_debug("[FREE] mem freed cpu_addr = 0x%x\n", + pxp_mem.cpu_addr); + if ((void *)pxp_mem.cpu_addr != NULL) + pxp_free_dma_buffer(&pxp_mem); + + spin_lock(&pxp_mem_lock); + list_for_each_entry_safe(rec, n, &head, list) { + if (rec->mem.cpu_addr == pxp_mem.cpu_addr) { + /* delete from list */ + list_del(&rec->list); + kfree(rec); + break; + } + } + spin_unlock(&pxp_mem_lock); + + break; + } + case PXP_IOC_WAIT4CMPLT: + { + struct pxp_chan_handle chan_handle; + int ret, chan_id; + + ret = copy_from_user(&chan_handle, + (struct pxp_chan_handle *)arg, + sizeof(struct pxp_chan_handle)); + if (ret) + return -EFAULT; + + chan_id = chan_handle.chan_id; + if (chan_id < 0 || chan_id >= NR_PXP_VIRT_CHANNEL) + return -ENODEV; + + ret = wait_event_interruptible + (irq_info[chan_id].waitq, + (irq_info[chan_id].irq_pending != 0)); + if (ret < 0) { + printk(KERN_WARNING + "pxp interrupt received.\n"); + return -ERESTARTSYS; + } else + irq_info[chan_id].irq_pending--; + + chan_handle.hist_status = irq_info[chan_id].hist_status; + ret = copy_to_user((struct pxp_chan_handle *)arg, + &chan_handle, + sizeof(struct pxp_chan_handle)); + if (ret) + return -EFAULT; + break; + } + default: + break; + } + + return 0; +} + +static const struct file_operations pxp_device_fops = { + .open = pxp_device_open, + .release = pxp_device_release, + .unlocked_ioctl = pxp_device_ioctl, + .mmap = pxp_device_mmap, +}; + +static struct miscdevice pxp_device_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "pxp_device", + .fops = &pxp_device_fops, +}; + +int register_pxp_device(void) +{ + int ret; + + ret = misc_register(&pxp_device_miscdev); + if (ret) + return ret; + + pr_debug("PxP_Device registered Successfully\n"); + return 0; +} + +void unregister_pxp_device(void) +{ + misc_deregister(&pxp_device_miscdev); +} diff -Nur linux-3.10.30/drivers/dma/pxp/pxp_dma_v2.c linux-3.10.30-cubox-i/drivers/dma/pxp/pxp_dma_v2.c --- linux-3.10.30/drivers/dma/pxp/pxp_dma_v2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/dma/pxp/pxp_dma_v2.c 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,1936 @@ +/* + * Copyright (C) 2010-2013 Freescale Semiconductor, 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 + * + */ +/* + * Based on STMP378X PxP driver + * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "regs-pxp_v2.h" + +#define PXP_DOWNSCALE_THRESHOLD 0x4000 + +static LIST_HEAD(head); +static int timeout_in_ms = 600; +static unsigned int block_size; +struct mutex hard_lock; + +struct pxp_dma { + struct dma_device dma; +}; + +struct pxps { + struct platform_device *pdev; + struct clk *clk; + void __iomem *base; + int irq; /* PXP IRQ to the CPU */ + + spinlock_t lock; + struct mutex clk_mutex; + int clk_stat; +#define CLK_STAT_OFF 0 +#define CLK_STAT_ON 1 + int pxp_ongoing; + int lut_state; + + struct device *dev; + struct pxp_dma pxp_dma; + struct pxp_channel channel[NR_PXP_VIRT_CHANNEL]; + struct work_struct work; + + /* describes most recent processing configuration */ + struct pxp_config_data pxp_conf_state; + + /* to turn clock off when pxp is inactive */ + struct timer_list clk_timer; +}; + +#define to_pxp_dma(d) container_of(d, struct pxp_dma, dma) +#define to_tx_desc(tx) container_of(tx, struct pxp_tx_desc, txd) +#define to_pxp_channel(d) container_of(d, struct pxp_channel, dma_chan) +#define to_pxp(id) container_of(id, struct pxps, pxp_dma) + +#define PXP_DEF_BUFS 2 +#define PXP_MIN_PIX 8 + +static uint32_t pxp_s0_formats[] = { + PXP_PIX_FMT_RGB32, + PXP_PIX_FMT_RGB565, + PXP_PIX_FMT_RGB555, + PXP_PIX_FMT_YUV420P, + PXP_PIX_FMT_YUV422P, +}; + +/* + * PXP common functions + */ +static void dump_pxp_reg(struct pxps *pxp) +{ + dev_dbg(pxp->dev, "PXP_CTRL 0x%x", + __raw_readl(pxp->base + HW_PXP_CTRL)); + dev_dbg(pxp->dev, "PXP_STAT 0x%x", + __raw_readl(pxp->base + HW_PXP_STAT)); + dev_dbg(pxp->dev, "PXP_OUT_CTRL 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_CTRL)); + dev_dbg(pxp->dev, "PXP_OUT_BUF 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_BUF)); + dev_dbg(pxp->dev, "PXP_OUT_BUF2 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_BUF2)); + dev_dbg(pxp->dev, "PXP_OUT_PITCH 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_PITCH)); + dev_dbg(pxp->dev, "PXP_OUT_LRC 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_LRC)); + dev_dbg(pxp->dev, "PXP_OUT_PS_ULC 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_PS_ULC)); + dev_dbg(pxp->dev, "PXP_OUT_PS_LRC 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_PS_LRC)); + dev_dbg(pxp->dev, "PXP_OUT_AS_ULC 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_AS_ULC)); + dev_dbg(pxp->dev, "PXP_OUT_AS_LRC 0x%x", + __raw_readl(pxp->base + HW_PXP_OUT_AS_LRC)); + dev_dbg(pxp->dev, "PXP_PS_CTRL 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_CTRL)); + dev_dbg(pxp->dev, "PXP_PS_BUF 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_BUF)); + dev_dbg(pxp->dev, "PXP_PS_UBUF 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_UBUF)); + dev_dbg(pxp->dev, "PXP_PS_VBUF 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_VBUF)); + dev_dbg(pxp->dev, "PXP_PS_PITCH 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_PITCH)); + dev_dbg(pxp->dev, "PXP_PS_BACKGROUND 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_BACKGROUND)); + dev_dbg(pxp->dev, "PXP_PS_SCALE 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_SCALE)); + dev_dbg(pxp->dev, "PXP_PS_OFFSET 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_OFFSET)); + dev_dbg(pxp->dev, "PXP_PS_CLRKEYLOW 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_CLRKEYLOW)); + dev_dbg(pxp->dev, "PXP_PS_CLRKEYHIGH 0x%x", + __raw_readl(pxp->base + HW_PXP_PS_CLRKEYHIGH)); + dev_dbg(pxp->dev, "PXP_AS_CTRL 0x%x", + __raw_readl(pxp->base + HW_PXP_AS_CTRL)); + dev_dbg(pxp->dev, "PXP_AS_BUF 0x%x", + __raw_readl(pxp->base + HW_PXP_AS_BUF)); + dev_dbg(pxp->dev, "PXP_AS_PITCH 0x%x", + __raw_readl(pxp->base + HW_PXP_AS_PITCH)); + dev_dbg(pxp->dev, "PXP_AS_CLRKEYLOW 0x%x", + __raw_readl(pxp->base + HW_PXP_AS_CLRKEYLOW)); + dev_dbg(pxp->dev, "PXP_AS_CLRKEYHIGH 0x%x", + __raw_readl(pxp->base + HW_PXP_AS_CLRKEYHIGH)); + dev_dbg(pxp->dev, "PXP_CSC1_COEF0 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC1_COEF0)); + dev_dbg(pxp->dev, "PXP_CSC1_COEF1 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC1_COEF1)); + dev_dbg(pxp->dev, "PXP_CSC1_COEF2 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC1_COEF2)); + dev_dbg(pxp->dev, "PXP_CSC2_CTRL 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC2_CTRL)); + dev_dbg(pxp->dev, "PXP_CSC2_COEF0 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC2_COEF0)); + dev_dbg(pxp->dev, "PXP_CSC2_COEF1 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC2_COEF1)); + dev_dbg(pxp->dev, "PXP_CSC2_COEF2 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC2_COEF2)); + dev_dbg(pxp->dev, "PXP_CSC2_COEF3 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC2_COEF3)); + dev_dbg(pxp->dev, "PXP_CSC2_COEF4 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC2_COEF4)); + dev_dbg(pxp->dev, "PXP_CSC2_COEF5 0x%x", + __raw_readl(pxp->base + HW_PXP_CSC2_COEF5)); + dev_dbg(pxp->dev, "PXP_LUT_CTRL 0x%x", + __raw_readl(pxp->base + HW_PXP_LUT_CTRL)); + dev_dbg(pxp->dev, "PXP_LUT_ADDR 0x%x", + __raw_readl(pxp->base + HW_PXP_LUT_ADDR)); + dev_dbg(pxp->dev, "PXP_LUT_DATA 0x%x", + __raw_readl(pxp->base + HW_PXP_LUT_DATA)); + dev_dbg(pxp->dev, "PXP_LUT_EXTMEM 0x%x", + __raw_readl(pxp->base + HW_PXP_LUT_EXTMEM)); + dev_dbg(pxp->dev, "PXP_CFA 0x%x", + __raw_readl(pxp->base + HW_PXP_CFA)); + dev_dbg(pxp->dev, "PXP_HIST_CTRL 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST_CTRL)); + dev_dbg(pxp->dev, "PXP_HIST2_PARAM 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST2_PARAM)); + dev_dbg(pxp->dev, "PXP_HIST4_PARAM 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST4_PARAM)); + dev_dbg(pxp->dev, "PXP_HIST8_PARAM0 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST8_PARAM0)); + dev_dbg(pxp->dev, "PXP_HIST8_PARAM1 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST8_PARAM1)); + dev_dbg(pxp->dev, "PXP_HIST16_PARAM0 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST16_PARAM0)); + dev_dbg(pxp->dev, "PXP_HIST16_PARAM1 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST16_PARAM1)); + dev_dbg(pxp->dev, "PXP_HIST16_PARAM2 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST16_PARAM2)); + dev_dbg(pxp->dev, "PXP_HIST16_PARAM3 0x%x", + __raw_readl(pxp->base + HW_PXP_HIST16_PARAM3)); + dev_dbg(pxp->dev, "PXP_POWER 0x%x", + __raw_readl(pxp->base + HW_PXP_POWER)); + dev_dbg(pxp->dev, "PXP_NEXT 0x%x", + __raw_readl(pxp->base + HW_PXP_NEXT)); + dev_dbg(pxp->dev, "PXP_DEBUGCTRL 0x%x", + __raw_readl(pxp->base + HW_PXP_DEBUGCTRL)); + dev_dbg(pxp->dev, "PXP_DEBUG 0x%x", + __raw_readl(pxp->base + HW_PXP_DEBUG)); + dev_dbg(pxp->dev, "PXP_VERSION 0x%x", + __raw_readl(pxp->base + HW_PXP_VERSION)); +} + +static bool is_yuv(u32 pix_fmt) +{ + if ((pix_fmt == PXP_PIX_FMT_YUYV) | + (pix_fmt == PXP_PIX_FMT_UYVY) | + (pix_fmt == PXP_PIX_FMT_YVYU) | + (pix_fmt == PXP_PIX_FMT_VYUY) | + (pix_fmt == PXP_PIX_FMT_Y41P) | + (pix_fmt == PXP_PIX_FMT_YUV444) | + (pix_fmt == PXP_PIX_FMT_NV12) | + (pix_fmt == PXP_PIX_FMT_NV16) | + (pix_fmt == PXP_PIX_FMT_NV61) | + (pix_fmt == PXP_PIX_FMT_GREY) | + (pix_fmt == PXP_PIX_FMT_GY04) | + (pix_fmt == PXP_PIX_FMT_YVU410P) | + (pix_fmt == PXP_PIX_FMT_YUV410P) | + (pix_fmt == PXP_PIX_FMT_YVU420P) | + (pix_fmt == PXP_PIX_FMT_YUV420P) | + (pix_fmt == PXP_PIX_FMT_YUV420P2) | + (pix_fmt == PXP_PIX_FMT_YVU422P) | + (pix_fmt == PXP_PIX_FMT_YUV422P)) { + return true; + } else { + return false; + } +} + +static void pxp_set_ctrl(struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_proc_data *proc_data = &pxp_conf->proc_data; + u32 ctrl; + u32 fmt_ctrl; + int need_swap = 0; /* to support YUYV and YVYU formats */ + + /* Configure S0 input format */ + switch (pxp_conf->s0_param.pixel_fmt) { + case PXP_PIX_FMT_RGB32: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__RGB888; + break; + case PXP_PIX_FMT_RGB565: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__RGB565; + break; + case PXP_PIX_FMT_RGB555: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__RGB555; + break; + case PXP_PIX_FMT_YUV420P: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV420; + break; + case PXP_PIX_FMT_YVU420P: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV420; + break; + case PXP_PIX_FMT_GREY: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__Y8; + break; + case PXP_PIX_FMT_GY04: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__Y4; + break; + case PXP_PIX_FMT_YUV422P: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV422; + break; + case PXP_PIX_FMT_UYVY: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__UYVY1P422; + break; + case PXP_PIX_FMT_YUYV: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__UYVY1P422; + need_swap = 1; + break; + case PXP_PIX_FMT_VYUY: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__VYUY1P422; + break; + case PXP_PIX_FMT_YVYU: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__VYUY1P422; + need_swap = 1; + break; + case PXP_PIX_FMT_NV12: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV2P420; + break; + case PXP_PIX_FMT_NV21: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YVU2P420; + break; + case PXP_PIX_FMT_NV16: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YUV2P422; + break; + case PXP_PIX_FMT_NV61: + fmt_ctrl = BV_PXP_PS_CTRL_FORMAT__YVU2P422; + break; + default: + fmt_ctrl = 0; + } + + ctrl = BF_PXP_PS_CTRL_FORMAT(fmt_ctrl) | BF_PXP_PS_CTRL_SWAP(need_swap); + __raw_writel(ctrl, pxp->base + HW_PXP_PS_CTRL_SET); + + /* Configure output format based on out_channel format */ + switch (pxp_conf->out_param.pixel_fmt) { + case PXP_PIX_FMT_RGB32: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__RGB888; + break; + case PXP_PIX_FMT_BGRA32: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__ARGB8888; + break; + case PXP_PIX_FMT_RGB24: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__RGB888P; + break; + case PXP_PIX_FMT_RGB565: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__RGB565; + break; + case PXP_PIX_FMT_RGB555: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__RGB555; + break; + case PXP_PIX_FMT_GREY: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__Y8; + break; + case PXP_PIX_FMT_GY04: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__Y4; + break; + case PXP_PIX_FMT_UYVY: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__UYVY1P422; + break; + case PXP_PIX_FMT_VYUY: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__VYUY1P422; + break; + case PXP_PIX_FMT_NV12: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__YUV2P420; + break; + case PXP_PIX_FMT_NV21: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__YVU2P420; + break; + case PXP_PIX_FMT_NV16: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__YUV2P422; + break; + case PXP_PIX_FMT_NV61: + fmt_ctrl = BV_PXP_OUT_CTRL_FORMAT__YVU2P422; + break; + default: + fmt_ctrl = 0; + } + + ctrl = BF_PXP_OUT_CTRL_FORMAT(fmt_ctrl); + __raw_writel(ctrl, pxp->base + HW_PXP_OUT_CTRL); + + ctrl = 0; + if (proc_data->scaling) + ; + if (proc_data->vflip) + ctrl |= BM_PXP_CTRL_VFLIP; + if (proc_data->hflip) + ctrl |= BM_PXP_CTRL_HFLIP; + if (proc_data->rotate) { + ctrl |= BF_PXP_CTRL_ROTATE(proc_data->rotate / 90); + if (proc_data->rot_pos) + ctrl |= BM_PXP_CTRL_ROT_POS; + } + + /* In default, the block size is set to 8x8 + * But block size can be set to 16x16 due to + * blocksize variable modification + */ + ctrl |= block_size << 23; + + __raw_writel(ctrl, pxp->base + HW_PXP_CTRL); +} + +static int pxp_start(struct pxps *pxp) +{ + __raw_writel(BM_PXP_CTRL_IRQ_ENABLE, pxp->base + HW_PXP_CTRL_SET); + __raw_writel(BM_PXP_CTRL_ENABLE, pxp->base + HW_PXP_CTRL_SET); + dump_pxp_reg(pxp); + + return 0; +} + +static void pxp_set_outbuf(struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_layer_param *out_params = &pxp_conf->out_param; + + __raw_writel(out_params->paddr, pxp->base + HW_PXP_OUT_BUF); + + __raw_writel(BF_PXP_OUT_LRC_X(out_params->width - 1) | + BF_PXP_OUT_LRC_Y(out_params->height - 1), + pxp->base + HW_PXP_OUT_LRC); + + if (out_params->pixel_fmt == PXP_PIX_FMT_RGB24) { + __raw_writel(out_params->stride * 3, + pxp->base + HW_PXP_OUT_PITCH); + } else if (out_params->pixel_fmt == PXP_PIX_FMT_BGRA32 || + out_params->pixel_fmt == PXP_PIX_FMT_RGB32) { + __raw_writel(out_params->stride << 2, + pxp->base + HW_PXP_OUT_PITCH); + } else if (out_params->pixel_fmt == PXP_PIX_FMT_RGB565) { + __raw_writel(out_params->stride << 1, + pxp->base + HW_PXP_OUT_PITCH); + } else if (out_params->pixel_fmt == PXP_PIX_FMT_UYVY || + (out_params->pixel_fmt == PXP_PIX_FMT_VYUY)) { + __raw_writel(out_params->stride << 1, + pxp->base + HW_PXP_OUT_PITCH); + } else if (out_params->pixel_fmt == PXP_PIX_FMT_GREY || + out_params->pixel_fmt == PXP_PIX_FMT_NV12 || + out_params->pixel_fmt == PXP_PIX_FMT_NV21 || + out_params->pixel_fmt == PXP_PIX_FMT_NV16 || + out_params->pixel_fmt == PXP_PIX_FMT_NV61) { + __raw_writel(out_params->stride, + pxp->base + HW_PXP_OUT_PITCH); + } else if (out_params->pixel_fmt == PXP_PIX_FMT_GY04) { + __raw_writel(out_params->stride >> 1, + pxp->base + HW_PXP_OUT_PITCH); + } else { + __raw_writel(0, pxp->base + HW_PXP_OUT_PITCH); + } + + /* set global alpha if necessary */ + if (out_params->global_alpha_enable) { + __raw_writel(out_params->global_alpha << 24, + pxp->base + HW_PXP_OUT_CTRL_SET); + __raw_writel(BM_PXP_OUT_CTRL_ALPHA_OUTPUT, + pxp->base + HW_PXP_OUT_CTRL_SET); + } +} + +static void pxp_set_s0colorkey(struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_layer_param *s0_params = &pxp_conf->s0_param; + + /* Low and high are set equal. V4L does not allow a chromakey range */ + if (s0_params->color_key_enable == 0 || s0_params->color_key == -1) { + /* disable color key */ + __raw_writel(0xFFFFFF, pxp->base + HW_PXP_PS_CLRKEYLOW); + __raw_writel(0, pxp->base + HW_PXP_PS_CLRKEYHIGH); + } else { + __raw_writel(s0_params->color_key, + pxp->base + HW_PXP_PS_CLRKEYLOW); + __raw_writel(s0_params->color_key, + pxp->base + HW_PXP_PS_CLRKEYHIGH); + } +} + +static void pxp_set_olcolorkey(int layer_no, struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_layer_param *ol_params = &pxp_conf->ol_param[layer_no]; + + /* Low and high are set equal. V4L does not allow a chromakey range */ + if (ol_params->color_key_enable != 0 && ol_params->color_key != -1) { + __raw_writel(ol_params->color_key, + pxp->base + HW_PXP_AS_CLRKEYLOW); + __raw_writel(ol_params->color_key, + pxp->base + HW_PXP_AS_CLRKEYHIGH); + } else { + /* disable color key */ + __raw_writel(0xFFFFFF, pxp->base + HW_PXP_AS_CLRKEYLOW); + __raw_writel(0, pxp->base + HW_PXP_AS_CLRKEYHIGH); + } +} + +static void pxp_set_oln(int layer_no, struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_layer_param *olparams_data = &pxp_conf->ol_param[layer_no]; + dma_addr_t phys_addr = olparams_data->paddr; + u32 pitch = olparams_data->stride ? olparams_data->stride : + olparams_data->width; + + __raw_writel(phys_addr, pxp->base + HW_PXP_AS_BUF); + + /* Fixme */ + if (olparams_data->width == 0 && olparams_data->height == 0) { + __raw_writel(0xffffffff, pxp->base + HW_PXP_OUT_AS_ULC); + __raw_writel(0x0, pxp->base + HW_PXP_OUT_AS_LRC); + } else { + __raw_writel(0x0, pxp->base + HW_PXP_OUT_AS_ULC); + if (pxp_conf->proc_data.rotate == 90 || + pxp_conf->proc_data.rotate == 270) { + if (pxp_conf->proc_data.rot_pos == 1) { + __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->height - 1) | + BF_PXP_OUT_AS_LRC_Y(olparams_data->width - 1), + pxp->base + HW_PXP_OUT_AS_LRC); + } else { + __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->width - 1) | + BF_PXP_OUT_AS_LRC_Y(olparams_data->height - 1), + pxp->base + HW_PXP_OUT_AS_LRC); + } + } else { + __raw_writel(BF_PXP_OUT_AS_LRC_X(olparams_data->width - 1) | + BF_PXP_OUT_AS_LRC_Y(olparams_data->height - 1), + pxp->base + HW_PXP_OUT_AS_LRC); + } + } + + if ((olparams_data->pixel_fmt == PXP_PIX_FMT_BGRA32) | + (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB32)) { + __raw_writel(pitch << 2, + pxp->base + HW_PXP_AS_PITCH); + } else if (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB565) { + __raw_writel(pitch << 1, + pxp->base + HW_PXP_AS_PITCH); + } else { + __raw_writel(0, pxp->base + HW_PXP_AS_PITCH); + } +} + +static void pxp_set_olparam(int layer_no, struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_layer_param *olparams_data = &pxp_conf->ol_param[layer_no]; + u32 olparam; + + olparam = BF_PXP_AS_CTRL_ALPHA(olparams_data->global_alpha); + if (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB32) { + olparam |= + BF_PXP_AS_CTRL_FORMAT(BV_PXP_AS_CTRL_FORMAT__RGB888); + } else if (olparams_data->pixel_fmt == PXP_PIX_FMT_BGRA32) { + olparam |= + BF_PXP_AS_CTRL_FORMAT(BV_PXP_AS_CTRL_FORMAT__ARGB8888); + if (!olparams_data->combine_enable) { + olparam |= + BF_PXP_AS_CTRL_ALPHA_CTRL + (BV_PXP_AS_CTRL_ALPHA_CTRL__ROPs); + olparam |= 0x3 << 16; + } + } else if (olparams_data->pixel_fmt == PXP_PIX_FMT_RGB565) { + olparam |= + BF_PXP_AS_CTRL_FORMAT(BV_PXP_AS_CTRL_FORMAT__RGB565); + } + if (olparams_data->global_alpha_enable) { + if (olparams_data->global_override) { + olparam |= + BF_PXP_AS_CTRL_ALPHA_CTRL + (BV_PXP_AS_CTRL_ALPHA_CTRL__Override); + } else { + olparam |= + BF_PXP_AS_CTRL_ALPHA_CTRL + (BV_PXP_AS_CTRL_ALPHA_CTRL__Multiply); + } + if (olparams_data->alpha_invert) + olparam |= BM_PXP_AS_CTRL_ALPHA_INVERT; + } + if (olparams_data->color_key_enable) + olparam |= BM_PXP_AS_CTRL_ENABLE_COLORKEY; + + __raw_writel(olparam, pxp->base + HW_PXP_AS_CTRL); +} + +static void pxp_set_s0param(struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_proc_data *proc_data = &pxp_conf->proc_data; + u32 s0param; + + /* contains the coordinate for the PS in the OUTPUT buffer. */ + if ((pxp_conf->s0_param).width == 0 && + (pxp_conf->s0_param).height == 0) { + __raw_writel(0xffffffff, pxp->base + HW_PXP_OUT_PS_ULC); + __raw_writel(0x0, pxp->base + HW_PXP_OUT_PS_LRC); + } else { + s0param = BF_PXP_OUT_PS_ULC_X(proc_data->drect.left); + s0param |= BF_PXP_OUT_PS_ULC_Y(proc_data->drect.top); + __raw_writel(s0param, pxp->base + HW_PXP_OUT_PS_ULC); + s0param = BF_PXP_OUT_PS_LRC_X(proc_data->drect.left + + proc_data->drect.width - 1); + s0param |= BF_PXP_OUT_PS_LRC_Y(proc_data->drect.top + + proc_data->drect.height - 1); + __raw_writel(s0param, pxp->base + HW_PXP_OUT_PS_LRC); + } +} + +/* crop behavior is re-designed in h/w. */ +static void pxp_set_s0crop(struct pxps *pxp) +{ + /* + * place-holder, it's implemented in other functions in this driver. + * Refer to "Clipping source images" section in RM for detail. + */ +} + +static int pxp_set_scaling(struct pxps *pxp) +{ + int ret = 0; + u32 xscale, yscale, s0scale; + u32 decx, decy, xdec = 0, ydec = 0; + struct pxp_proc_data *proc_data = &pxp->pxp_conf_state.proc_data; + + if (((proc_data->srect.width == proc_data->drect.width) && + (proc_data->srect.height == proc_data->drect.height)) || + ((proc_data->srect.width == 0) && (proc_data->srect.height == 0))) { + proc_data->scaling = 0; + __raw_writel(0x10001000, pxp->base + HW_PXP_PS_SCALE); + __raw_writel(0, pxp->base + HW_PXP_PS_CTRL); + goto out; + } + + proc_data->scaling = 1; + decx = proc_data->srect.width / proc_data->drect.width; + decy = proc_data->srect.height / proc_data->drect.height; + if (decx > 0) { + if (decx >= 2 && decx < 4) { + decx = 2; + xdec = 1; + } else if (decx >= 4 && decx < 8) { + decx = 4; + xdec = 2; + } else if (decx >= 8) { + decx = 8; + xdec = 3; + } + xscale = proc_data->srect.width * 0x1000 / + (proc_data->drect.width * decx); + } else + xscale = proc_data->srect.width * 0x1000 / + proc_data->drect.width; + if (decy > 0) { + if (decy >= 2 && decy < 4) { + decy = 2; + ydec = 1; + } else if (decy >= 4 && decy < 8) { + decy = 4; + ydec = 2; + } else if (decy >= 8) { + decy = 8; + ydec = 3; + } + yscale = proc_data->srect.height * 0x1000 / + (proc_data->drect.height * decy); + } else + yscale = proc_data->srect.height * 0x1000 / + proc_data->drect.height; + + __raw_writel((xdec << 10) | (ydec << 8), pxp->base + HW_PXP_PS_CTRL); + + if (xscale > PXP_DOWNSCALE_THRESHOLD) + xscale = PXP_DOWNSCALE_THRESHOLD; + if (yscale > PXP_DOWNSCALE_THRESHOLD) + yscale = PXP_DOWNSCALE_THRESHOLD; + s0scale = BF_PXP_PS_SCALE_YSCALE(yscale) | + BF_PXP_PS_SCALE_XSCALE(xscale); + __raw_writel(s0scale, pxp->base + HW_PXP_PS_SCALE); + +out: + pxp_set_ctrl(pxp); + + return ret; +} + +static void pxp_set_bg(struct pxps *pxp) +{ + __raw_writel(pxp->pxp_conf_state.proc_data.bgcolor, + pxp->base + HW_PXP_PS_BACKGROUND); +} + +static void pxp_set_lut(struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + int lut_op = pxp_conf->proc_data.lut_transform; + u32 reg_val; + int i; + bool use_cmap = (lut_op & PXP_LUT_USE_CMAP) ? true : false; + u8 *cmap = pxp_conf->proc_data.lut_map; + u32 entry_src; + u32 pix_val; + u8 entry[4]; + + /* + * If LUT already configured as needed, return... + * Unless CMAP is needed and it has been updated. + */ + if ((pxp->lut_state == lut_op) && + !(use_cmap && pxp_conf->proc_data.lut_map_updated)) + return; + + if (lut_op == PXP_LUT_NONE) { + __raw_writel(BM_PXP_LUT_CTRL_BYPASS, + pxp->base + HW_PXP_LUT_CTRL); + } else if (((lut_op & PXP_LUT_INVERT) != 0) + && ((lut_op & PXP_LUT_BLACK_WHITE) != 0)) { + /* Fill out LUT table with inverted monochromized values */ + + /* clear bypass bit, set lookup mode & out mode */ + __raw_writel(BF_PXP_LUT_CTRL_LOOKUP_MODE + (BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8) | + BF_PXP_LUT_CTRL_OUT_MODE + (BV_PXP_LUT_CTRL_OUT_MODE__Y8), + pxp->base + HW_PXP_LUT_CTRL); + + /* Initialize LUT address to 0 and set NUM_BYTES to 0 */ + __raw_writel(0, pxp->base + HW_PXP_LUT_ADDR); + + /* LUT address pointer auto-increments after each data write */ + for (pix_val = 0; pix_val < 256; pix_val += 4) { + for (i = 0; i < 4; i++) { + entry_src = use_cmap ? + cmap[pix_val + i] : pix_val + i; + entry[i] = (entry_src < 0x80) ? 0xFF : 0x00; + } + reg_val = (entry[3] << 24) | (entry[2] << 16) | + (entry[1] << 8) | entry[0]; + __raw_writel(reg_val, pxp->base + HW_PXP_LUT_DATA); + } + } else if ((lut_op & PXP_LUT_INVERT) != 0) { + /* Fill out LUT table with 8-bit inverted values */ + + /* clear bypass bit, set lookup mode & out mode */ + __raw_writel(BF_PXP_LUT_CTRL_LOOKUP_MODE + (BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8) | + BF_PXP_LUT_CTRL_OUT_MODE + (BV_PXP_LUT_CTRL_OUT_MODE__Y8), + pxp->base + HW_PXP_LUT_CTRL); + + /* Initialize LUT address to 0 and set NUM_BYTES to 0 */ + __raw_writel(0, pxp->base + HW_PXP_LUT_ADDR); + + /* LUT address pointer auto-increments after each data write */ + for (pix_val = 0; pix_val < 256; pix_val += 4) { + for (i = 0; i < 4; i++) { + entry_src = use_cmap ? + cmap[pix_val + i] : pix_val + i; + entry[i] = ~entry_src & 0xFF; + } + reg_val = (entry[3] << 24) | (entry[2] << 16) | + (entry[1] << 8) | entry[0]; + __raw_writel(reg_val, pxp->base + HW_PXP_LUT_DATA); + } + } else if ((lut_op & PXP_LUT_BLACK_WHITE) != 0) { + /* Fill out LUT table with 8-bit monochromized values */ + + /* clear bypass bit, set lookup mode & out mode */ + __raw_writel(BF_PXP_LUT_CTRL_LOOKUP_MODE + (BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8) | + BF_PXP_LUT_CTRL_OUT_MODE + (BV_PXP_LUT_CTRL_OUT_MODE__Y8), + pxp->base + HW_PXP_LUT_CTRL); + + /* Initialize LUT address to 0 and set NUM_BYTES to 0 */ + __raw_writel(0, pxp->base + HW_PXP_LUT_ADDR); + + /* LUT address pointer auto-increments after each data write */ + for (pix_val = 0; pix_val < 256; pix_val += 4) { + for (i = 0; i < 4; i++) { + entry_src = use_cmap ? + cmap[pix_val + i] : pix_val + i; + entry[i] = (entry_src < 0x80) ? 0x00 : 0xFF; + } + reg_val = (entry[3] << 24) | (entry[2] << 16) | + (entry[1] << 8) | entry[0]; + __raw_writel(reg_val, pxp->base + HW_PXP_LUT_DATA); + } + } else if (use_cmap) { + /* Fill out LUT table using colormap values */ + + /* clear bypass bit, set lookup mode & out mode */ + __raw_writel(BF_PXP_LUT_CTRL_LOOKUP_MODE + (BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8) | + BF_PXP_LUT_CTRL_OUT_MODE + (BV_PXP_LUT_CTRL_OUT_MODE__Y8), + pxp->base + HW_PXP_LUT_CTRL); + + /* Initialize LUT address to 0 and set NUM_BYTES to 0 */ + __raw_writel(0, pxp->base + HW_PXP_LUT_ADDR); + + /* LUT address pointer auto-increments after each data write */ + for (pix_val = 0; pix_val < 256; pix_val += 4) { + for (i = 0; i < 4; i++) + entry[i] = cmap[pix_val + i]; + reg_val = (entry[3] << 24) | (entry[2] << 16) | + (entry[1] << 8) | entry[0]; + __raw_writel(reg_val, pxp->base + HW_PXP_LUT_DATA); + } + } + + pxp->lut_state = lut_op; +} + +static void pxp_set_csc(struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_layer_param *s0_params = &pxp_conf->s0_param; + struct pxp_layer_param *ol_params = &pxp_conf->ol_param[0]; + struct pxp_layer_param *out_params = &pxp_conf->out_param; + + bool input_is_YUV = is_yuv(s0_params->pixel_fmt); + bool output_is_YUV = is_yuv(out_params->pixel_fmt); + + if (input_is_YUV && output_is_YUV) { + /* + * Input = YUV, Output = YUV + * No CSC unless we need to do combining + */ + if (ol_params->combine_enable) { + /* Must convert to RGB for combining with RGB overlay */ + + /* CSC1 - YUV->RGB */ + __raw_writel(0x04030000, pxp->base + HW_PXP_CSC1_COEF0); + __raw_writel(0x01230208, pxp->base + HW_PXP_CSC1_COEF1); + __raw_writel(0x076b079c, pxp->base + HW_PXP_CSC1_COEF2); + + /* CSC2 - RGB->YUV */ + __raw_writel(0x4, pxp->base + HW_PXP_CSC2_CTRL); + __raw_writel(0x0096004D, pxp->base + HW_PXP_CSC2_COEF0); + __raw_writel(0x05DA001D, pxp->base + HW_PXP_CSC2_COEF1); + __raw_writel(0x007005B6, pxp->base + HW_PXP_CSC2_COEF2); + __raw_writel(0x057C009E, pxp->base + HW_PXP_CSC2_COEF3); + __raw_writel(0x000005E6, pxp->base + HW_PXP_CSC2_COEF4); + __raw_writel(0x00000000, pxp->base + HW_PXP_CSC2_COEF5); + } else { + /* Input & Output both YUV, so bypass both CSCs */ + + /* CSC1 - Bypass */ + __raw_writel(0x40000000, pxp->base + HW_PXP_CSC1_COEF0); + + /* CSC2 - Bypass */ + __raw_writel(0x1, pxp->base + HW_PXP_CSC2_CTRL); + } + } else if (input_is_YUV && !output_is_YUV) { + /* + * Input = YUV, Output = RGB + * Use CSC1 to convert to RGB + */ + + /* CSC1 - YUV->RGB */ + __raw_writel(0x84ab01f0, pxp->base + HW_PXP_CSC1_COEF0); + __raw_writel(0x01980204, pxp->base + HW_PXP_CSC1_COEF1); + __raw_writel(0x0730079c, pxp->base + HW_PXP_CSC1_COEF2); + + /* CSC2 - Bypass */ + __raw_writel(0x1, pxp->base + HW_PXP_CSC2_CTRL); + } else if (!input_is_YUV && output_is_YUV) { + /* + * Input = RGB, Output = YUV + * Use CSC2 to convert to YUV + */ + + /* CSC1 - Bypass */ + __raw_writel(0x40000000, pxp->base + HW_PXP_CSC1_COEF0); + + /* CSC2 - RGB->YUV */ + __raw_writel(0x4, pxp->base + HW_PXP_CSC2_CTRL); + __raw_writel(0x0096004D, pxp->base + HW_PXP_CSC2_COEF0); + __raw_writel(0x05DA001D, pxp->base + HW_PXP_CSC2_COEF1); + __raw_writel(0x007005B6, pxp->base + HW_PXP_CSC2_COEF2); + __raw_writel(0x057C009E, pxp->base + HW_PXP_CSC2_COEF3); + __raw_writel(0x000005E6, pxp->base + HW_PXP_CSC2_COEF4); + __raw_writel(0x00000000, pxp->base + HW_PXP_CSC2_COEF5); + } else { + /* + * Input = RGB, Output = RGB + * Input & Output both RGB, so bypass both CSCs + */ + + /* CSC1 - Bypass */ + __raw_writel(0x40000000, pxp->base + HW_PXP_CSC1_COEF0); + + /* CSC2 - Bypass */ + __raw_writel(0x1, pxp->base + HW_PXP_CSC2_CTRL); + } + + /* YCrCb colorspace */ + /* Not sure when we use this...no YCrCb formats are defined for PxP */ + /* + __raw_writel(0x84ab01f0, HW_PXP_CSCCOEFF0_ADDR); + __raw_writel(0x01230204, HW_PXP_CSCCOEFF1_ADDR); + __raw_writel(0x0730079c, HW_PXP_CSCCOEFF2_ADDR); + */ + +} + +static void pxp_set_s0buf(struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_layer_param *s0_params = &pxp_conf->s0_param; + struct pxp_proc_data *proc_data = &pxp_conf->proc_data; + dma_addr_t Y, U, V; + dma_addr_t Y1, U1, V1; + u32 offset, bpp = 1; + u32 pitch = s0_params->stride ? s0_params->stride : + s0_params->width; + + Y = s0_params->paddr; + + if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB565) + bpp = 2; + else if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB32) + bpp = 4; + offset = (proc_data->srect.top * s0_params->width + + proc_data->srect.left) * bpp; + /* clipping or cropping */ + Y1 = Y + offset; + __raw_writel(Y1, pxp->base + HW_PXP_PS_BUF); + if ((s0_params->pixel_fmt == PXP_PIX_FMT_YUV420P) || + (s0_params->pixel_fmt == PXP_PIX_FMT_YVU420P) || + (s0_params->pixel_fmt == PXP_PIX_FMT_GREY) || + (s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P)) { + /* Set to 1 if YUV format is 4:2:2 rather than 4:2:0 */ + int s = 2; + if (s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P) + s = 1; + + offset = proc_data->srect.top * s0_params->width / 4 + + proc_data->srect.left / 2; + U = Y + (s0_params->width * s0_params->height); + U1 = U + offset; + V = U + ((s0_params->width * s0_params->height) >> s); + V1 = V + offset; + __raw_writel(U1, pxp->base + HW_PXP_PS_UBUF); + __raw_writel(V1, pxp->base + HW_PXP_PS_VBUF); + } else if ((s0_params->pixel_fmt == PXP_PIX_FMT_NV12) || + (s0_params->pixel_fmt == PXP_PIX_FMT_NV21) || + (s0_params->pixel_fmt == PXP_PIX_FMT_NV16) || + (s0_params->pixel_fmt == PXP_PIX_FMT_NV61)) { + int s = 2; + if ((s0_params->pixel_fmt == PXP_PIX_FMT_NV16) || + (s0_params->pixel_fmt == PXP_PIX_FMT_NV61)) + s = 1; + + offset = (proc_data->srect.top * s0_params->width + + proc_data->srect.left) / s; + U = Y + (s0_params->width * s0_params->height); + U1 = U + offset; + + __raw_writel(U1, pxp->base + HW_PXP_PS_UBUF); + } + + /* TODO: only support RGB565, Y8, Y4, YUV420 */ + if (s0_params->pixel_fmt == PXP_PIX_FMT_GREY || + s0_params->pixel_fmt == PXP_PIX_FMT_YUV420P || + s0_params->pixel_fmt == PXP_PIX_FMT_YVU420P || + s0_params->pixel_fmt == PXP_PIX_FMT_NV12 || + s0_params->pixel_fmt == PXP_PIX_FMT_NV21 || + s0_params->pixel_fmt == PXP_PIX_FMT_NV16 || + s0_params->pixel_fmt == PXP_PIX_FMT_NV61 || + s0_params->pixel_fmt == PXP_PIX_FMT_YUV422P) { + __raw_writel(pitch, pxp->base + HW_PXP_PS_PITCH); + } + else if (s0_params->pixel_fmt == PXP_PIX_FMT_GY04) + __raw_writel(pitch >> 1, + pxp->base + HW_PXP_PS_PITCH); + else if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB32) + __raw_writel(pitch << 2, + pxp->base + HW_PXP_PS_PITCH); + else if (s0_params->pixel_fmt == PXP_PIX_FMT_UYVY || + s0_params->pixel_fmt == PXP_PIX_FMT_YUYV || + s0_params->pixel_fmt == PXP_PIX_FMT_VYUY || + s0_params->pixel_fmt == PXP_PIX_FMT_YVYU) + __raw_writel(pitch << 1, + pxp->base + HW_PXP_PS_PITCH); + else if (s0_params->pixel_fmt == PXP_PIX_FMT_RGB565) + __raw_writel(pitch << 1, + pxp->base + HW_PXP_PS_PITCH); + else + __raw_writel(0, pxp->base + HW_PXP_PS_PITCH); +} + +/** + * pxp_config() - configure PxP for a processing task + * @pxps: PXP context. + * @pxp_chan: PXP channel. + * @return: 0 on success or negative error code on failure. + */ +static int pxp_config(struct pxps *pxp, struct pxp_channel *pxp_chan) +{ + struct pxp_config_data *pxp_conf_data = &pxp->pxp_conf_state; + int ol_nr; + int i; + + /* Configure PxP regs */ + pxp_set_ctrl(pxp); + pxp_set_s0param(pxp); + pxp_set_s0crop(pxp); + pxp_set_scaling(pxp); + ol_nr = pxp_conf_data->layer_nr - 2; + while (ol_nr > 0) { + i = pxp_conf_data->layer_nr - 2 - ol_nr; + pxp_set_oln(i, pxp); + pxp_set_olparam(i, pxp); + /* only the color key in higher overlay will take effect. */ + pxp_set_olcolorkey(i, pxp); + ol_nr--; + } + pxp_set_s0colorkey(pxp); + pxp_set_csc(pxp); + pxp_set_bg(pxp); + pxp_set_lut(pxp); + + pxp_set_s0buf(pxp); + pxp_set_outbuf(pxp); + + return 0; +} + +static void pxp_clk_enable(struct pxps *pxp) +{ + mutex_lock(&pxp->clk_mutex); + + if (pxp->clk_stat == CLK_STAT_ON) { + mutex_unlock(&pxp->clk_mutex); + return; + } + + pm_runtime_get_sync(pxp->dev); + + clk_prepare_enable(pxp->clk); + pxp->clk_stat = CLK_STAT_ON; + + mutex_unlock(&pxp->clk_mutex); +} + +static void pxp_clk_disable(struct pxps *pxp) +{ + unsigned long flags; + + mutex_lock(&pxp->clk_mutex); + + if (pxp->clk_stat == CLK_STAT_OFF) { + mutex_unlock(&pxp->clk_mutex); + return; + } + + spin_lock_irqsave(&pxp->lock, flags); + if ((pxp->pxp_ongoing == 0) && list_empty(&head)) { + spin_unlock_irqrestore(&pxp->lock, flags); + clk_disable_unprepare(pxp->clk); + pxp->clk_stat = CLK_STAT_OFF; + } else + spin_unlock_irqrestore(&pxp->lock, flags); + + pm_runtime_put_sync_suspend(pxp->dev); + + mutex_unlock(&pxp->clk_mutex); +} + +static inline void clkoff_callback(struct work_struct *w) +{ + struct pxps *pxp = container_of(w, struct pxps, work); + + pxp_clk_disable(pxp); +} + +static void pxp_clkoff_timer(unsigned long arg) +{ + struct pxps *pxp = (struct pxps *)arg; + + if ((pxp->pxp_ongoing == 0) && list_empty(&head)) + schedule_work(&pxp->work); + else + mod_timer(&pxp->clk_timer, + jiffies + msecs_to_jiffies(timeout_in_ms)); +} + +static struct pxp_tx_desc *pxpdma_first_active(struct pxp_channel *pxp_chan) +{ + return list_entry(pxp_chan->active_list.next, struct pxp_tx_desc, list); +} + +static struct pxp_tx_desc *pxpdma_first_queued(struct pxp_channel *pxp_chan) +{ + return list_entry(pxp_chan->queue.next, struct pxp_tx_desc, list); +} + +/* called with pxp_chan->lock held */ +static void __pxpdma_dostart(struct pxp_channel *pxp_chan) +{ + struct pxp_dma *pxp_dma = to_pxp_dma(pxp_chan->dma_chan.device); + struct pxps *pxp = to_pxp(pxp_dma); + struct pxp_tx_desc *desc; + struct pxp_tx_desc *child; + int i = 0; + + /* so far we presume only one transaction on active_list */ + /* S0 */ + desc = pxpdma_first_active(pxp_chan); + memcpy(&pxp->pxp_conf_state.s0_param, + &desc->layer_param.s0_param, sizeof(struct pxp_layer_param)); + memcpy(&pxp->pxp_conf_state.proc_data, + &desc->proc_data, sizeof(struct pxp_proc_data)); + + /* Save PxP configuration */ + list_for_each_entry(child, &desc->tx_list, list) { + if (i == 0) { /* Output */ + memcpy(&pxp->pxp_conf_state.out_param, + &child->layer_param.out_param, + sizeof(struct pxp_layer_param)); + } else { /* Overlay */ + memcpy(&pxp->pxp_conf_state.ol_param[i - 1], + &child->layer_param.ol_param, + sizeof(struct pxp_layer_param)); + } + + i++; + } + pr_debug("%s:%d S0 w/h %d/%d paddr %08x\n", __func__, __LINE__, + pxp->pxp_conf_state.s0_param.width, + pxp->pxp_conf_state.s0_param.height, + pxp->pxp_conf_state.s0_param.paddr); + pr_debug("%s:%d OUT w/h %d/%d paddr %08x\n", __func__, __LINE__, + pxp->pxp_conf_state.out_param.width, + pxp->pxp_conf_state.out_param.height, + pxp->pxp_conf_state.out_param.paddr); +} + +static void pxpdma_dostart_work(struct pxps *pxp) +{ + struct pxp_channel *pxp_chan = NULL; + unsigned long flags, flags1; + + spin_lock_irqsave(&pxp->lock, flags); + if (list_empty(&head)) { + pxp->pxp_ongoing = 0; + spin_unlock_irqrestore(&pxp->lock, flags); + return; + } + + pxp_chan = list_entry(head.next, struct pxp_channel, list); + + spin_lock_irqsave(&pxp_chan->lock, flags1); + if (!list_empty(&pxp_chan->active_list)) { + struct pxp_tx_desc *desc; + /* REVISIT */ + desc = pxpdma_first_active(pxp_chan); + __pxpdma_dostart(pxp_chan); + } + spin_unlock_irqrestore(&pxp_chan->lock, flags1); + + /* Configure PxP */ + pxp_config(pxp, pxp_chan); + + pxp_start(pxp); + + spin_unlock_irqrestore(&pxp->lock, flags); +} + +static void pxpdma_dequeue(struct pxp_channel *pxp_chan, struct list_head *list) +{ + struct pxp_tx_desc *desc = NULL; + do { + desc = pxpdma_first_queued(pxp_chan); + list_move_tail(&desc->list, list); + } while (!list_empty(&pxp_chan->queue)); +} + +static dma_cookie_t pxp_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct pxp_tx_desc *desc = to_tx_desc(tx); + struct pxp_channel *pxp_chan = to_pxp_channel(tx->chan); + dma_cookie_t cookie; + unsigned long flags; + + dev_dbg(&pxp_chan->dma_chan.dev->device, "received TX\n"); + + mutex_lock(&pxp_chan->chan_mutex); + + cookie = pxp_chan->dma_chan.cookie; + + if (++cookie < 0) + cookie = 1; + + /* from dmaengine.h: "last cookie value returned to client" */ + pxp_chan->dma_chan.cookie = cookie; + tx->cookie = cookie; + + /* pxp_chan->lock can be taken under ichan->lock, but not v.v. */ + spin_lock_irqsave(&pxp_chan->lock, flags); + + /* Here we add the tx descriptor to our PxP task queue. */ + list_add_tail(&desc->list, &pxp_chan->queue); + + spin_unlock_irqrestore(&pxp_chan->lock, flags); + + dev_dbg(&pxp_chan->dma_chan.dev->device, "done TX\n"); + + mutex_unlock(&pxp_chan->chan_mutex); + return cookie; +} + +/* Called with pxp_chan->chan_mutex held */ +static int pxp_desc_alloc(struct pxp_channel *pxp_chan, int n) +{ + struct pxp_tx_desc *desc = vmalloc(n * sizeof(struct pxp_tx_desc)); + + if (!desc) + return -ENOMEM; + + pxp_chan->n_tx_desc = n; + pxp_chan->desc = desc; + INIT_LIST_HEAD(&pxp_chan->active_list); + INIT_LIST_HEAD(&pxp_chan->queue); + INIT_LIST_HEAD(&pxp_chan->free_list); + + while (n--) { + struct dma_async_tx_descriptor *txd = &desc->txd; + + memset(txd, 0, sizeof(*txd)); + INIT_LIST_HEAD(&desc->tx_list); + dma_async_tx_descriptor_init(txd, &pxp_chan->dma_chan); + txd->tx_submit = pxp_tx_submit; + + list_add(&desc->list, &pxp_chan->free_list); + + desc++; + } + + return 0; +} + +/** + * pxp_init_channel() - initialize a PXP channel. + * @pxp_dma: PXP DMA context. + * @pchan: pointer to the channel object. + * @return 0 on success or negative error code on failure. + */ +static int pxp_init_channel(struct pxp_dma *pxp_dma, + struct pxp_channel *pxp_chan) +{ + unsigned long flags; + struct pxps *pxp = to_pxp(pxp_dma); + int ret = 0, n_desc = 0; + + /* + * We are using _virtual_ channel here. + * Each channel contains all parameters of corresponding layers + * for one transaction; each layer is represented as one descriptor + * (i.e., pxp_tx_desc) here. + */ + + spin_lock_irqsave(&pxp->lock, flags); + + /* max desc nr: S0+OL+OUT = 1+8+1 */ + n_desc = 16; + + spin_unlock_irqrestore(&pxp->lock, flags); + + if (n_desc && !pxp_chan->desc) + ret = pxp_desc_alloc(pxp_chan, n_desc); + + return ret; +} + +/** + * pxp_uninit_channel() - uninitialize a PXP channel. + * @pxp_dma: PXP DMA context. + * @pchan: pointer to the channel object. + * @return 0 on success or negative error code on failure. + */ +static int pxp_uninit_channel(struct pxp_dma *pxp_dma, + struct pxp_channel *pxp_chan) +{ + int ret = 0; + + if (pxp_chan->desc) + vfree(pxp_chan->desc); + + pxp_chan->desc = NULL; + + return ret; +} + +static irqreturn_t pxp_irq(int irq, void *dev_id) +{ + struct pxps *pxp = dev_id; + struct pxp_channel *pxp_chan; + struct pxp_tx_desc *desc; + dma_async_tx_callback callback; + void *callback_param; + unsigned long flags; + u32 hist_status; + + dump_pxp_reg(pxp); + + hist_status = + __raw_readl(pxp->base + HW_PXP_HIST_CTRL) & BM_PXP_HIST_CTRL_STATUS; + + __raw_writel(BM_PXP_STAT_IRQ, pxp->base + HW_PXP_STAT_CLR); + + spin_lock_irqsave(&pxp->lock, flags); + + if (list_empty(&head)) { + pxp->pxp_ongoing = 0; + spin_unlock_irqrestore(&pxp->lock, flags); + return IRQ_NONE; + } + + pxp_chan = list_entry(head.next, struct pxp_channel, list); + list_del_init(&pxp_chan->list); + + if (list_empty(&pxp_chan->active_list)) { + pr_debug("PXP_IRQ pxp_chan->active_list empty. chan_id %d\n", + pxp_chan->dma_chan.chan_id); + pxp->pxp_ongoing = 0; + spin_unlock_irqrestore(&pxp->lock, flags); + return IRQ_NONE; + } + + /* Get descriptor and call callback */ + desc = pxpdma_first_active(pxp_chan); + + pxp_chan->completed = desc->txd.cookie; + + callback = desc->txd.callback; + callback_param = desc->txd.callback_param; + + /* Send histogram status back to caller */ + desc->hist_status = hist_status; + + if ((desc->txd.flags & DMA_PREP_INTERRUPT) && callback) + callback(callback_param); + + pxp_chan->status = PXP_CHANNEL_INITIALIZED; + + list_splice_init(&desc->tx_list, &pxp_chan->free_list); + list_move(&desc->list, &pxp_chan->free_list); + + mutex_unlock(&hard_lock); + pxp->pxp_ongoing = 0; + mod_timer(&pxp->clk_timer, jiffies + msecs_to_jiffies(timeout_in_ms)); + + spin_unlock_irqrestore(&pxp->lock, flags); + + return IRQ_HANDLED; +} + +/* called with pxp_chan->lock held */ +static struct pxp_tx_desc *pxpdma_desc_get(struct pxp_channel *pxp_chan) +{ + struct pxp_tx_desc *desc, *_desc; + struct pxp_tx_desc *ret = NULL; + + list_for_each_entry_safe(desc, _desc, &pxp_chan->free_list, list) { + list_del_init(&desc->list); + ret = desc; + break; + } + + return ret; +} + +/* called with pxp_chan->lock held */ +static void pxpdma_desc_put(struct pxp_channel *pxp_chan, + struct pxp_tx_desc *desc) +{ + if (desc) { + struct device *dev = &pxp_chan->dma_chan.dev->device; + struct pxp_tx_desc *child; + + list_for_each_entry(child, &desc->tx_list, list) + dev_info(dev, "moving child desc %p to freelist\n", child); + list_splice_init(&desc->tx_list, &pxp_chan->free_list); + dev_info(dev, "moving desc %p to freelist\n", desc); + list_add(&desc->list, &pxp_chan->free_list); + } +} + +/* Allocate and initialise a transfer descriptor. */ +static struct dma_async_tx_descriptor *pxp_prep_slave_sg(struct dma_chan *chan, + struct scatterlist + *sgl, + unsigned int sg_len, + enum + dma_transfer_direction + direction, + unsigned long tx_flags, + void *context) +{ + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); + struct pxps *pxp = to_pxp(pxp_dma); + struct pxp_tx_desc *desc = NULL; + struct pxp_tx_desc *first = NULL, *prev = NULL; + struct scatterlist *sg; + unsigned long flags; + dma_addr_t phys_addr; + int i; + + if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) { + dev_err(chan->device->dev, "Invalid DMA direction %d!\n", + direction); + return NULL; + } + + if (unlikely(sg_len < 2)) + return NULL; + + spin_lock_irqsave(&pxp_chan->lock, flags); + for_each_sg(sgl, sg, sg_len, i) { + desc = pxpdma_desc_get(pxp_chan); + if (!desc) { + pxpdma_desc_put(pxp_chan, first); + dev_err(chan->device->dev, "Can't get DMA desc.\n"); + spin_unlock_irqrestore(&pxp_chan->lock, flags); + return NULL; + } + + phys_addr = sg_dma_address(sg); + + if (!first) { + first = desc; + + desc->layer_param.s0_param.paddr = phys_addr; + } else { + list_add_tail(&desc->list, &first->tx_list); + prev->next = desc; + desc->next = NULL; + + if (i == 1) + desc->layer_param.out_param.paddr = phys_addr; + else + desc->layer_param.ol_param.paddr = phys_addr; + } + + prev = desc; + } + spin_unlock_irqrestore(&pxp_chan->lock, flags); + + pxp->pxp_conf_state.layer_nr = sg_len; + first->txd.flags = tx_flags; + first->len = sg_len; + pr_debug("%s:%d first %p, first->len %d, flags %08x\n", + __func__, __LINE__, first, first->len, first->txd.flags); + + return &first->txd; +} + +static void pxp_issue_pending(struct dma_chan *chan) +{ + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); + struct pxps *pxp = to_pxp(pxp_dma); + unsigned long flags0, flags; + + spin_lock_irqsave(&pxp->lock, flags0); + spin_lock_irqsave(&pxp_chan->lock, flags); + + if (!list_empty(&pxp_chan->queue)) { + pxpdma_dequeue(pxp_chan, &pxp_chan->active_list); + pxp_chan->status = PXP_CHANNEL_READY; + list_add_tail(&pxp_chan->list, &head); + } else { + spin_unlock_irqrestore(&pxp_chan->lock, flags); + spin_unlock_irqrestore(&pxp->lock, flags0); + return; + } + spin_unlock_irqrestore(&pxp_chan->lock, flags); + spin_unlock_irqrestore(&pxp->lock, flags0); + + pxp_clk_enable(pxp); + mutex_lock(&hard_lock); + + spin_lock_irqsave(&pxp->lock, flags); + pxp->pxp_ongoing = 1; + spin_unlock_irqrestore(&pxp->lock, flags); + pxpdma_dostart_work(pxp); +} + +static void __pxp_terminate_all(struct dma_chan *chan) +{ + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + unsigned long flags; + + /* pchan->queue is modified in ISR, have to spinlock */ + spin_lock_irqsave(&pxp_chan->lock, flags); + list_splice_init(&pxp_chan->queue, &pxp_chan->free_list); + list_splice_init(&pxp_chan->active_list, &pxp_chan->free_list); + + spin_unlock_irqrestore(&pxp_chan->lock, flags); + + pxp_chan->status = PXP_CHANNEL_INITIALIZED; +} + +static int pxp_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + + /* Only supports DMA_TERMINATE_ALL */ + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + + mutex_lock(&pxp_chan->chan_mutex); + __pxp_terminate_all(chan); + mutex_unlock(&pxp_chan->chan_mutex); + + return 0; +} + +static int pxp_alloc_chan_resources(struct dma_chan *chan) +{ + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); + int ret; + + /* dmaengine.c now guarantees to only offer free channels */ + BUG_ON(chan->client_count > 1); + WARN_ON(pxp_chan->status != PXP_CHANNEL_FREE); + + chan->cookie = 1; + pxp_chan->completed = -ENXIO; + + pr_debug("%s dma_chan.chan_id %d\n", __func__, chan->chan_id); + ret = pxp_init_channel(pxp_dma, pxp_chan); + if (ret < 0) + goto err_chan; + + pxp_chan->status = PXP_CHANNEL_INITIALIZED; + + dev_dbg(&chan->dev->device, "Found channel 0x%x, irq %d\n", + chan->chan_id, pxp_chan->eof_irq); + + return ret; + +err_chan: + return ret; +} + +static void pxp_free_chan_resources(struct dma_chan *chan) +{ + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + struct pxp_dma *pxp_dma = to_pxp_dma(chan->device); + + mutex_lock(&pxp_chan->chan_mutex); + + __pxp_terminate_all(chan); + + pxp_chan->status = PXP_CHANNEL_FREE; + + pxp_uninit_channel(pxp_dma, pxp_chan); + + mutex_unlock(&pxp_chan->chan_mutex); +} + +static enum dma_status pxp_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + + if (cookie != chan->cookie) + return DMA_ERROR; + + if (txstate) { + txstate->last = pxp_chan->completed; + txstate->used = chan->cookie; + txstate->residue = 0; + } + return DMA_SUCCESS; +} + +static int pxp_hw_init(struct pxps *pxp) +{ + struct pxp_config_data *pxp_conf = &pxp->pxp_conf_state; + struct pxp_proc_data *proc_data = &pxp_conf->proc_data; + u32 reg_val; + + /* Pull PxP out of reset */ + __raw_writel(0, pxp->base + HW_PXP_CTRL); + + /* Config defaults */ + + /* Initialize non-channel-specific PxP parameters */ + proc_data->drect.left = proc_data->srect.left = 0; + proc_data->drect.top = proc_data->srect.top = 0; + proc_data->drect.width = proc_data->srect.width = 0; + proc_data->drect.height = proc_data->srect.height = 0; + proc_data->scaling = 0; + proc_data->hflip = 0; + proc_data->vflip = 0; + proc_data->rotate = 0; + proc_data->bgcolor = 0; + + /* Initialize S0 channel parameters */ + pxp_conf->s0_param.pixel_fmt = pxp_s0_formats[0]; + pxp_conf->s0_param.width = 0; + pxp_conf->s0_param.height = 0; + pxp_conf->s0_param.color_key = -1; + pxp_conf->s0_param.color_key_enable = false; + + /* Initialize OL channel parameters */ + pxp_conf->ol_param[0].combine_enable = false; + pxp_conf->ol_param[0].width = 0; + pxp_conf->ol_param[0].height = 0; + pxp_conf->ol_param[0].pixel_fmt = PXP_PIX_FMT_RGB565; + pxp_conf->ol_param[0].color_key_enable = false; + pxp_conf->ol_param[0].color_key = -1; + pxp_conf->ol_param[0].global_alpha_enable = false; + pxp_conf->ol_param[0].global_alpha = 0; + pxp_conf->ol_param[0].local_alpha_enable = false; + + /* Initialize Output channel parameters */ + pxp_conf->out_param.width = 0; + pxp_conf->out_param.height = 0; + pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_RGB565; + + proc_data->overlay_state = 0; + + /* Write default h/w config */ + pxp_set_ctrl(pxp); + pxp_set_s0param(pxp); + pxp_set_s0crop(pxp); + /* + * simply program the ULC to a higher value than the LRC + * to avoid any AS pixels to show up in the output buffer. + */ + __raw_writel(0xFFFFFFFF, pxp->base + HW_PXP_OUT_AS_ULC); + pxp_set_olparam(0, pxp); + pxp_set_olcolorkey(0, pxp); + + pxp_set_s0colorkey(pxp); + pxp_set_csc(pxp); + pxp_set_bg(pxp); + pxp_set_lut(pxp); + + /* One-time histogram configuration */ + reg_val = + BF_PXP_HIST_CTRL_PANEL_MODE(BV_PXP_HIST_CTRL_PANEL_MODE__GRAY16); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST_CTRL); + + reg_val = BF_PXP_HIST2_PARAM_VALUE0(0x00) | + BF_PXP_HIST2_PARAM_VALUE1(0x00F); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST2_PARAM); + + reg_val = BF_PXP_HIST4_PARAM_VALUE0(0x00) | + BF_PXP_HIST4_PARAM_VALUE1(0x05) | + BF_PXP_HIST4_PARAM_VALUE2(0x0A) | BF_PXP_HIST4_PARAM_VALUE3(0x0F); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST4_PARAM); + + reg_val = BF_PXP_HIST8_PARAM0_VALUE0(0x00) | + BF_PXP_HIST8_PARAM0_VALUE1(0x02) | + BF_PXP_HIST8_PARAM0_VALUE2(0x04) | BF_PXP_HIST8_PARAM0_VALUE3(0x06); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST8_PARAM0); + reg_val = BF_PXP_HIST8_PARAM1_VALUE4(0x09) | + BF_PXP_HIST8_PARAM1_VALUE5(0x0B) | + BF_PXP_HIST8_PARAM1_VALUE6(0x0D) | BF_PXP_HIST8_PARAM1_VALUE7(0x0F); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST8_PARAM1); + + reg_val = BF_PXP_HIST16_PARAM0_VALUE0(0x00) | + BF_PXP_HIST16_PARAM0_VALUE1(0x01) | + BF_PXP_HIST16_PARAM0_VALUE2(0x02) | + BF_PXP_HIST16_PARAM0_VALUE3(0x03); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM0); + reg_val = BF_PXP_HIST16_PARAM1_VALUE4(0x04) | + BF_PXP_HIST16_PARAM1_VALUE5(0x05) | + BF_PXP_HIST16_PARAM1_VALUE6(0x06) | + BF_PXP_HIST16_PARAM1_VALUE7(0x07); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM1); + reg_val = BF_PXP_HIST16_PARAM2_VALUE8(0x08) | + BF_PXP_HIST16_PARAM2_VALUE9(0x09) | + BF_PXP_HIST16_PARAM2_VALUE10(0x0A) | + BF_PXP_HIST16_PARAM2_VALUE11(0x0B); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM2); + reg_val = BF_PXP_HIST16_PARAM3_VALUE12(0x0C) | + BF_PXP_HIST16_PARAM3_VALUE13(0x0D) | + BF_PXP_HIST16_PARAM3_VALUE14(0x0E) | + BF_PXP_HIST16_PARAM3_VALUE15(0x0F); + __raw_writel(reg_val, pxp->base + HW_PXP_HIST16_PARAM3); + + return 0; +} + +static int pxp_dma_init(struct pxps *pxp) +{ + struct pxp_dma *pxp_dma = &pxp->pxp_dma; + struct dma_device *dma = &pxp_dma->dma; + int i; + + dma_cap_set(DMA_SLAVE, dma->cap_mask); + dma_cap_set(DMA_PRIVATE, dma->cap_mask); + + /* Compulsory common fields */ + dma->dev = pxp->dev; + dma->device_alloc_chan_resources = pxp_alloc_chan_resources; + dma->device_free_chan_resources = pxp_free_chan_resources; + dma->device_tx_status = pxp_tx_status; + dma->device_issue_pending = pxp_issue_pending; + + /* Compulsory for DMA_SLAVE fields */ + dma->device_prep_slave_sg = pxp_prep_slave_sg; + dma->device_control = pxp_control; + + /* Initialize PxP Channels */ + INIT_LIST_HEAD(&dma->channels); + for (i = 0; i < NR_PXP_VIRT_CHANNEL; i++) { + struct pxp_channel *pxp_chan = pxp->channel + i; + struct dma_chan *dma_chan = &pxp_chan->dma_chan; + + spin_lock_init(&pxp_chan->lock); + mutex_init(&pxp_chan->chan_mutex); + + /* Only one EOF IRQ for PxP, shared by all channels */ + pxp_chan->eof_irq = pxp->irq; + pxp_chan->status = PXP_CHANNEL_FREE; + pxp_chan->completed = -ENXIO; + snprintf(pxp_chan->eof_name, sizeof(pxp_chan->eof_name), + "PXP EOF %d", i); + + dma_chan->device = &pxp_dma->dma; + dma_chan->cookie = 1; + dma_chan->chan_id = i; + list_add_tail(&dma_chan->device_node, &dma->channels); + } + + return dma_async_device_register(&pxp_dma->dma); +} + +static ssize_t clk_off_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", timeout_in_ms); +} + +static ssize_t clk_off_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val; + if (sscanf(buf, "%d", &val) > 0) { + timeout_in_ms = val; + return count; + } + return -EINVAL; +} + +static DEVICE_ATTR(clk_off_timeout, 0644, clk_off_timeout_show, + clk_off_timeout_store); + +static ssize_t block_size_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", block_size); +} + +static ssize_t block_size_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char **last = NULL; + + block_size = simple_strtoul(buf, last, 0); + if (block_size > 1) + block_size = 1; + + return count; +} +static DEVICE_ATTR(block_size, S_IWUSR | S_IRUGO, + block_size_show, block_size_store); + +static const struct of_device_id imx_pxpdma_dt_ids[] = { + { .compatible = "fsl,imx6dl-pxp-dma", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_pxpdma_dt_ids); + +static int pxp_probe(struct platform_device *pdev) +{ + struct pxps *pxp; + struct resource *res; + int irq; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || irq < 0) { + err = -ENODEV; + goto exit; + } + + pxp = devm_kzalloc(&pdev->dev, sizeof(*pxp), GFP_KERNEL); + if (!pxp) { + dev_err(&pdev->dev, "failed to allocate control object\n"); + err = -ENOMEM; + goto exit; + } + + pxp->dev = &pdev->dev; + + platform_set_drvdata(pdev, pxp); + pxp->irq = irq; + + pxp->pxp_ongoing = 0; + pxp->lut_state = 0; + + spin_lock_init(&pxp->lock); + mutex_init(&pxp->clk_mutex); + mutex_init(&hard_lock); + + pxp->base = devm_request_and_ioremap(&pdev->dev, res); + if (pxp->base == NULL) { + dev_err(&pdev->dev, "Couldn't ioremap regs\n"); + err = -ENODEV; + goto exit; + } + + pxp->pdev = pdev; + + pxp->clk = devm_clk_get(&pdev->dev, "pxp-axi"); + clk_prepare_enable(pxp->clk); + + err = pxp_hw_init(pxp); + clk_disable_unprepare(pxp->clk); + if (err) { + dev_err(&pdev->dev, "failed to initialize hardware\n"); + goto exit; + } + + err = devm_request_irq(&pdev->dev, pxp->irq, pxp_irq, 0, + "pxp-dmaengine", pxp); + if (err) + goto exit; + /* Initialize DMA engine */ + err = pxp_dma_init(pxp); + if (err < 0) + goto exit; + + if (device_create_file(&pdev->dev, &dev_attr_clk_off_timeout)) { + dev_err(&pdev->dev, + "Unable to create file from clk_off_timeout\n"); + goto exit; + } + + device_create_file(&pdev->dev, &dev_attr_block_size); + dump_pxp_reg(pxp); + + INIT_WORK(&pxp->work, clkoff_callback); + init_timer(&pxp->clk_timer); + pxp->clk_timer.function = pxp_clkoff_timer; + pxp->clk_timer.data = (unsigned long)pxp; + + register_pxp_device(); + + pm_runtime_enable(pxp->dev); + +exit: + if (err) + dev_err(&pdev->dev, "Exiting (unsuccessfully) pxp_probe()\n"); + return err; +} + +static int pxp_remove(struct platform_device *pdev) +{ + struct pxps *pxp = platform_get_drvdata(pdev); + + unregister_pxp_device(); + cancel_work_sync(&pxp->work); + del_timer_sync(&pxp->clk_timer); + clk_disable_unprepare(pxp->clk); + device_remove_file(&pdev->dev, &dev_attr_clk_off_timeout); + device_remove_file(&pdev->dev, &dev_attr_block_size); + dma_async_device_unregister(&(pxp->pxp_dma.dma)); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pxp_suspend(struct device *dev) +{ + struct pxps *pxp = dev_get_drvdata(dev); + + pxp_clk_enable(pxp); + while (__raw_readl(pxp->base + HW_PXP_CTRL) & BM_PXP_CTRL_ENABLE) + ; + + __raw_writel(BM_PXP_CTRL_SFTRST, pxp->base + HW_PXP_CTRL); + pxp_clk_disable(pxp); + + return 0; +} + +static int pxp_resume(struct device *dev) +{ + struct pxps *pxp = dev_get_drvdata(dev); + + pxp_clk_enable(pxp); + /* Pull PxP out of reset */ + __raw_writel(0, pxp->base + HW_PXP_CTRL); + pxp_clk_disable(pxp); + + return 0; +} +#else +#define pxp_suspend NULL +#define pxp_resume NULL +#endif + +#ifdef CONFIG_PM_RUNTIME +static int pxp_runtime_suspend(struct device *dev) +{ + release_bus_freq(BUS_FREQ_HIGH); + dev_dbg(dev, "pxp busfreq high release.\n"); + + return 0; +} + +static int pxp_runtime_resume(struct device *dev) +{ + request_bus_freq(BUS_FREQ_HIGH); + dev_dbg(dev, "pxp busfreq high request.\n"); + + return 0; +} +#else +#define pxp_runtime_suspend NULL +#define pxp_runtime_resume NULL +#endif + +static const struct dev_pm_ops pxp_pm_ops = { + SET_RUNTIME_PM_OPS(pxp_runtime_suspend, pxp_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pxp_suspend, pxp_resume) +}; + +static struct platform_driver pxp_driver = { + .driver = { + .name = "imx-pxp", + .of_match_table = of_match_ptr(imx_pxpdma_dt_ids), + .pm = &pxp_pm_ops, + }, + .probe = pxp_probe, + .remove = pxp_remove, +}; + +module_platform_driver(pxp_driver); + + +MODULE_DESCRIPTION("i.MX PxP driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/dma/pxp/regs-pxp_v2.h linux-3.10.30-cubox-i/drivers/dma/pxp/regs-pxp_v2.h --- linux-3.10.30/drivers/dma/pxp/regs-pxp_v2.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/dma/pxp/regs-pxp_v2.h 2014-03-08 20:33:30.000000000 +0100 @@ -0,0 +1,1152 @@ +/* + * Freescale PXP Register Definitions + * + * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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 + * + * This file is created by xml file. Don't Edit it. + * + * Xml Revision: 1.29 + * Template revision: 1.3 + */ + +#ifndef __ARCH_ARM___PXP_H +#define __ARCH_ARM___PXP_H + +#define HW_PXP_CTRL (0x00000000) +#define HW_PXP_CTRL_SET (0x00000004) +#define HW_PXP_CTRL_CLR (0x00000008) +#define HW_PXP_CTRL_TOG (0x0000000c) + +#define BM_PXP_CTRL_SFTRST 0x80000000 +#define BM_PXP_CTRL_CLKGATE 0x40000000 +#define BM_PXP_CTRL_RSVD4 0x20000000 +#define BM_PXP_CTRL_EN_REPEAT 0x10000000 +#define BP_PXP_CTRL_RSVD3 26 +#define BM_PXP_CTRL_RSVD3 0x0C000000 +#define BF_PXP_CTRL_RSVD3(v) \ + (((v) << 26) & BM_PXP_CTRL_RSVD3) +#define BP_PXP_CTRL_INTERLACED_INPUT 24 +#define BM_PXP_CTRL_INTERLACED_INPUT 0x03000000 +#define BF_PXP_CTRL_INTERLACED_INPUT(v) \ + (((v) << 24) & BM_PXP_CTRL_INTERLACED_INPUT) +#define BV_PXP_CTRL_INTERLACED_INPUT__PROGRESSIVE 0x0 +#define BV_PXP_CTRL_INTERLACED_INPUT__FIELD0 0x2 +#define BV_PXP_CTRL_INTERLACED_INPUT__FIELD1 0x3 +#define BM_PXP_CTRL_BLOCK_SIZE 0x00800000 +#define BV_PXP_CTRL_BLOCK_SIZE__8X8 0x0 +#define BV_PXP_CTRL_BLOCK_SIZE__16X16 0x1 +#define BM_PXP_CTRL_ROT_POS 0x00400000 +#define BM_PXP_CTRL_IN_PLACE 0x00200000 +#define BP_PXP_CTRL_RSVD1 12 +#define BM_PXP_CTRL_RSVD1 0x001FF000 +#define BF_PXP_CTRL_RSVD1(v) \ + (((v) << 12) & BM_PXP_CTRL_RSVD1) +#define BM_PXP_CTRL_VFLIP 0x00000800 +#define BM_PXP_CTRL_HFLIP 0x00000400 +#define BP_PXP_CTRL_ROTATE 8 +#define BM_PXP_CTRL_ROTATE 0x00000300 +#define BF_PXP_CTRL_ROTATE(v) \ + (((v) << 8) & BM_PXP_CTRL_ROTATE) +#define BV_PXP_CTRL_ROTATE__ROT_0 0x0 +#define BV_PXP_CTRL_ROTATE__ROT_90 0x1 +#define BV_PXP_CTRL_ROTATE__ROT_180 0x2 +#define BV_PXP_CTRL_ROTATE__ROT_270 0x3 +#define BP_PXP_CTRL_RSVD0 5 +#define BM_PXP_CTRL_RSVD0 0x000000E0 +#define BF_PXP_CTRL_RSVD0(v) \ + (((v) << 5) & BM_PXP_CTRL_RSVD0) +#define BM_PXP_CTRL_ENABLE_LCD_HANDSHAKE 0x00000010 +#define BM_PXP_CTRL_LUT_DMA_IRQ_ENABLE 0x00000008 +#define BM_PXP_CTRL_NEXT_IRQ_ENABLE 0x00000004 +#define BM_PXP_CTRL_IRQ_ENABLE 0x00000002 +#define BM_PXP_CTRL_ENABLE 0x00000001 + +#define HW_PXP_STAT (0x00000010) +#define HW_PXP_STAT_SET (0x00000014) +#define HW_PXP_STAT_CLR (0x00000018) +#define HW_PXP_STAT_TOG (0x0000001c) + +#define BP_PXP_STAT_BLOCKX 24 +#define BM_PXP_STAT_BLOCKX 0xFF000000 +#define BF_PXP_STAT_BLOCKX(v) \ + (((v) << 24) & BM_PXP_STAT_BLOCKX) +#define BP_PXP_STAT_BLOCKY 16 +#define BM_PXP_STAT_BLOCKY 0x00FF0000 +#define BF_PXP_STAT_BLOCKY(v) \ + (((v) << 16) & BM_PXP_STAT_BLOCKY) +#define BP_PXP_STAT_RSVD2 9 +#define BM_PXP_STAT_RSVD2 0x0000FE00 +#define BF_PXP_STAT_RSVD2(v) \ + (((v) << 9) & BM_PXP_STAT_RSVD2) +#define BM_PXP_STAT_LUT_DMA_LOAD_DONE_IRQ 0x00000100 +#define BP_PXP_STAT_AXI_ERROR_ID 4 +#define BM_PXP_STAT_AXI_ERROR_ID 0x000000F0 +#define BF_PXP_STAT_AXI_ERROR_ID(v) \ + (((v) << 4) & BM_PXP_STAT_AXI_ERROR_ID) +#define BM_PXP_STAT_NEXT_IRQ 0x00000008 +#define BM_PXP_STAT_AXI_READ_ERROR 0x00000004 +#define BM_PXP_STAT_AXI_WRITE_ERROR 0x00000002 +#define BM_PXP_STAT_IRQ 0x00000001 + +#define HW_PXP_OUT_CTRL (0x00000020) +#define HW_PXP_OUT_CTRL_SET (0x00000024) +#define HW_PXP_OUT_CTRL_CLR (0x00000028) +#define HW_PXP_OUT_CTRL_TOG (0x0000002c) + +#define BP_PXP_OUT_CTRL_ALPHA 24 +#define BM_PXP_OUT_CTRL_ALPHA 0xFF000000 +#define BF_PXP_OUT_CTRL_ALPHA(v) \ + (((v) << 24) & BM_PXP_OUT_CTRL_ALPHA) +#define BM_PXP_OUT_CTRL_ALPHA_OUTPUT 0x00800000 +#define BP_PXP_OUT_CTRL_RSVD1 10 +#define BM_PXP_OUT_CTRL_RSVD1 0x007FFC00 +#define BF_PXP_OUT_CTRL_RSVD1(v) \ + (((v) << 10) & BM_PXP_OUT_CTRL_RSVD1) +#define BP_PXP_OUT_CTRL_INTERLACED_OUTPUT 8 +#define BM_PXP_OUT_CTRL_INTERLACED_OUTPUT 0x00000300 +#define BF_PXP_OUT_CTRL_INTERLACED_OUTPUT(v) \ + (((v) << 8) & BM_PXP_OUT_CTRL_INTERLACED_OUTPUT) +#define BV_PXP_OUT_CTRL_INTERLACED_OUTPUT__PROGRESSIVE 0x0 +#define BV_PXP_OUT_CTRL_INTERLACED_OUTPUT__FIELD0 0x1 +#define BV_PXP_OUT_CTRL_INTERLACED_OUTPUT__FIELD1 0x2 +#define BV_PXP_OUT_CTRL_INTERLACED_OUTPUT__INTERLACED 0x3 +#define BP_PXP_OUT_CTRL_RSVD0 5 +#define BM_PXP_OUT_CTRL_RSVD0 0x000000E0 +#define BF_PXP_OUT_CTRL_RSVD0(v) \ + (((v) << 5) & BM_PXP_OUT_CTRL_RSVD0) +#define BP_PXP_OUT_CTRL_FORMAT 0 +#define BM_PXP_OUT_CTRL_FORMAT 0x0000001F +#define BF_PXP_OUT_CTRL_FORMAT(v) \ + (((v) << 0) & BM_PXP_OUT_CTRL_FORMAT) +#define BV_PXP_OUT_CTRL_FORMAT__ARGB8888 0x0 +#define BV_PXP_OUT_CTRL_FORMAT__RGB888 0x4 +#define BV_PXP_OUT_CTRL_FORMAT__RGB888P 0x5 +#define BV_PXP_OUT_CTRL_FORMAT__ARGB1555 0x8 +#define BV_PXP_OUT_CTRL_FORMAT__ARGB4444 0x9 +#define BV_PXP_OUT_CTRL_FORMAT__RGB555 0xC +#define BV_PXP_OUT_CTRL_FORMAT__RGB444 0xD +#define BV_PXP_OUT_CTRL_FORMAT__RGB565 0xE +#define BV_PXP_OUT_CTRL_FORMAT__YUV1P444 0x10 +#define BV_PXP_OUT_CTRL_FORMAT__UYVY1P422 0x12 +#define BV_PXP_OUT_CTRL_FORMAT__VYUY1P422 0x13 +#define BV_PXP_OUT_CTRL_FORMAT__Y8 0x14 +#define BV_PXP_OUT_CTRL_FORMAT__Y4 0x15 +#define BV_PXP_OUT_CTRL_FORMAT__YUV2P422 0x18 +#define BV_PXP_OUT_CTRL_FORMAT__YUV2P420 0x19 +#define BV_PXP_OUT_CTRL_FORMAT__YVU2P422 0x1A +#define BV_PXP_OUT_CTRL_FORMAT__YVU2P420 0x1B + +#define HW_PXP_OUT_BUF (0x00000030) + +#define BP_PXP_OUT_BUF_ADDR 0 +#define BM_PXP_OUT_BUF_ADDR 0xFFFFFFFF +#define BF_PXP_OUT_BUF_ADDR(v) (v) + +#define HW_PXP_OUT_BUF2 (0x00000040) + +#define BP_PXP_OUT_BUF2_ADDR 0 +#define BM_PXP_OUT_BUF2_ADDR 0xFFFFFFFF +#define BF_PXP_OUT_BUF2_ADDR(v) (v) + +#define HW_PXP_OUT_PITCH (0x00000050) + +#define BP_PXP_OUT_PITCH_RSVD 16 +#define BM_PXP_OUT_PITCH_RSVD 0xFFFF0000 +#define BF_PXP_OUT_PITCH_RSVD(v) \ + (((v) << 16) & BM_PXP_OUT_PITCH_RSVD) +#define BP_PXP_OUT_PITCH_PITCH 0 +#define BM_PXP_OUT_PITCH_PITCH 0x0000FFFF +#define BF_PXP_OUT_PITCH_PITCH(v) \ + (((v) << 0) & BM_PXP_OUT_PITCH_PITCH) + +#define HW_PXP_OUT_LRC (0x00000060) + +#define BP_PXP_OUT_LRC_RSVD1 30 +#define BM_PXP_OUT_LRC_RSVD1 0xC0000000 +#define BF_PXP_OUT_LRC_RSVD1(v) \ + (((v) << 30) & BM_PXP_OUT_LRC_RSVD1) +#define BP_PXP_OUT_LRC_X 16 +#define BM_PXP_OUT_LRC_X 0x3FFF0000 +#define BF_PXP_OUT_LRC_X(v) \ + (((v) << 16) & BM_PXP_OUT_LRC_X) +#define BP_PXP_OUT_LRC_RSVD0 14 +#define BM_PXP_OUT_LRC_RSVD0 0x0000C000 +#define BF_PXP_OUT_LRC_RSVD0(v) \ + (((v) << 14) & BM_PXP_OUT_LRC_RSVD0) +#define BP_PXP_OUT_LRC_Y 0 +#define BM_PXP_OUT_LRC_Y 0x00003FFF +#define BF_PXP_OUT_LRC_Y(v) \ + (((v) << 0) & BM_PXP_OUT_LRC_Y) + +#define HW_PXP_OUT_PS_ULC (0x00000070) + +#define BP_PXP_OUT_PS_ULC_RSVD1 30 +#define BM_PXP_OUT_PS_ULC_RSVD1 0xC0000000 +#define BF_PXP_OUT_PS_ULC_RSVD1(v) \ + (((v) << 30) & BM_PXP_OUT_PS_ULC_RSVD1) +#define BP_PXP_OUT_PS_ULC_X 16 +#define BM_PXP_OUT_PS_ULC_X 0x3FFF0000 +#define BF_PXP_OUT_PS_ULC_X(v) \ + (((v) << 16) & BM_PXP_OUT_PS_ULC_X) +#define BP_PXP_OUT_PS_ULC_RSVD0 14 +#define BM_PXP_OUT_PS_ULC_RSVD0 0x0000C000 +#define BF_PXP_OUT_PS_ULC_RSVD0(v) \ + (((v) << 14) & BM_PXP_OUT_PS_ULC_RSVD0) +#define BP_PXP_OUT_PS_ULC_Y 0 +#define BM_PXP_OUT_PS_ULC_Y 0x00003FFF +#define BF_PXP_OUT_PS_ULC_Y(v) \ + (((v) << 0) & BM_PXP_OUT_PS_ULC_Y) + +#define HW_PXP_OUT_PS_LRC (0x00000080) + +#define BP_PXP_OUT_PS_LRC_RSVD1 30 +#define BM_PXP_OUT_PS_LRC_RSVD1 0xC0000000 +#define BF_PXP_OUT_PS_LRC_RSVD1(v) \ + (((v) << 30) & BM_PXP_OUT_PS_LRC_RSVD1) +#define BP_PXP_OUT_PS_LRC_X 16 +#define BM_PXP_OUT_PS_LRC_X 0x3FFF0000 +#define BF_PXP_OUT_PS_LRC_X(v) \ + (((v) << 16) & BM_PXP_OUT_PS_LRC_X) +#define BP_PXP_OUT_PS_LRC_RSVD0 14 +#define BM_PXP_OUT_PS_LRC_RSVD0 0x0000C000 +#define BF_PXP_OUT_PS_LRC_RSVD0(v) \ + (((v) << 14) & BM_PXP_OUT_PS_LRC_RSVD0) +#define BP_PXP_OUT_PS_LRC_Y 0 +#define BM_PXP_OUT_PS_LRC_Y 0x00003FFF +#define BF_PXP_OUT_PS_LRC_Y(v) \ + (((v) << 0) & BM_PXP_OUT_PS_LRC_Y) + +#define HW_PXP_OUT_AS_ULC (0x00000090) + +#define BP_PXP_OUT_AS_ULC_RSVD1 30 +#define BM_PXP_OUT_AS_ULC_RSVD1 0xC0000000 +#define BF_PXP_OUT_AS_ULC_RSVD1(v) \ + (((v) << 30) & BM_PXP_OUT_AS_ULC_RSVD1) +#define BP_PXP_OUT_AS_ULC_X 16 +#define BM_PXP_OUT_AS_ULC_X 0x3FFF0000 +#define BF_PXP_OUT_AS_ULC_X(v) \ + (((v) << 16) & BM_PXP_OUT_AS_ULC_X) +#define BP_PXP_OUT_AS_ULC_RSVD0 14 +#define BM_PXP_OUT_AS_ULC_RSVD0 0x0000C000 +#define BF_PXP_OUT_AS_ULC_RSVD0(v) \ + (((v) << 14) & BM_PXP_OUT_AS_ULC_RSVD0) +#define BP_PXP_OUT_AS_ULC_Y 0 +#define BM_PXP_OUT_AS_ULC_Y 0x00003FFF +#define BF_PXP_OUT_AS_ULC_Y(v) \ + (((v) << 0) & BM_PXP_OUT_AS_ULC_Y) + +#define HW_PXP_OUT_AS_LRC (0x000000a0) + +#define BP_PXP_OUT_AS_LRC_RSVD1 30 +#define BM_PXP_OUT_AS_LRC_RSVD1 0xC0000000 +#define BF_PXP_OUT_AS_LRC_RSVD1(v) \ + (((v) << 30) & BM_PXP_OUT_AS_LRC_RSVD1) +#define BP_PXP_OUT_AS_LRC_X 16 +#define BM_PXP_OUT_AS_LRC_X 0x3FFF0000 +#define BF_PXP_OUT_AS_LRC_X(v) \ + (((v) << 16) & BM_PXP_OUT_AS_LRC_X) +#define BP_PXP_OUT_AS_LRC_RSVD0 14 +#define BM_PXP_OUT_AS_LRC_RSVD0 0x0000C000 +#define BF_PXP_OUT_AS_LRC_RSVD0(v) \ + (((v) << 14) & BM_PXP_OUT_AS_LRC_RSVD0) +#define BP_PXP_OUT_AS_LRC_Y 0 +#define BM_PXP_OUT_AS_LRC_Y 0x00003FFF +#define BF_PXP_OUT_AS_LRC_Y(v) \ + (((v) << 0) & BM_PXP_OUT_AS_LRC_Y) + +#define HW_PXP_PS_CTRL (0x000000b0) +#define HW_PXP_PS_CTRL_SET (0x000000b4) +#define HW_PXP_PS_CTRL_CLR (0x000000b8) +#define HW_PXP_PS_CTRL_TOG (0x000000bc) + +#define BP_PXP_PS_CTRL_RSVD1 12 +#define BM_PXP_PS_CTRL_RSVD1 0xFFFFF000 +#define BF_PXP_PS_CTRL_RSVD1(v) \ + (((v) << 12) & BM_PXP_PS_CTRL_RSVD1) +#define BP_PXP_PS_CTRL_DECX 10 +#define BM_PXP_PS_CTRL_DECX 0x00000C00 +#define BF_PXP_PS_CTRL_DECX(v) \ + (((v) << 10) & BM_PXP_PS_CTRL_DECX) +#define BV_PXP_PS_CTRL_DECX__DISABLE 0x0 +#define BV_PXP_PS_CTRL_DECX__DECX2 0x1 +#define BV_PXP_PS_CTRL_DECX__DECX4 0x2 +#define BV_PXP_PS_CTRL_DECX__DECX8 0x3 +#define BP_PXP_PS_CTRL_DECY 8 +#define BM_PXP_PS_CTRL_DECY 0x00000300 +#define BF_PXP_PS_CTRL_DECY(v) \ + (((v) << 8) & BM_PXP_PS_CTRL_DECY) +#define BV_PXP_PS_CTRL_DECY__DISABLE 0x0 +#define BV_PXP_PS_CTRL_DECY__DECY2 0x1 +#define BV_PXP_PS_CTRL_DECY__DECY4 0x2 +#define BV_PXP_PS_CTRL_DECY__DECY8 0x3 +#define BP_PXP_PS_CTRL_SWAP 5 +#define BM_PXP_PS_CTRL_SWAP 0x000000E0 +#define BF_PXP_PS_CTRL_SWAP(v) \ + (((v) << 5) & BM_PXP_PS_CTRL_SWAP) +#define BP_PXP_PS_CTRL_FORMAT 0 +#define BM_PXP_PS_CTRL_FORMAT 0x0000001F +#define BF_PXP_PS_CTRL_FORMAT(v) \ + (((v) << 0) & BM_PXP_PS_CTRL_FORMAT) +#define BV_PXP_PS_CTRL_FORMAT__RGB888 0x4 +#define BV_PXP_PS_CTRL_FORMAT__RGB555 0xC +#define BV_PXP_PS_CTRL_FORMAT__RGB444 0xD +#define BV_PXP_PS_CTRL_FORMAT__RGB565 0xE +#define BV_PXP_PS_CTRL_FORMAT__YUV1P444 0x10 +#define BV_PXP_PS_CTRL_FORMAT__UYVY1P422 0x12 +#define BV_PXP_PS_CTRL_FORMAT__VYUY1P422 0x13 +#define BV_PXP_PS_CTRL_FORMAT__Y8 0x14 +#define BV_PXP_PS_CTRL_FORMAT__Y4 0x15 +#define BV_PXP_PS_CTRL_FORMAT__YUV2P422 0x18 +#define BV_PXP_PS_CTRL_FORMAT__YUV2P420 0x19 +#define BV_PXP_PS_CTRL_FORMAT__YVU2P422 0x1A +#define BV_PXP_PS_CTRL_FORMAT__YVU2P420 0x1B +#define BV_PXP_PS_CTRL_FORMAT__YUV422 0x1E +#define BV_PXP_PS_CTRL_FORMAT__YUV420 0x1F + +#define HW_PXP_PS_BUF (0x000000c0) + +#define BP_PXP_PS_BUF_ADDR 0 +#define BM_PXP_PS_BUF_ADDR 0xFFFFFFFF +#define BF_PXP_PS_BUF_ADDR(v) (v) + +#define HW_PXP_PS_UBUF (0x000000d0) + +#define BP_PXP_PS_UBUF_ADDR 0 +#define BM_PXP_PS_UBUF_ADDR 0xFFFFFFFF +#define BF_PXP_PS_UBUF_ADDR(v) (v) + +#define HW_PXP_PS_VBUF (0x000000e0) + +#define BP_PXP_PS_VBUF_ADDR 0 +#define BM_PXP_PS_VBUF_ADDR 0xFFFFFFFF +#define BF_PXP_PS_VBUF_ADDR(v) (v) + +#define HW_PXP_PS_PITCH (0x000000f0) + +#define BP_PXP_PS_PITCH_RSVD 16 +#define BM_PXP_PS_PITCH_RSVD 0xFFFF0000 +#define BF_PXP_PS_PITCH_RSVD(v) \ + (((v) << 16) & BM_PXP_PS_PITCH_RSVD) +#define BP_PXP_PS_PITCH_PITCH 0 +#define BM_PXP_PS_PITCH_PITCH 0x0000FFFF +#define BF_PXP_PS_PITCH_PITCH(v) \ + (((v) << 0) & BM_PXP_PS_PITCH_PITCH) + +#define HW_PXP_PS_BACKGROUND (0x00000100) + +#define BP_PXP_PS_BACKGROUND_RSVD 24 +#define BM_PXP_PS_BACKGROUND_RSVD 0xFF000000 +#define BF_PXP_PS_BACKGROUND_RSVD(v) \ + (((v) << 24) & BM_PXP_PS_BACKGROUND_RSVD) +#define BP_PXP_PS_BACKGROUND_COLOR 0 +#define BM_PXP_PS_BACKGROUND_COLOR 0x00FFFFFF +#define BF_PXP_PS_BACKGROUND_COLOR(v) \ + (((v) << 0) & BM_PXP_PS_BACKGROUND_COLOR) + +#define HW_PXP_PS_SCALE (0x00000110) + +#define BM_PXP_PS_SCALE_RSVD2 0x80000000 +#define BP_PXP_PS_SCALE_YSCALE 16 +#define BM_PXP_PS_SCALE_YSCALE 0x7FFF0000 +#define BF_PXP_PS_SCALE_YSCALE(v) \ + (((v) << 16) & BM_PXP_PS_SCALE_YSCALE) +#define BM_PXP_PS_SCALE_RSVD1 0x00008000 +#define BP_PXP_PS_SCALE_XSCALE 0 +#define BM_PXP_PS_SCALE_XSCALE 0x00007FFF +#define BF_PXP_PS_SCALE_XSCALE(v) \ + (((v) << 0) & BM_PXP_PS_SCALE_XSCALE) + +#define HW_PXP_PS_OFFSET (0x00000120) + +#define BP_PXP_PS_OFFSET_RSVD2 28 +#define BM_PXP_PS_OFFSET_RSVD2 0xF0000000 +#define BF_PXP_PS_OFFSET_RSVD2(v) \ + (((v) << 28) & BM_PXP_PS_OFFSET_RSVD2) +#define BP_PXP_PS_OFFSET_YOFFSET 16 +#define BM_PXP_PS_OFFSET_YOFFSET 0x0FFF0000 +#define BF_PXP_PS_OFFSET_YOFFSET(v) \ + (((v) << 16) & BM_PXP_PS_OFFSET_YOFFSET) +#define BP_PXP_PS_OFFSET_RSVD1 12 +#define BM_PXP_PS_OFFSET_RSVD1 0x0000F000 +#define BF_PXP_PS_OFFSET_RSVD1(v) \ + (((v) << 12) & BM_PXP_PS_OFFSET_RSVD1) +#define BP_PXP_PS_OFFSET_XOFFSET 0 +#define BM_PXP_PS_OFFSET_XOFFSET 0x00000FFF +#define BF_PXP_PS_OFFSET_XOFFSET(v) \ + (((v) << 0) & BM_PXP_PS_OFFSET_XOFFSET) + +#define HW_PXP_PS_CLRKEYLOW (0x00000130) + +#define BP_PXP_PS_CLRKEYLOW_RSVD1 24 +#define BM_PXP_PS_CLRKEYLOW_RSVD1 0xFF000000 +#define BF_PXP_PS_CLRKEYLOW_RSVD1(v) \ + (((v) << 24) & BM_PXP_PS_CLRKEYLOW_RSVD1) +#define BP_PXP_PS_CLRKEYLOW_PIXEL 0 +#define BM_PXP_PS_CLRKEYLOW_PIXEL 0x00FFFFFF +#define BF_PXP_PS_CLRKEYLOW_PIXEL(v) \ + (((v) << 0) & BM_PXP_PS_CLRKEYLOW_PIXEL) + +#define HW_PXP_PS_CLRKEYHIGH (0x00000140) + +#define BP_PXP_PS_CLRKEYHIGH_RSVD1 24 +#define BM_PXP_PS_CLRKEYHIGH_RSVD1 0xFF000000 +#define BF_PXP_PS_CLRKEYHIGH_RSVD1(v) \ + (((v) << 24) & BM_PXP_PS_CLRKEYHIGH_RSVD1) +#define BP_PXP_PS_CLRKEYHIGH_PIXEL 0 +#define BM_PXP_PS_CLRKEYHIGH_PIXEL 0x00FFFFFF +#define BF_PXP_PS_CLRKEYHIGH_PIXEL(v) \ + (((v) << 0) & BM_PXP_PS_CLRKEYHIGH_PIXEL) + +#define HW_PXP_AS_CTRL (0x00000150) + +#define BP_PXP_AS_CTRL_RSVD1 21 +#define BM_PXP_AS_CTRL_RSVD1 0xFFE00000 +#define BF_PXP_AS_CTRL_RSVD1(v) \ + (((v) << 21) & BM_PXP_AS_CTRL_RSVD1) +#define BM_PXP_AS_CTRL_ALPHA_INVERT 0x00100000 +#define BP_PXP_AS_CTRL_ROP 16 +#define BM_PXP_AS_CTRL_ROP 0x000F0000 +#define BF_PXP_AS_CTRL_ROP(v) \ + (((v) << 16) & BM_PXP_AS_CTRL_ROP) +#define BV_PXP_AS_CTRL_ROP__MASKAS 0x0 +#define BV_PXP_AS_CTRL_ROP__MASKNOTAS 0x1 +#define BV_PXP_AS_CTRL_ROP__MASKASNOT 0x2 +#define BV_PXP_AS_CTRL_ROP__MERGEAS 0x3 +#define BV_PXP_AS_CTRL_ROP__MERGENOTAS 0x4 +#define BV_PXP_AS_CTRL_ROP__MERGEASNOT 0x5 +#define BV_PXP_AS_CTRL_ROP__NOTCOPYAS 0x6 +#define BV_PXP_AS_CTRL_ROP__NOT 0x7 +#define BV_PXP_AS_CTRL_ROP__NOTMASKAS 0x8 +#define BV_PXP_AS_CTRL_ROP__NOTMERGEAS 0x9 +#define BV_PXP_AS_CTRL_ROP__XORAS 0xA +#define BV_PXP_AS_CTRL_ROP__NOTXORAS 0xB +#define BP_PXP_AS_CTRL_ALPHA 8 +#define BM_PXP_AS_CTRL_ALPHA 0x0000FF00 +#define BF_PXP_AS_CTRL_ALPHA(v) \ + (((v) << 8) & BM_PXP_AS_CTRL_ALPHA) +#define BP_PXP_AS_CTRL_FORMAT 4 +#define BM_PXP_AS_CTRL_FORMAT 0x000000F0 +#define BF_PXP_AS_CTRL_FORMAT(v) \ + (((v) << 4) & BM_PXP_AS_CTRL_FORMAT) +#define BV_PXP_AS_CTRL_FORMAT__ARGB8888 0x0 +#define BV_PXP_AS_CTRL_FORMAT__RGB888 0x4 +#define BV_PXP_AS_CTRL_FORMAT__ARGB1555 0x8 +#define BV_PXP_AS_CTRL_FORMAT__ARGB4444 0x9 +#define BV_PXP_AS_CTRL_FORMAT__RGB555 0xC +#define BV_PXP_AS_CTRL_FORMAT__RGB444 0xD +#define BV_PXP_AS_CTRL_FORMAT__RGB565 0xE +#define BM_PXP_AS_CTRL_ENABLE_COLORKEY 0x00000008 +#define BP_PXP_AS_CTRL_ALPHA_CTRL 1 +#define BM_PXP_AS_CTRL_ALPHA_CTRL 0x00000006 +#define BF_PXP_AS_CTRL_ALPHA_CTRL(v) \ + (((v) << 1) & BM_PXP_AS_CTRL_ALPHA_CTRL) +#define BV_PXP_AS_CTRL_ALPHA_CTRL__Embedded 0x0 +#define BV_PXP_AS_CTRL_ALPHA_CTRL__Override 0x1 +#define BV_PXP_AS_CTRL_ALPHA_CTRL__Multiply 0x2 +#define BV_PXP_AS_CTRL_ALPHA_CTRL__ROPs 0x3 +#define BM_PXP_AS_CTRL_RSVD0 0x00000001 + +#define HW_PXP_AS_BUF (0x00000160) + +#define BP_PXP_AS_BUF_ADDR 0 +#define BM_PXP_AS_BUF_ADDR 0xFFFFFFFF +#define BF_PXP_AS_BUF_ADDR(v) (v) + +#define HW_PXP_AS_PITCH (0x00000170) + +#define BP_PXP_AS_PITCH_RSVD 16 +#define BM_PXP_AS_PITCH_RSVD 0xFFFF0000 +#define BF_PXP_AS_PITCH_RSVD(v) \ + (((v) << 16) & BM_PXP_AS_PITCH_RSVD) +#define BP_PXP_AS_PITCH_PITCH 0 +#define BM_PXP_AS_PITCH_PITCH 0x0000FFFF +#define BF_PXP_AS_PITCH_PITCH(v) \ + (((v) << 0) & BM_PXP_AS_PITCH_PITCH) + +#define HW_PXP_AS_CLRKEYLOW (0x00000180) + +#define BP_PXP_AS_CLRKEYLOW_RSVD1 24 +#define BM_PXP_AS_CLRKEYLOW_RSVD1 0xFF000000 +#define BF_PXP_AS_CLRKEYLOW_RSVD1(v) \ + (((v) << 24) & BM_PXP_AS_CLRKEYLOW_RSVD1) +#define BP_PXP_AS_CLRKEYLOW_PIXEL 0 +#define BM_PXP_AS_CLRKEYLOW_PIXEL 0x00FFFFFF +#define BF_PXP_AS_CLRKEYLOW_PIXEL(v) \ + (((v) << 0) & BM_PXP_AS_CLRKEYLOW_PIXEL) + +#define HW_PXP_AS_CLRKEYHIGH (0x00000190) + +#define BP_PXP_AS_CLRKEYHIGH_RSVD1 24 +#define BM_PXP_AS_CLRKEYHIGH_RSVD1 0xFF000000 +#define BF_PXP_AS_CLRKEYHIGH_RSVD1(v) \ + (((v) << 24) & BM_PXP_AS_CLRKEYHIGH_RSVD1) +#define BP_PXP_AS_CLRKEYHIGH_PIXEL 0 +#define BM_PXP_AS_CLRKEYHIGH_PIXEL 0x00FFFFFF +#define BF_PXP_AS_CLRKEYHIGH_PIXEL(v) \ + (((v) << 0) & BM_PXP_AS_CLRKEYHIGH_PIXEL) + +#define HW_PXP_CSC1_COEF0 (0x000001a0) + +#define BM_PXP_CSC1_COEF0_YCBCR_MODE 0x80000000 +#define BM_PXP_CSC1_COEF0_BYPASS 0x40000000 +#define BM_PXP_CSC1_COEF0_RSVD1 0x20000000 +#define BP_PXP_CSC1_COEF0_C0 18 +#define BM_PXP_CSC1_COEF0_C0 0x1FFC0000 +#define BF_PXP_CSC1_COEF0_C0(v) \ + (((v) << 18) & BM_PXP_CSC1_COEF0_C0) +#define BP_PXP_CSC1_COEF0_UV_OFFSET 9 +#define BM_PXP_CSC1_COEF0_UV_OFFSET 0x0003FE00 +#define BF_PXP_CSC1_COEF0_UV_OFFSET(v) \ + (((v) << 9) & BM_PXP_CSC1_COEF0_UV_OFFSET) +#define BP_PXP_CSC1_COEF0_Y_OFFSET 0 +#define BM_PXP_CSC1_COEF0_Y_OFFSET 0x000001FF +#define BF_PXP_CSC1_COEF0_Y_OFFSET(v) \ + (((v) << 0) & BM_PXP_CSC1_COEF0_Y_OFFSET) + +#define HW_PXP_CSC1_COEF1 (0x000001b0) + +#define BP_PXP_CSC1_COEF1_RSVD1 27 +#define BM_PXP_CSC1_COEF1_RSVD1 0xF8000000 +#define BF_PXP_CSC1_COEF1_RSVD1(v) \ + (((v) << 27) & BM_PXP_CSC1_COEF1_RSVD1) +#define BP_PXP_CSC1_COEF1_C1 16 +#define BM_PXP_CSC1_COEF1_C1 0x07FF0000 +#define BF_PXP_CSC1_COEF1_C1(v) \ + (((v) << 16) & BM_PXP_CSC1_COEF1_C1) +#define BP_PXP_CSC1_COEF1_RSVD0 11 +#define BM_PXP_CSC1_COEF1_RSVD0 0x0000F800 +#define BF_PXP_CSC1_COEF1_RSVD0(v) \ + (((v) << 11) & BM_PXP_CSC1_COEF1_RSVD0) +#define BP_PXP_CSC1_COEF1_C4 0 +#define BM_PXP_CSC1_COEF1_C4 0x000007FF +#define BF_PXP_CSC1_COEF1_C4(v) \ + (((v) << 0) & BM_PXP_CSC1_COEF1_C4) + +#define HW_PXP_CSC1_COEF2 (0x000001c0) + +#define BP_PXP_CSC1_COEF2_RSVD1 27 +#define BM_PXP_CSC1_COEF2_RSVD1 0xF8000000 +#define BF_PXP_CSC1_COEF2_RSVD1(v) \ + (((v) << 27) & BM_PXP_CSC1_COEF2_RSVD1) +#define BP_PXP_CSC1_COEF2_C2 16 +#define BM_PXP_CSC1_COEF2_C2 0x07FF0000 +#define BF_PXP_CSC1_COEF2_C2(v) \ + (((v) << 16) & BM_PXP_CSC1_COEF2_C2) +#define BP_PXP_CSC1_COEF2_RSVD0 11 +#define BM_PXP_CSC1_COEF2_RSVD0 0x0000F800 +#define BF_PXP_CSC1_COEF2_RSVD0(v) \ + (((v) << 11) & BM_PXP_CSC1_COEF2_RSVD0) +#define BP_PXP_CSC1_COEF2_C3 0 +#define BM_PXP_CSC1_COEF2_C3 0x000007FF +#define BF_PXP_CSC1_COEF2_C3(v) \ + (((v) << 0) & BM_PXP_CSC1_COEF2_C3) + +#define HW_PXP_CSC2_CTRL (0x000001d0) + +#define BP_PXP_CSC2_CTRL_RSVD 3 +#define BM_PXP_CSC2_CTRL_RSVD 0xFFFFFFF8 +#define BF_PXP_CSC2_CTRL_RSVD(v) \ + (((v) << 3) & BM_PXP_CSC2_CTRL_RSVD) +#define BP_PXP_CSC2_CTRL_CSC_MODE 1 +#define BM_PXP_CSC2_CTRL_CSC_MODE 0x00000006 +#define BF_PXP_CSC2_CTRL_CSC_MODE(v) \ + (((v) << 1) & BM_PXP_CSC2_CTRL_CSC_MODE) +#define BV_PXP_CSC2_CTRL_CSC_MODE__YUV2RGB 0x0 +#define BV_PXP_CSC2_CTRL_CSC_MODE__YCbCr2RGB 0x1 +#define BV_PXP_CSC2_CTRL_CSC_MODE__RGB2YUV 0x2 +#define BV_PXP_CSC2_CTRL_CSC_MODE__RGB2YCbCr 0x3 +#define BM_PXP_CSC2_CTRL_BYPASS 0x00000001 + +#define HW_PXP_CSC2_COEF0 (0x000001e0) + +#define BP_PXP_CSC2_COEF0_RSVD1 27 +#define BM_PXP_CSC2_COEF0_RSVD1 0xF8000000 +#define BF_PXP_CSC2_COEF0_RSVD1(v) \ + (((v) << 27) & BM_PXP_CSC2_COEF0_RSVD1) +#define BP_PXP_CSC2_COEF0_A2 16 +#define BM_PXP_CSC2_COEF0_A2 0x07FF0000 +#define BF_PXP_CSC2_COEF0_A2(v) \ + (((v) << 16) & BM_PXP_CSC2_COEF0_A2) +#define BP_PXP_CSC2_COEF0_RSVD0 11 +#define BM_PXP_CSC2_COEF0_RSVD0 0x0000F800 +#define BF_PXP_CSC2_COEF0_RSVD0(v) \ + (((v) << 11) & BM_PXP_CSC2_COEF0_RSVD0) +#define BP_PXP_CSC2_COEF0_A1 0 +#define BM_PXP_CSC2_COEF0_A1 0x000007FF +#define BF_PXP_CSC2_COEF0_A1(v) \ + (((v) << 0) & BM_PXP_CSC2_COEF0_A1) + +#define HW_PXP_CSC2_COEF1 (0x000001f0) + +#define BP_PXP_CSC2_COEF1_RSVD1 27 +#define BM_PXP_CSC2_COEF1_RSVD1 0xF8000000 +#define BF_PXP_CSC2_COEF1_RSVD1(v) \ + (((v) << 27) & BM_PXP_CSC2_COEF1_RSVD1) +#define BP_PXP_CSC2_COEF1_B1 16 +#define BM_PXP_CSC2_COEF1_B1 0x07FF0000 +#define BF_PXP_CSC2_COEF1_B1(v) \ + (((v) << 16) & BM_PXP_CSC2_COEF1_B1) +#define BP_PXP_CSC2_COEF1_RSVD0 11 +#define BM_PXP_CSC2_COEF1_RSVD0 0x0000F800 +#define BF_PXP_CSC2_COEF1_RSVD0(v) \ + (((v) << 11) & BM_PXP_CSC2_COEF1_RSVD0) +#define BP_PXP_CSC2_COEF1_A3 0 +#define BM_PXP_CSC2_COEF1_A3 0x000007FF +#define BF_PXP_CSC2_COEF1_A3(v) \ + (((v) << 0) & BM_PXP_CSC2_COEF1_A3) + +#define HW_PXP_CSC2_COEF2 (0x00000200) + +#define BP_PXP_CSC2_COEF2_RSVD1 27 +#define BM_PXP_CSC2_COEF2_RSVD1 0xF8000000 +#define BF_PXP_CSC2_COEF2_RSVD1(v) \ + (((v) << 27) & BM_PXP_CSC2_COEF2_RSVD1) +#define BP_PXP_CSC2_COEF2_B3 16 +#define BM_PXP_CSC2_COEF2_B3 0x07FF0000 +#define BF_PXP_CSC2_COEF2_B3(v) \ + (((v) << 16) & BM_PXP_CSC2_COEF2_B3) +#define BP_PXP_CSC2_COEF2_RSVD0 11 +#define BM_PXP_CSC2_COEF2_RSVD0 0x0000F800 +#define BF_PXP_CSC2_COEF2_RSVD0(v) \ + (((v) << 11) & BM_PXP_CSC2_COEF2_RSVD0) +#define BP_PXP_CSC2_COEF2_B2 0 +#define BM_PXP_CSC2_COEF2_B2 0x000007FF +#define BF_PXP_CSC2_COEF2_B2(v) \ + (((v) << 0) & BM_PXP_CSC2_COEF2_B2) + +#define HW_PXP_CSC2_COEF3 (0x00000210) + +#define BP_PXP_CSC2_COEF3_RSVD1 27 +#define BM_PXP_CSC2_COEF3_RSVD1 0xF8000000 +#define BF_PXP_CSC2_COEF3_RSVD1(v) \ + (((v) << 27) & BM_PXP_CSC2_COEF3_RSVD1) +#define BP_PXP_CSC2_COEF3_C2 16 +#define BM_PXP_CSC2_COEF3_C2 0x07FF0000 +#define BF_PXP_CSC2_COEF3_C2(v) \ + (((v) << 16) & BM_PXP_CSC2_COEF3_C2) +#define BP_PXP_CSC2_COEF3_RSVD0 11 +#define BM_PXP_CSC2_COEF3_RSVD0 0x0000F800 +#define BF_PXP_CSC2_COEF3_RSVD0(v) \ + (((v) << 11) & BM_PXP_CSC2_COEF3_RSVD0) +#define BP_PXP_CSC2_COEF3_C1 0 +#define BM_PXP_CSC2_COEF3_C1 0x000007FF +#define BF_PXP_CSC2_COEF3_C1(v) \ + (((v) << 0) & BM_PXP_CSC2_COEF3_C1) + +#define HW_PXP_CSC2_COEF4 (0x00000220) + +#define BP_PXP_CSC2_COEF4_RSVD1 25 +#define BM_PXP_CSC2_COEF4_RSVD1 0xFE000000 +#define BF_PXP_CSC2_COEF4_RSVD1(v) \ + (((v) << 25) & BM_PXP_CSC2_COEF4_RSVD1) +#define BP_PXP_CSC2_COEF4_D1 16 +#define BM_PXP_CSC2_COEF4_D1 0x01FF0000 +#define BF_PXP_CSC2_COEF4_D1(v) \ + (((v) << 16) & BM_PXP_CSC2_COEF4_D1) +#define BP_PXP_CSC2_COEF4_RSVD0 11 +#define BM_PXP_CSC2_COEF4_RSVD0 0x0000F800 +#define BF_PXP_CSC2_COEF4_RSVD0(v) \ + (((v) << 11) & BM_PXP_CSC2_COEF4_RSVD0) +#define BP_PXP_CSC2_COEF4_C3 0 +#define BM_PXP_CSC2_COEF4_C3 0x000007FF +#define BF_PXP_CSC2_COEF4_C3(v) \ + (((v) << 0) & BM_PXP_CSC2_COEF4_C3) + +#define HW_PXP_CSC2_COEF5 (0x00000230) + +#define BP_PXP_CSC2_COEF5_RSVD1 25 +#define BM_PXP_CSC2_COEF5_RSVD1 0xFE000000 +#define BF_PXP_CSC2_COEF5_RSVD1(v) \ + (((v) << 25) & BM_PXP_CSC2_COEF5_RSVD1) +#define BP_PXP_CSC2_COEF5_D3 16 +#define BM_PXP_CSC2_COEF5_D3 0x01FF0000 +#define BF_PXP_CSC2_COEF5_D3(v) \ + (((v) << 16) & BM_PXP_CSC2_COEF5_D3) +#define BP_PXP_CSC2_COEF5_RSVD0 9 +#define BM_PXP_CSC2_COEF5_RSVD0 0x0000FE00 +#define BF_PXP_CSC2_COEF5_RSVD0(v) \ + (((v) << 9) & BM_PXP_CSC2_COEF5_RSVD0) +#define BP_PXP_CSC2_COEF5_D2 0 +#define BM_PXP_CSC2_COEF5_D2 0x000001FF +#define BF_PXP_CSC2_COEF5_D2(v) \ + (((v) << 0) & BM_PXP_CSC2_COEF5_D2) + +#define HW_PXP_LUT_CTRL (0x00000240) + +#define BM_PXP_LUT_CTRL_BYPASS 0x80000000 +#define BP_PXP_LUT_CTRL_RSVD3 26 +#define BM_PXP_LUT_CTRL_RSVD3 0x7C000000 +#define BF_PXP_LUT_CTRL_RSVD3(v) \ + (((v) << 26) & BM_PXP_LUT_CTRL_RSVD3) +#define BP_PXP_LUT_CTRL_LOOKUP_MODE 24 +#define BM_PXP_LUT_CTRL_LOOKUP_MODE 0x03000000 +#define BF_PXP_LUT_CTRL_LOOKUP_MODE(v) \ + (((v) << 24) & BM_PXP_LUT_CTRL_LOOKUP_MODE) +#define BV_PXP_LUT_CTRL_LOOKUP_MODE__CACHE_RGB565 0x0 +#define BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_Y8 0x1 +#define BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_RGB444 0x2 +#define BV_PXP_LUT_CTRL_LOOKUP_MODE__DIRECT_RGB454 0x3 +#define BP_PXP_LUT_CTRL_RSVD2 18 +#define BM_PXP_LUT_CTRL_RSVD2 0x00FC0000 +#define BF_PXP_LUT_CTRL_RSVD2(v) \ + (((v) << 18) & BM_PXP_LUT_CTRL_RSVD2) +#define BP_PXP_LUT_CTRL_OUT_MODE 16 +#define BM_PXP_LUT_CTRL_OUT_MODE 0x00030000 +#define BF_PXP_LUT_CTRL_OUT_MODE(v) \ + (((v) << 16) & BM_PXP_LUT_CTRL_OUT_MODE) +#define BV_PXP_LUT_CTRL_OUT_MODE__RESERVED 0x0 +#define BV_PXP_LUT_CTRL_OUT_MODE__Y8 0x1 +#define BV_PXP_LUT_CTRL_OUT_MODE__RGBW4444CFA 0x2 +#define BV_PXP_LUT_CTRL_OUT_MODE__RGB888 0x3 +#define BP_PXP_LUT_CTRL_RSVD1 11 +#define BM_PXP_LUT_CTRL_RSVD1 0x0000F800 +#define BF_PXP_LUT_CTRL_RSVD1(v) \ + (((v) << 11) & BM_PXP_LUT_CTRL_RSVD1) +#define BM_PXP_LUT_CTRL_SEL_8KB 0x00000400 +#define BM_PXP_LUT_CTRL_LRU_UPD 0x00000200 +#define BM_PXP_LUT_CTRL_INVALID 0x00000100 +#define BP_PXP_LUT_CTRL_RSVD0 1 +#define BM_PXP_LUT_CTRL_RSVD0 0x000000FE +#define BF_PXP_LUT_CTRL_RSVD0(v) \ + (((v) << 1) & BM_PXP_LUT_CTRL_RSVD0) +#define BM_PXP_LUT_CTRL_DMA_START 0x00000001 + +#define HW_PXP_LUT_ADDR (0x00000250) + +#define BM_PXP_LUT_ADDR_RSVD2 0x80000000 +#define BP_PXP_LUT_ADDR_NUM_BYTES 16 +#define BM_PXP_LUT_ADDR_NUM_BYTES 0x7FFF0000 +#define BF_PXP_LUT_ADDR_NUM_BYTES(v) \ + (((v) << 16) & BM_PXP_LUT_ADDR_NUM_BYTES) +#define BP_PXP_LUT_ADDR_RSVD1 14 +#define BM_PXP_LUT_ADDR_RSVD1 0x0000C000 +#define BF_PXP_LUT_ADDR_RSVD1(v) \ + (((v) << 14) & BM_PXP_LUT_ADDR_RSVD1) +#define BP_PXP_LUT_ADDR_ADDR 0 +#define BM_PXP_LUT_ADDR_ADDR 0x00003FFF +#define BF_PXP_LUT_ADDR_ADDR(v) \ + (((v) << 0) & BM_PXP_LUT_ADDR_ADDR) + +#define HW_PXP_LUT_DATA (0x00000260) + +#define BP_PXP_LUT_DATA_DATA 0 +#define BM_PXP_LUT_DATA_DATA 0xFFFFFFFF +#define BF_PXP_LUT_DATA_DATA(v) (v) + +#define HW_PXP_LUT_EXTMEM (0x00000270) + +#define BP_PXP_LUT_EXTMEM_ADDR 0 +#define BM_PXP_LUT_EXTMEM_ADDR 0xFFFFFFFF +#define BF_PXP_LUT_EXTMEM_ADDR(v) (v) + +#define HW_PXP_CFA (0x00000280) + +#define BP_PXP_CFA_DATA 0 +#define BM_PXP_CFA_DATA 0xFFFFFFFF +#define BF_PXP_CFA_DATA(v) (v) + +#define HW_PXP_HIST_CTRL (0x00000290) + +#define BP_PXP_HIST_CTRL_RSVD 6 +#define BM_PXP_HIST_CTRL_RSVD 0xFFFFFFC0 +#define BF_PXP_HIST_CTRL_RSVD(v) \ + (((v) << 6) & BM_PXP_HIST_CTRL_RSVD) +#define BP_PXP_HIST_CTRL_PANEL_MODE 4 +#define BM_PXP_HIST_CTRL_PANEL_MODE 0x00000030 +#define BF_PXP_HIST_CTRL_PANEL_MODE(v) \ + (((v) << 4) & BM_PXP_HIST_CTRL_PANEL_MODE) +#define BV_PXP_HIST_CTRL_PANEL_MODE__GRAY4 0x0 +#define BV_PXP_HIST_CTRL_PANEL_MODE__GRAY8 0x1 +#define BV_PXP_HIST_CTRL_PANEL_MODE__GRAY16 0x2 +#define BV_PXP_HIST_CTRL_PANEL_MODE__GRAY32 0x3 +#define BP_PXP_HIST_CTRL_STATUS 0 +#define BM_PXP_HIST_CTRL_STATUS 0x0000000F +#define BF_PXP_HIST_CTRL_STATUS(v) \ + (((v) << 0) & BM_PXP_HIST_CTRL_STATUS) + +#define HW_PXP_HIST2_PARAM (0x000002a0) + +#define BP_PXP_HIST2_PARAM_RSVD 16 +#define BM_PXP_HIST2_PARAM_RSVD 0xFFFF0000 +#define BF_PXP_HIST2_PARAM_RSVD(v) \ + (((v) << 16) & BM_PXP_HIST2_PARAM_RSVD) +#define BP_PXP_HIST2_PARAM_RSVD1 13 +#define BM_PXP_HIST2_PARAM_RSVD1 0x0000E000 +#define BF_PXP_HIST2_PARAM_RSVD1(v) \ + (((v) << 13) & BM_PXP_HIST2_PARAM_RSVD1) +#define BP_PXP_HIST2_PARAM_VALUE1 8 +#define BM_PXP_HIST2_PARAM_VALUE1 0x00001F00 +#define BF_PXP_HIST2_PARAM_VALUE1(v) \ + (((v) << 8) & BM_PXP_HIST2_PARAM_VALUE1) +#define BP_PXP_HIST2_PARAM_RSVD0 5 +#define BM_PXP_HIST2_PARAM_RSVD0 0x000000E0 +#define BF_PXP_HIST2_PARAM_RSVD0(v) \ + (((v) << 5) & BM_PXP_HIST2_PARAM_RSVD0) +#define BP_PXP_HIST2_PARAM_VALUE0 0 +#define BM_PXP_HIST2_PARAM_VALUE0 0x0000001F +#define BF_PXP_HIST2_PARAM_VALUE0(v) \ + (((v) << 0) & BM_PXP_HIST2_PARAM_VALUE0) + +#define HW_PXP_HIST4_PARAM (0x000002b0) + +#define BP_PXP_HIST4_PARAM_RSVD3 29 +#define BM_PXP_HIST4_PARAM_RSVD3 0xE0000000 +#define BF_PXP_HIST4_PARAM_RSVD3(v) \ + (((v) << 29) & BM_PXP_HIST4_PARAM_RSVD3) +#define BP_PXP_HIST4_PARAM_VALUE3 24 +#define BM_PXP_HIST4_PARAM_VALUE3 0x1F000000 +#define BF_PXP_HIST4_PARAM_VALUE3(v) \ + (((v) << 24) & BM_PXP_HIST4_PARAM_VALUE3) +#define BP_PXP_HIST4_PARAM_RSVD2 21 +#define BM_PXP_HIST4_PARAM_RSVD2 0x00E00000 +#define BF_PXP_HIST4_PARAM_RSVD2(v) \ + (((v) << 21) & BM_PXP_HIST4_PARAM_RSVD2) +#define BP_PXP_HIST4_PARAM_VALUE2 16 +#define BM_PXP_HIST4_PARAM_VALUE2 0x001F0000 +#define BF_PXP_HIST4_PARAM_VALUE2(v) \ + (((v) << 16) & BM_PXP_HIST4_PARAM_VALUE2) +#define BP_PXP_HIST4_PARAM_RSVD1 13 +#define BM_PXP_HIST4_PARAM_RSVD1 0x0000E000 +#define BF_PXP_HIST4_PARAM_RSVD1(v) \ + (((v) << 13) & BM_PXP_HIST4_PARAM_RSVD1) +#define BP_PXP_HIST4_PARAM_VALUE1 8 +#define BM_PXP_HIST4_PARAM_VALUE1 0x00001F00 +#define BF_PXP_HIST4_PARAM_VALUE1(v) \ + (((v) << 8) & BM_PXP_HIST4_PARAM_VALUE1) +#define BP_PXP_HIST4_PARAM_RSVD0 5 +#define BM_PXP_HIST4_PARAM_RSVD0 0x000000E0 +#define BF_PXP_HIST4_PARAM_RSVD0(v) \ + (((v) << 5) & BM_PXP_HIST4_PARAM_RSVD0) +#define BP_PXP_HIST4_PARAM_VALUE0 0 +#define BM_PXP_HIST4_PARAM_VALUE0 0x0000001F +#define BF_PXP_HIST4_PARAM_VALUE0(v) \ + (((v) << 0) & BM_PXP_HIST4_PARAM_VALUE0) + +#define HW_PXP_HIST8_PARAM0 (0x000002c0) + +#define BP_PXP_HIST8_PARAM0_RSVD3 29 +#define BM_PXP_HIST8_PARAM0_RSVD3 0xE0000000 +#define BF_PXP_HIST8_PARAM0_RSVD3(v) \ + (((v) << 29) & BM_PXP_HIST8_PARAM0_RSVD3) +#define BP_PXP_HIST8_PARAM0_VALUE3 24 +#define BM_PXP_HIST8_PARAM0_VALUE3 0x1F000000 +#define BF_PXP_HIST8_PARAM0_VALUE3(v) \ + (((v) << 24) & BM_PXP_HIST8_PARAM0_VALUE3) +#define BP_PXP_HIST8_PARAM0_RSVD2 21 +#define BM_PXP_HIST8_PARAM0_RSVD2 0x00E00000 +#define BF_PXP_HIST8_PARAM0_RSVD2(v) \ + (((v) << 21) & BM_PXP_HIST8_PARAM0_RSVD2) +#define BP_PXP_HIST8_PARAM0_VALUE2 16 +#define BM_PXP_HIST8_PARAM0_VALUE2 0x001F0000 +#define BF_PXP_HIST8_PARAM0_VALUE2(v) \ + (((v) << 16) & BM_PXP_HIST8_PARAM0_VALUE2) +#define BP_PXP_HIST8_PARAM0_RSVD1 13 +#define BM_PXP_HIST8_PARAM0_RSVD1 0x0000E000 +#define BF_PXP_HIST8_PARAM0_RSVD1(v) \ + (((v) << 13) & BM_PXP_HIST8_PARAM0_RSVD1) +#define BP_PXP_HIST8_PARAM0_VALUE1 8 +#define BM_PXP_HIST8_PARAM0_VALUE1 0x00001F00 +#define BF_PXP_HIST8_PARAM0_VALUE1(v) \ + (((v) << 8) & BM_PXP_HIST8_PARAM0_VALUE1) +#define BP_PXP_HIST8_PARAM0_RSVD0 5 +#define BM_PXP_HIST8_PARAM0_RSVD0 0x000000E0 +#define BF_PXP_HIST8_PARAM0_RSVD0(v) \ + (((v) << 5) & BM_PXP_HIST8_PARAM0_RSVD0) +#define BP_PXP_HIST8_PARAM0_VALUE0 0 +#define BM_PXP_HIST8_PARAM0_VALUE0 0x0000001F +#define BF_PXP_HIST8_PARAM0_VALUE0(v) \ + (((v) << 0) & BM_PXP_HIST8_PARAM0_VALUE0) + +#define HW_PXP_HIST8_PARAM1 (0x000002d0) + +#define BP_PXP_HIST8_PARAM1_RSVD7 29 +#define BM_PXP_HIST8_PARAM1_RSVD7 0xE0000000 +#define BF_PXP_HIST8_PARAM1_RSVD7(v) \ + (((v) << 29) & BM_PXP_HIST8_PARAM1_RSVD7) +#define BP_PXP_HIST8_PARAM1_VALUE7 24 +#define BM_PXP_HIST8_PARAM1_VALUE7 0x1F000000 +#define BF_PXP_HIST8_PARAM1_VALUE7(v) \ + (((v) << 24) & BM_PXP_HIST8_PARAM1_VALUE7) +#define BP_PXP_HIST8_PARAM1_RSVD6 21 +#define BM_PXP_HIST8_PARAM1_RSVD6 0x00E00000 +#define BF_PXP_HIST8_PARAM1_RSVD6(v) \ + (((v) << 21) & BM_PXP_HIST8_PARAM1_RSVD6) +#define BP_PXP_HIST8_PARAM1_VALUE6 16 +#define BM_PXP_HIST8_PARAM1_VALUE6 0x001F0000 +#define BF_PXP_HIST8_PARAM1_VALUE6(v) \ + (((v) << 16) & BM_PXP_HIST8_PARAM1_VALUE6) +#define BP_PXP_HIST8_PARAM1_RSVD5 13 +#define BM_PXP_HIST8_PARAM1_RSVD5 0x0000E000 +#define BF_PXP_HIST8_PARAM1_RSVD5(v) \ + (((v) << 13) & BM_PXP_HIST8_PARAM1_RSVD5) +#define BP_PXP_HIST8_PARAM1_VALUE5 8 +#define BM_PXP_HIST8_PARAM1_VALUE5 0x00001F00 +#define BF_PXP_HIST8_PARAM1_VALUE5(v) \ + (((v) << 8) & BM_PXP_HIST8_PARAM1_VALUE5) +#define BP_PXP_HIST8_PARAM1_RSVD4 5 +#define BM_PXP_HIST8_PARAM1_RSVD4 0x000000E0 +#define BF_PXP_HIST8_PARAM1_RSVD4(v) \ + (((v) << 5) & BM_PXP_HIST8_PARAM1_RSVD4) +#define BP_PXP_HIST8_PARAM1_VALUE4 0 +#define BM_PXP_HIST8_PARAM1_VALUE4 0x0000001F +#define BF_PXP_HIST8_PARAM1_VALUE4(v) \ + (((v) << 0) & BM_PXP_HIST8_PARAM1_VALUE4) + +#define HW_PXP_HIST16_PARAM0 (0x000002e0) + +#define BP_PXP_HIST16_PARAM0_RSVD3 29 +#define BM_PXP_HIST16_PARAM0_RSVD3 0xE0000000 +#define BF_PXP_HIST16_PARAM0_RSVD3(v) \ + (((v) << 29) & BM_PXP_HIST16_PARAM0_RSVD3) +#define BP_PXP_HIST16_PARAM0_VALUE3 24 +#define BM_PXP_HIST16_PARAM0_VALUE3 0x1F000000 +#define BF_PXP_HIST16_PARAM0_VALUE3(v) \ + (((v) << 24) & BM_PXP_HIST16_PARAM0_VALUE3) +#define BP_PXP_HIST16_PARAM0_RSVD2 21 +#define BM_PXP_HIST16_PARAM0_RSVD2 0x00E00000 +#define BF_PXP_HIST16_PARAM0_RSVD2(v) \ + (((v) << 21) & BM_PXP_HIST16_PARAM0_RSVD2) +#define BP_PXP_HIST16_PARAM0_VALUE2 16 +#define BM_PXP_HIST16_PARAM0_VALUE2 0x001F0000 +#define BF_PXP_HIST16_PARAM0_VALUE2(v) \ + (((v) << 16) & BM_PXP_HIST16_PARAM0_VALUE2) +#define BP_PXP_HIST16_PARAM0_RSVD1 13 +#define BM_PXP_HIST16_PARAM0_RSVD1 0x0000E000 +#define BF_PXP_HIST16_PARAM0_RSVD1(v) \ + (((v) << 13) & BM_PXP_HIST16_PARAM0_RSVD1) +#define BP_PXP_HIST16_PARAM0_VALUE1 8 +#define BM_PXP_HIST16_PARAM0_VALUE1 0x00001F00 +#define BF_PXP_HIST16_PARAM0_VALUE1(v) \ + (((v) << 8) & BM_PXP_HIST16_PARAM0_VALUE1) +#define BP_PXP_HIST16_PARAM0_RSVD0 5 +#define BM_PXP_HIST16_PARAM0_RSVD0 0x000000E0 +#define BF_PXP_HIST16_PARAM0_RSVD0(v) \ + (((v) << 5) & BM_PXP_HIST16_PARAM0_RSVD0) +#define BP_PXP_HIST16_PARAM0_VALUE0 0 +#define BM_PXP_HIST16_PARAM0_VALUE0 0x0000001F +#define BF_PXP_HIST16_PARAM0_VALUE0(v) \ + (((v) << 0) & BM_PXP_HIST16_PARAM0_VALUE0) + +#define HW_PXP_HIST16_PARAM1 (0x000002f0) + +#define BP_PXP_HIST16_PARAM1_RSVD7 29 +#define BM_PXP_HIST16_PARAM1_RSVD7 0xE0000000 +#define BF_PXP_HIST16_PARAM1_RSVD7(v) \ + (((v) << 29) & BM_PXP_HIST16_PARAM1_RSVD7) +#define BP_PXP_HIST16_PARAM1_VALUE7 24 +#define BM_PXP_HIST16_PARAM1_VALUE7 0x1F000000 +#define BF_PXP_HIST16_PARAM1_VALUE7(v) \ + (((v) << 24) & BM_PXP_HIST16_PARAM1_VALUE7) +#define BP_PXP_HIST16_PARAM1_RSVD6 21 +#define BM_PXP_HIST16_PARAM1_RSVD6 0x00E00000 +#define BF_PXP_HIST16_PARAM1_RSVD6(v) \ + (((v) << 21) & BM_PXP_HIST16_PARAM1_RSVD6) +#define BP_PXP_HIST16_PARAM1_VALUE6 16 +#define BM_PXP_HIST16_PARAM1_VALUE6 0x001F0000 +#define BF_PXP_HIST16_PARAM1_VALUE6(v) \ + (((v) << 16) & BM_PXP_HIST16_PARAM1_VALUE6) +#define BP_PXP_HIST16_PARAM1_RSVD5 13 +#define BM_PXP_HIST16_PARAM1_RSVD5 0x0000E000 +#define BF_PXP_HIST16_PARAM1_RSVD5(v) \ + (((v) << 13) & BM_PXP_HIST16_PARAM1_RSVD5) +#define BP_PXP_HIST16_PARAM1_VALUE5 8 +#define BM_PXP_HIST16_PARAM1_VALUE5 0x00001F00 +#define BF_PXP_HIST16_PARAM1_VALUE5(v) \ + (((v) << 8) & BM_PXP_HIST16_PARAM1_VALUE5) +#define BP_PXP_HIST16_PARAM1_RSVD4 5 +#define BM_PXP_HIST16_PARAM1_RSVD4 0x000000E0 +#define BF_PXP_HIST16_PARAM1_RSVD4(v) \ + (((v) << 5) & BM_PXP_HIST16_PARAM1_RSVD4) +#define BP_PXP_HIST16_PARAM1_VALUE4 0 +#define BM_PXP_HIST16_PARAM1_VALUE4 0x0000001F +#define BF_PXP_HIST16_PARAM1_VALUE4(v) \ + (((v) << 0) & BM_PXP_HIST16_PARAM1_VALUE4) + +#define HW_PXP_HIST16_PARAM2 (0x00000300) + +#define BP_PXP_HIST16_PARAM2_RSVD11 29 +#define BM_PXP_HIST16_PARAM2_RSVD11 0xE0000000 +#define BF_PXP_HIST16_PARAM2_RSVD11(v) \ + (((v) << 29) & BM_PXP_HIST16_PARAM2_RSVD11) +#define BP_PXP_HIST16_PARAM2_VALUE11 24 +#define BM_PXP_HIST16_PARAM2_VALUE11 0x1F000000 +#define BF_PXP_HIST16_PARAM2_VALUE11(v) \ + (((v) << 24) & BM_PXP_HIST16_PARAM2_VALUE11) +#define BP_PXP_HIST16_PARAM2_RSVD10 21 +#define BM_PXP_HIST16_PARAM2_RSVD10 0x00E00000 +#define BF_PXP_HIST16_PARAM2_RSVD10(v) \ + (((v) << 21) & BM_PXP_HIST16_PARAM2_RSVD10) +#define BP_PXP_HIST16_PARAM2_VALUE10 16 +#define BM_PXP_HIST16_PARAM2_VALUE10 0x001F0000 +#define BF_PXP_HIST16_PARAM2_VALUE10(v) \ + (((v) << 16) & BM_PXP_HIST16_PARAM2_VALUE10) +#define BP_PXP_HIST16_PARAM2_RSVD9 13 +#define BM_PXP_HIST16_PARAM2_RSVD9 0x0000E000 +#define BF_PXP_HIST16_PARAM2_RSVD9(v) \ + (((v) << 13) & BM_PXP_HIST16_PARAM2_RSVD9) +#define BP_PXP_HIST16_PARAM2_VALUE9 8 +#define BM_PXP_HIST16_PARAM2_VALUE9 0x00001F00 +#define BF_PXP_HIST16_PARAM2_VALUE9(v) \ + (((v) << 8) & BM_PXP_HIST16_PARAM2_VALUE9) +#define BP_PXP_HIST16_PARAM2_RSVD8 5 +#define BM_PXP_HIST16_PARAM2_RSVD8 0x000000E0 +#define BF_PXP_HIST16_PARAM2_RSVD8(v) \ + (((v) << 5) & BM_PXP_HIST16_PARAM2_RSVD8) +#define BP_PXP_HIST16_PARAM2_VALUE8 0 +#define BM_PXP_HIST16_PARAM2_VALUE8 0x0000001F +#define BF_PXP_HIST16_PARAM2_VALUE8(v) \ + (((v) << 0) & BM_PXP_HIST16_PARAM2_VALUE8) + +#define HW_PXP_HIST16_PARAM3 (0x00000310) + +#define BP_PXP_HIST16_PARAM3_RSVD15 29 +#define BM_PXP_HIST16_PARAM3_RSVD15 0xE0000000 +#define BF_PXP_HIST16_PARAM3_RSVD15(v) \ + (((v) << 29) & BM_PXP_HIST16_PARAM3_RSVD15) +#define BP_PXP_HIST16_PARAM3_VALUE15 24 +#define BM_PXP_HIST16_PARAM3_VALUE15 0x1F000000 +#define BF_PXP_HIST16_PARAM3_VALUE15(v) \ + (((v) << 24) & BM_PXP_HIST16_PARAM3_VALUE15) +#define BP_PXP_HIST16_PARAM3_RSVD14 21 +#define BM_PXP_HIST16_PARAM3_RSVD14 0x00E00000 +#define BF_PXP_HIST16_PARAM3_RSVD14(v) \ + (((v) << 21) & BM_PXP_HIST16_PARAM3_RSVD14) +#define BP_PXP_HIST16_PARAM3_VALUE14 16 +#define BM_PXP_HIST16_PARAM3_VALUE14 0x001F0000 +#define BF_PXP_HIST16_PARAM3_VALUE14(v) \ + (((v) << 16) & BM_PXP_HIST16_PARAM3_VALUE14) +#define BP_PXP_HIST16_PARAM3_RSVD13 13 +#define BM_PXP_HIST16_PARAM3_RSVD13 0x0000E000 +#define BF_PXP_HIST16_PARAM3_RSVD13(v) \ + (((v) << 13) & BM_PXP_HIST16_PARAM3_RSVD13) +#define BP_PXP_HIST16_PARAM3_VALUE13 8 +#define BM_PXP_HIST16_PARAM3_VALUE13 0x00001F00 +#define BF_PXP_HIST16_PARAM3_VALUE13(v) \ + (((v) << 8) & BM_PXP_HIST16_PARAM3_VALUE13) +#define BP_PXP_HIST16_PARAM3_RSVD12 5 +#define BM_PXP_HIST16_PARAM3_RSVD12 0x000000E0 +#define BF_PXP_HIST16_PARAM3_RSVD12(v) \ + (((v) << 5) & BM_PXP_HIST16_PARAM3_RSVD12) +#define BP_PXP_HIST16_PARAM3_VALUE12 0 +#define BM_PXP_HIST16_PARAM3_VALUE12 0x0000001F +#define BF_PXP_HIST16_PARAM3_VALUE12(v) \ + (((v) << 0) & BM_PXP_HIST16_PARAM3_VALUE12) + +#define HW_PXP_POWER (0x00000320) + +#define BP_PXP_POWER_CTRL 12 +#define BM_PXP_POWER_CTRL 0xFFFFF000 +#define BF_PXP_POWER_CTRL(v) \ + (((v) << 12) & BM_PXP_POWER_CTRL) +#define BP_PXP_POWER_ROT_MEM_LP_STATE 9 +#define BM_PXP_POWER_ROT_MEM_LP_STATE 0x00000E00 +#define BF_PXP_POWER_ROT_MEM_LP_STATE(v) \ + (((v) << 9) & BM_PXP_POWER_ROT_MEM_LP_STATE) +#define BV_PXP_POWER_ROT_MEM_LP_STATE__NONE 0x0 +#define BV_PXP_POWER_ROT_MEM_LP_STATE__LS 0x1 +#define BV_PXP_POWER_ROT_MEM_LP_STATE__DS 0x2 +#define BV_PXP_POWER_ROT_MEM_LP_STATE__SD 0x4 +#define BP_PXP_POWER_LUT_LP_STATE_WAY1_BANKN 6 +#define BM_PXP_POWER_LUT_LP_STATE_WAY1_BANKN 0x000001C0 +#define BF_PXP_POWER_LUT_LP_STATE_WAY1_BANKN(v) \ + (((v) << 6) & BM_PXP_POWER_LUT_LP_STATE_WAY1_BANKN) +#define BV_PXP_POWER_LUT_LP_STATE_WAY1_BANKN__NONE 0x0 +#define BV_PXP_POWER_LUT_LP_STATE_WAY1_BANKN__LS 0x1 +#define BV_PXP_POWER_LUT_LP_STATE_WAY1_BANKN__DS 0x2 +#define BV_PXP_POWER_LUT_LP_STATE_WAY1_BANKN__SD 0x4 +#define BP_PXP_POWER_LUT_LP_STATE_WAY0_BANKN 3 +#define BM_PXP_POWER_LUT_LP_STATE_WAY0_BANKN 0x00000038 +#define BF_PXP_POWER_LUT_LP_STATE_WAY0_BANKN(v) \ + (((v) << 3) & BM_PXP_POWER_LUT_LP_STATE_WAY0_BANKN) +#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANKN__NONE 0x0 +#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANKN__LS 0x1 +#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANKN__DS 0x2 +#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANKN__SD 0x4 +#define BP_PXP_POWER_LUT_LP_STATE_WAY0_BANK0 0 +#define BM_PXP_POWER_LUT_LP_STATE_WAY0_BANK0 0x00000007 +#define BF_PXP_POWER_LUT_LP_STATE_WAY0_BANK0(v) \ + (((v) << 0) & BM_PXP_POWER_LUT_LP_STATE_WAY0_BANK0) +#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANK0__NONE 0x0 +#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANK0__LS 0x1 +#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANK0__DS 0x2 +#define BV_PXP_POWER_LUT_LP_STATE_WAY0_BANK0__SD 0x4 + +#define HW_PXP_NEXT (0x00000400) + +#define BP_PXP_NEXT_POINTER 2 +#define BM_PXP_NEXT_POINTER 0xFFFFFFFC +#define BF_PXP_NEXT_POINTER(v) \ + (((v) << 2) & BM_PXP_NEXT_POINTER) +#define BM_PXP_NEXT_RSVD 0x00000002 +#define BM_PXP_NEXT_ENABLED 0x00000001 + +#define HW_PXP_DEBUGCTRL (0x00000410) + +#define BP_PXP_DEBUGCTRL_RSVD 12 +#define BM_PXP_DEBUGCTRL_RSVD 0xFFFFF000 +#define BF_PXP_DEBUGCTRL_RSVD(v) \ + (((v) << 12) & BM_PXP_DEBUGCTRL_RSVD) +#define BP_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT 8 +#define BM_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT 0x00000F00 +#define BF_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT(v) \ + (((v) << 8) & BM_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT) +#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__NONE 0x0 +#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__MISS_CNT 0x1 +#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__HIT_CNT 0x2 +#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__LAT_CNT 0x4 +#define BV_PXP_DEBUGCTRL_LUT_CLR_STAT_CNT__MAX_LAT 0x8 +#define BP_PXP_DEBUGCTRL_SELECT 0 +#define BM_PXP_DEBUGCTRL_SELECT 0x000000FF +#define BF_PXP_DEBUGCTRL_SELECT(v) \ + (((v) << 0) & BM_PXP_DEBUGCTRL_SELECT) +#define BV_PXP_DEBUGCTRL_SELECT__NONE 0x0 +#define BV_PXP_DEBUGCTRL_SELECT__CTRL 0x1 +#define BV_PXP_DEBUGCTRL_SELECT__PSBUF 0x2 +#define BV_PXP_DEBUGCTRL_SELECT__PSBAX 0x3 +#define BV_PXP_DEBUGCTRL_SELECT__PSBAY 0x4 +#define BV_PXP_DEBUGCTRL_SELECT__ASBUF 0x5 +#define BV_PXP_DEBUGCTRL_SELECT__ROTATION 0x6 +#define BV_PXP_DEBUGCTRL_SELECT__OUTBUF0 0x7 +#define BV_PXP_DEBUGCTRL_SELECT__OUTBUF1 0x8 +#define BV_PXP_DEBUGCTRL_SELECT__OUTBUF2 0x9 +#define BV_PXP_DEBUGCTRL_SELECT__LUT_STAT 0x10 +#define BV_PXP_DEBUGCTRL_SELECT__LUT_MISS 0x11 +#define BV_PXP_DEBUGCTRL_SELECT__LUT_HIT 0x12 +#define BV_PXP_DEBUGCTRL_SELECT__LUT_LAT 0x13 +#define BV_PXP_DEBUGCTRL_SELECT__LUT_MAX_LAT 0x14 + +#define HW_PXP_DEBUG (0x00000420) + +#define BP_PXP_DEBUG_DATA 0 +#define BM_PXP_DEBUG_DATA 0xFFFFFFFF +#define BF_PXP_DEBUG_DATA(v) (v) + +#define HW_PXP_VERSION (0x00000430) + +#define BP_PXP_VERSION_MAJOR 24 +#define BM_PXP_VERSION_MAJOR 0xFF000000 +#define BF_PXP_VERSION_MAJOR(v) \ + (((v) << 24) & BM_PXP_VERSION_MAJOR) +#define BP_PXP_VERSION_MINOR 16 +#define BM_PXP_VERSION_MINOR 0x00FF0000 +#define BF_PXP_VERSION_MINOR(v) \ + (((v) << 16) & BM_PXP_VERSION_MINOR) +#define BP_PXP_VERSION_STEP 0 +#define BM_PXP_VERSION_STEP 0x0000FFFF +#define BF_PXP_VERSION_STEP(v) \ + (((v) << 0) & BM_PXP_VERSION_STEP) +#endif /* __ARCH_ARM___PXP_H */ diff -Nur linux-3.10.30/drivers/extcon/extcon-adc-jack.c linux-3.10.30-cubox-i/drivers/extcon/extcon-adc-jack.c --- linux-3.10.30/drivers/extcon/extcon-adc-jack.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/extcon/extcon-adc-jack.c 2014-03-08 20:33:31.000000000 +0100 @@ -87,7 +87,8 @@ { struct adc_jack_data *data = _data; - schedule_delayed_work(&data->handler, data->handling_delay); + queue_delayed_work(system_power_efficient_wq, + &data->handler, data->handling_delay); return IRQ_HANDLED; } diff -Nur linux-3.10.30/drivers/extcon/extcon-gpio.c linux-3.10.30-cubox-i/drivers/extcon/extcon-gpio.c --- linux-3.10.30/drivers/extcon/extcon-gpio.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/extcon/extcon-gpio.c 2014-03-08 20:33:31.000000000 +0100 @@ -56,7 +56,7 @@ { struct gpio_extcon_data *extcon_data = dev_id; - schedule_delayed_work(&extcon_data->work, + queue_delayed_work(system_power_efficient_wq, &extcon_data->work, extcon_data->debounce_jiffies); return IRQ_HANDLED; } diff -Nur linux-3.10.30/drivers/gator/Kconfig linux-3.10.30-cubox-i/drivers/gator/Kconfig --- linux-3.10.30/drivers/gator/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/Kconfig 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,33 @@ +config GATOR + tristate "Gator module for ARM's Streamline Performance Analyzer" + default m if (ARM || ARM64) + depends on PROFILING + depends on HIGH_RES_TIMERS + depends on LOCAL_TIMERS || !(ARM && SMP) + select TRACING + +config GATOR_WITH_MALI_SUPPORT + bool + +choice + prompt "Enable Mali GPU support in Gator" + depends on GATOR + optional + +config GATOR_MALI_400MP + bool "Mali-400MP" + select GATOR_WITH_MALI_SUPPORT + +config GATOR_MALI_T6XX + bool "Mali-T604 or Mali-T658" + select GATOR_WITH_MALI_SUPPORT + +endchoice + +config GATOR_MALI_PATH + string "Path to Mali driver" + depends on GATOR_WITH_MALI_SUPPORT + default "drivers/gpu/arm/mali400mp" + help + The gator code adds this to its include path so it can get the Mali + trace headers with: #include "linux/mali_linux_trace.h" diff -Nur linux-3.10.30/drivers/gator/LICENSE linux-3.10.30-cubox-i/drivers/gator/LICENSE --- linux-3.10.30/drivers/gator/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/LICENSE 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff -Nur linux-3.10.30/drivers/gator/Makefile linux-3.10.30-cubox-i/drivers/gator/Makefile --- linux-3.10.30/drivers/gator/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/Makefile 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,76 @@ +ifneq ($(KERNELRELEASE),) + +# Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c +# EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING + +CONFIG_GATOR ?= m +obj-$(CONFIG_GATOR) := gator.o + +gator-y := gator_main.o \ + gator_events_irq.o \ + gator_events_sched.o \ + gator_events_net.o \ + gator_events_block.o \ + gator_events_meminfo.o \ + gator_events_perf_pmu.o \ + gator_events_mmapped.o \ + +# Convert the old GATOR_WITH_MALI_SUPPORT to the new kernel flags +ifneq ($(GATOR_WITH_MALI_SUPPORT),) + CONFIG_GATOR_WITH_MALI_SUPPORT := y + ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) + CONFIG_GATOR_MALI_4XXMP := n + CONFIG_GATOR_MALI_T6XX := y + else + CONFIG_GATOR_MALI_4XXMP := y + CONFIG_GATOR_MALI_T6XX := n + endif + EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT) + ifneq ($(GATOR_MALI_INTERFACE_STYLE),) + EXTRA_CFLAGS += -DGATOR_MALI_INTERFACE_STYLE=$(GATOR_MALI_INTERFACE_STYLE) + endif +endif + +ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y) + ifeq ($(CONFIG_GATOR_MALI_T6XX),y) + gator-y += gator_events_mali_t6xx.o \ + gator_events_mali_t6xx_hw.o + include $(src)/mali_t6xx.mk + else + gator-y += gator_events_mali_4xx.o + endif + gator-y += gator_events_mali_common.o + + ifneq ($(CONFIG_GATOR_MALI_PATH),) + ccflags-y += -I$(CONFIG_GATOR_MALI_PATH) + endif + ccflags-$(CONFIG_GATOR_MALI_4XXMP) += -DMALI_SUPPORT=MALI_4xx + ccflags-$(CONFIG_GATOR_MALI_T6XX) += -DMALI_SUPPORT=MALI_T6xx +endif + +# GATOR_TEST controls whether to include (=1) or exclude (=0) test code. +GATOR_TEST ?= 0 +EXTRA_CFLAGS += -DGATOR_TEST=$(GATOR_TEST) + +gator-$(CONFIG_ARM) += gator_events_armv6.o \ + gator_events_armv7.o \ + gator_events_ccn-504.o \ + gator_events_l2c-310.o \ + gator_events_scorpion.o + +gator-$(CONFIG_ARM64) += gator_events_ccn-504.o + +else + +all: + @echo + @echo "usage:" + @echo " make -C M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules" + @echo + $(error) + +clean: + rm -f *.o .*.cmd modules.order Module.symvers gator.ko gator.mod.c + rm -rf .tmp_versions + +endif diff -Nur linux-3.10.30/drivers/gator/gator.h linux-3.10.30-cubox-i/drivers/gator/gator.h --- linux-3.10.30/drivers/gator/gator.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator.h 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,142 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 GATOR_H_ +#define GATOR_H_ + +#include +#include +#include +#include + +#define GATOR_PERF_SUPPORT LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) +#define GATOR_PERF_PMU_SUPPORT GATOR_PERF_SUPPORT && defined(CONFIG_PERF_EVENTS) && (!(defined(__arm__) || defined(__aarch64__)) || defined(CONFIG_HW_PERF_EVENTS)) +#define GATOR_NO_PERF_SUPPORT (!(GATOR_PERF_SUPPORT)) +#define GATOR_CPU_FREQ_SUPPORT (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) && defined(CONFIG_CPU_FREQ) +#define GATOR_IKS_SUPPORT defined(CONFIG_BL_SWITCHER) + +// cpu ids +#define ARM1136 0xb36 +#define ARM1156 0xb56 +#define ARM1176 0xb76 +#define ARM11MPCORE 0xb02 +#define CORTEX_A5 0xc05 +#define CORTEX_A7 0xc07 +#define CORTEX_A8 0xc08 +#define CORTEX_A9 0xc09 +#define CORTEX_A12 0xc0d +#define CORTEX_A15 0xc0f +#define SCORPION 0x00f +#define SCORPIONMP 0x02d +#define KRAITSIM 0x049 +#define KRAIT 0x04d +#define KRAIT_S4_PRO 0x06f +#define CORTEX_A53 0xd03 +#define CORTEX_A57 0xd07 +#define AARCH64 0xd0f +#define OTHER 0xfff + +#define MAXSIZE_CORE_NAME 32 + +struct gator_cpu { + const int cpuid; + // Human readable name + const char core_name[MAXSIZE_CORE_NAME]; + // Perf PMU name + const char * const pmu_name; + // gatorfs event name + const char * const pmnc_name; + // compatible from Documentation/devicetree/bindings/arm/cpus.txt + const char * const dt_name; + const int pmnc_counters; +}; + +const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid); +const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name); + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, + char const *name, + const struct file_operations *fops, int perm); + +struct dentry *gatorfs_mkdir(struct super_block *sb, struct dentry *root, + char const *name); + +int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val); + +int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val); + +void gator_op_create_files(struct super_block *sb, struct dentry *root); + +/****************************************************************************** + * Tracepoints + ******************************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +# error Kernels prior to 2.6.32 not supported +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + static void probe_##probe_name(PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + register_trace_##probe_name(probe_##probe_name) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + unregister_trace_##probe_name(probe_##probe_name) +#else +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + static void probe_##probe_name(void *data, PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + register_trace_##probe_name(probe_##probe_name, NULL) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + unregister_trace_##probe_name(probe_##probe_name, NULL) +#endif + +/****************************************************************************** + * Events + ******************************************************************************/ +struct gator_interface { + void (*shutdown)(void); // Complementary function to init + int (*create_files)(struct super_block *sb, struct dentry *root); + int (*start)(void); + void (*stop)(void); // Complementary function to start + int (*online)(int **buffer, bool migrate); + int (*offline)(int **buffer, bool migrate); + void (*online_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu' + void (*offline_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu' + int (*read)(int **buffer); + int (*read64)(long long **buffer); + int (*read_proc)(long long **buffer, struct task_struct *); + struct list_head list; +}; + +int gator_events_install(struct gator_interface *interface); +int gator_events_get_key(void); +u32 gator_cpuid(void); + +void gator_backtrace_handler(struct pt_regs *const regs); + +#if !GATOR_IKS_SUPPORT + +#define get_physical_cpu() smp_processor_id() +#define lcpu_to_pcpu(lcpu) lcpu +#define pcpu_to_lcpu(pcpu) pcpu + +#else + +#define get_physical_cpu() lcpu_to_pcpu(get_logical_cpu()) +int lcpu_to_pcpu(const int lcpu); +int pcpu_to_lcpu(const int pcpu); + +#endif + +#define get_logical_cpu() smp_processor_id() +#define on_primary_core() (get_logical_cpu() == 0) + +#endif // GATOR_H_ diff -Nur linux-3.10.30/drivers/gator/gator_annotate.c linux-3.10.30-cubox-i/drivers/gator/gator_annotate.c --- linux-3.10.30/drivers/gator/gator_annotate.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_annotate.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,186 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(annotate_lock); +static bool collect_annotations = false; + +static int annotate_copy(struct file *file, char const __user *buf, size_t count) +{ + int cpu = 0; + int write = per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF]; + + if (file == NULL) { + // copy from kernel + memcpy(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count); + } else { + // copy from user space + if (copy_from_user(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count) != 0) + return -1; + } + per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF] = (write + count) & gator_buffer_mask[ANNOTATE_BUF]; + + return 0; +} + +static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count_orig, loff_t *offset) +{ + int pid, cpu, header_size, available, contiguous, length1, length2, size, count = count_orig & 0x7fffffff; + bool interrupt_context; + + if (*offset) { + return -EINVAL; + } + + interrupt_context = in_interrupt(); + // Annotations are not supported in interrupt context, but may work if you comment out the the next four lines of code. + // By doing so, annotations in interrupt context can result in deadlocks and lost data. + if (interrupt_context) { + printk(KERN_WARNING "gator: Annotations are not supported in interrupt context. Edit gator_annotate.c in the gator driver to enable annotations in interrupt context.\n"); + return -EINVAL; + } + + retry: + // synchronize between cores and with collect_annotations + spin_lock(&annotate_lock); + + if (!collect_annotations) { + // Not collecting annotations, tell the caller everything was written + size = count_orig; + goto annotate_write_out; + } + + // Annotation only uses a single per-cpu buffer as the data must be in order to the engine + cpu = 0; + + if (current == NULL) { + pid = 0; + } else { + pid = current->pid; + } + + // determine total size of the payload + header_size = MAXSIZE_PACK32 * 3 + MAXSIZE_PACK64; + available = buffer_bytes_available(cpu, ANNOTATE_BUF) - header_size; + size = count < available ? count : available; + + if (size <= 0) { + // Buffer is full, wait until space is available + spin_unlock(&annotate_lock); + + // Drop the annotation as blocking is not allowed in interrupt context + if (interrupt_context) { + return -EINVAL; + } + + wait_event_interruptible(gator_annotate_wait, buffer_bytes_available(cpu, ANNOTATE_BUF) > header_size || !collect_annotations); + + // Check to see if a signal is pending + if (signal_pending(current)) { + return -EINTR; + } + + goto retry; + } + + // synchronize shared variables annotateBuf and annotatePos + if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF]) { + u64 time = gator_get_time(); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu()); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid); + gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, time); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, size); + + // determine the sizes to capture, length1 + length2 will equal size + contiguous = contiguous_space_available(cpu, ANNOTATE_BUF); + if (size < contiguous) { + length1 = size; + length2 = 0; + } else { + length1 = contiguous; + length2 = size - contiguous; + } + + if (annotate_copy(file, buf, length1) != 0) { + size = -EINVAL; + goto annotate_write_out; + } + + if (length2 > 0 && annotate_copy(file, &buf[length1], length2) != 0) { + size = -EINVAL; + goto annotate_write_out; + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, ANNOTATE_BUF, time); + } + +annotate_write_out: + spin_unlock(&annotate_lock); + + // return the number of bytes written + return size; +} + +#include "gator_annotate_kernel.c" + +static int annotate_release(struct inode *inode, struct file *file) +{ + int cpu = 0; + + // synchronize between cores + spin_lock(&annotate_lock); + + if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF] && buffer_check_space(cpu, ANNOTATE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) { + uint32_t pid = current->pid; + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu()); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid); + gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 0); // time + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, 0); // size + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, ANNOTATE_BUF, gator_get_time()); + + spin_unlock(&annotate_lock); + + return 0; +} + +static const struct file_operations annotate_fops = { + .write = annotate_write, + .release = annotate_release +}; + +static int gator_annotate_create_files(struct super_block *sb, struct dentry *root) +{ + return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666); +} + +static int gator_annotate_start(void) +{ + collect_annotations = true; + return 0; +} + +static void gator_annotate_stop(void) +{ + // the spinlock here will ensure that when this function exits, we are not in the middle of an annotation + spin_lock(&annotate_lock); + collect_annotations = false; + wake_up(&gator_annotate_wait); + spin_unlock(&annotate_lock); +} diff -Nur linux-3.10.30/drivers/gator/gator_annotate_kernel.c linux-3.10.30-cubox-i/drivers/gator/gator_annotate_kernel.c --- linux-3.10.30/drivers/gator/gator_annotate_kernel.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_annotate_kernel.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,200 @@ +/** + * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * + * 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. + * + */ + +#define ESCAPE_CODE 0x1c +#define STRING_ANNOTATION 0x06 +#define NAME_CHANNEL_ANNOTATION 0x07 +#define NAME_GROUP_ANNOTATION 0x08 +#define VISUAL_ANNOTATION 0x04 +#define MARKER_ANNOTATION 0x05 + +static void kannotate_write(const char *ptr, unsigned int size) +{ + int retval; + int pos = 0; + loff_t offset = 0; + while (pos < size) { + retval = annotate_write(NULL, &ptr[pos], size - pos, &offset); + if (retval < 0) { + printk(KERN_WARNING "gator: kannotate_write failed with return value %d\n", retval); + return; + } + pos += retval; + } +} + +static void marshal_u16(char *buf, u16 val) { + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; +} + +static void marshal_u32(char *buf, u32 val) { + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[3] = (val >> 24) & 0xff; +} + +void gator_annotate_channel(int channel, const char *str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = STRING_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u16(header + 6, str_size); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); +} + +EXPORT_SYMBOL(gator_annotate_channel); + +void gator_annotate(const char *str) +{ + gator_annotate_channel(0, str); +} + +EXPORT_SYMBOL(gator_annotate); + +void gator_annotate_channel_color(int channel, int color, const char *str) +{ + const u16 str_size = (strlen(str) + 4) & 0xffff; + char header[12]; + header[0] = ESCAPE_CODE; + header[1] = STRING_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u16(header + 6, str_size); + marshal_u32(header + 8, color); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size - 4); +} + +EXPORT_SYMBOL(gator_annotate_channel_color); + +void gator_annotate_color(int color, const char *str) +{ + gator_annotate_channel_color(0, color, str); +} + +EXPORT_SYMBOL(gator_annotate_color); + +void gator_annotate_channel_end(int channel) +{ + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = STRING_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u16(header + 6, 0); + kannotate_write(header, sizeof(header)); +} + +EXPORT_SYMBOL(gator_annotate_channel_end); + +void gator_annotate_end(void) +{ + gator_annotate_channel_end(0); +} + +EXPORT_SYMBOL(gator_annotate_end); + +void gator_annotate_name_channel(int channel, int group, const char* str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[12]; + header[0] = ESCAPE_CODE; + header[1] = NAME_CHANNEL_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u32(header + 6, group); + marshal_u16(header + 10, str_size); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); +} + +EXPORT_SYMBOL(gator_annotate_name_channel); + +void gator_annotate_name_group(int group, const char* str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = NAME_GROUP_ANNOTATION; + marshal_u32(header + 2, group); + marshal_u16(header + 6, str_size); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); +} + +EXPORT_SYMBOL(gator_annotate_name_group); + +void gator_annotate_visual(const char *data, unsigned int length, const char *str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[4]; + char header_length[4]; + header[0] = ESCAPE_CODE; + header[1] = VISUAL_ANNOTATION; + marshal_u16(header + 2, str_size); + marshal_u32(header_length, length); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); + kannotate_write(header_length, sizeof(header_length)); + kannotate_write(data, length); +} + +EXPORT_SYMBOL(gator_annotate_visual); + +void gator_annotate_marker(void) +{ + char header[4]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, 0); + kannotate_write(header, sizeof(header)); +} + +EXPORT_SYMBOL(gator_annotate_marker); + +void gator_annotate_marker_str(const char *str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[4]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, str_size); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); +} + +EXPORT_SYMBOL(gator_annotate_marker_str); + +void gator_annotate_marker_color(int color) +{ + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, 4); + marshal_u32(header + 4, color); + kannotate_write(header, sizeof(header)); +} + +EXPORT_SYMBOL(gator_annotate_marker_color); + +void gator_annotate_marker_color_str(int color, const char *str) +{ + const u16 str_size = (strlen(str) + 4) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, str_size); + marshal_u32(header + 4, color); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size - 4); +} + +EXPORT_SYMBOL(gator_annotate_marker_color_str); diff -Nur linux-3.10.30/drivers/gator/gator_backtrace.c linux-3.10.30-cubox-i/drivers/gator/gator_backtrace.c --- linux-3.10.30/drivers/gator/gator_backtrace.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_backtrace.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,168 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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. + * + */ + +/* + * EABI backtrace stores {fp,lr} on the stack. + */ +struct stack_frame_eabi { + union { + struct { + unsigned long fp; + // May be the fp in the case of a leaf function or clang + unsigned long lr; + // If lr is really the fp, lr2 is the corresponding lr + unsigned long lr2; + }; + // Used to read 32 bit fp/lr from a 64 bit kernel + struct { + u32 fp_32; + // same as lr above + u32 lr_32; + // same as lr2 above + u32 lr2_32; + }; + }; +}; + +static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth) +{ +#if defined(__arm__) || defined(__aarch64__) + struct stack_frame_eabi *curr; + struct stack_frame_eabi bufcurr; +#if defined(__arm__) + const bool is_compat = false; + unsigned long fp = regs->ARM_fp; + unsigned long sp = regs->ARM_sp; + unsigned long lr = regs->ARM_lr; + const int gcc_frame_offset = sizeof(unsigned long); +#else + // Is userspace aarch32 (32 bit) + const bool is_compat = compat_user_mode(regs); + unsigned long fp = (is_compat ? regs->regs[11] : regs->regs[29]); + unsigned long sp = (is_compat ? regs->compat_sp : regs->sp); + unsigned long lr = (is_compat ? regs->compat_lr : regs->regs[30]); + const int gcc_frame_offset = (is_compat ? sizeof(u32) : 0); +#endif + // clang frame offset is always zero + int is_user_mode = user_mode(regs); + + // pc (current function) has already been added + + if (!is_user_mode) { + return; + } + + // Add the lr (parent function) + // entry preamble may not have executed + gator_add_trace(cpu, lr); + + // check fp is valid + if (fp == 0 || fp < sp) { + return; + } + + // Get the current stack frame + curr = (struct stack_frame_eabi *)(fp - gcc_frame_offset); + if ((unsigned long)curr & 3) { + return; + } + + while (depth-- && curr) { + if (!access_ok(VERIFY_READ, curr, sizeof(struct stack_frame_eabi)) || + __copy_from_user_inatomic(&bufcurr, curr, sizeof(struct stack_frame_eabi))) { + return; + } + + fp = (is_compat ? bufcurr.fp_32 : bufcurr.fp); + lr = (is_compat ? bufcurr.lr_32 : bufcurr.lr); + +#define calc_next(reg) ((reg) - gcc_frame_offset) + // Returns true if reg is a valid fp +#define validate_next(reg, curr) \ + ((reg) != 0 && (calc_next(reg) & 3) == 0 && (unsigned long)(curr) < calc_next(reg)) + + // Try lr from the stack as the fp because gcc leaf functions do not push lr + // If gcc_frame_offset is non-zero, the lr will also be the clang fp + // This assumes code is at a lower address than the stack + if (validate_next(lr, curr)) { + fp = lr; + lr = (is_compat ? bufcurr.lr2_32 : bufcurr.lr2); + } + + gator_add_trace(cpu, lr); + + if (!validate_next(fp, curr)) { + return; + } + + // Move to the next stack frame + curr = (struct stack_frame_eabi *)calc_next(fp); + } +#endif +} + +#if defined(__arm__) || defined(__aarch64__) +static int report_trace(struct stackframe *frame, void *d) +{ + unsigned int *depth = d, cookie = NO_COOKIE; + unsigned long addr = frame->pc; + + if (*depth) { +#if defined(MODULE) + unsigned int cpu = get_physical_cpu(); + struct module *mod = __module_address(addr); + if (mod) { + cookie = get_cookie(cpu, current, mod->name, false); + addr = addr - (unsigned long)mod->module_core; + } +#endif + marshal_backtrace(addr & ~1, cookie); + (*depth)--; + } + + return *depth == 0; +} +#endif + +// Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile +// #define GATOR_KERNEL_STACK_UNWINDING + +#if (defined(__arm__) || defined(__aarch64__)) && !defined(GATOR_KERNEL_STACK_UNWINDING) +// Disabled by default +MODULE_PARM_DESC(kernel_stack_unwinding, "Allow kernel stack unwinding."); +bool kernel_stack_unwinding = 0; +module_param(kernel_stack_unwinding, bool, 0644); +#endif + +static void kernel_backtrace(int cpu, struct pt_regs *const regs) +{ +#if defined(__arm__) || defined(__aarch64__) +#ifdef GATOR_KERNEL_STACK_UNWINDING + int depth = gator_backtrace_depth; +#else + int depth = (kernel_stack_unwinding ? gator_backtrace_depth : 1); +#endif + struct stackframe frame; + if (depth == 0) + depth = 1; +#if defined(__arm__) + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; +#else + frame.fp = regs->regs[29]; + frame.sp = regs->sp; + frame.pc = regs->pc; +#endif + walk_stackframe(&frame, report_trace, &depth); +#else + marshal_backtrace(PC_REG & ~1, NO_COOKIE); +#endif +} diff -Nur linux-3.10.30/drivers/gator/gator_cookies.c linux-3.10.30-cubox-i/drivers/gator/gator_cookies.c --- linux-3.10.30/drivers/gator/gator_cookies.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_cookies.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,433 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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. + * + */ + +#define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */ +#define TRANSLATE_BUFFER_SIZE 512 // must be a power of 2 - 512/4 = 128 entries +#define TRANSLATE_TEXT_SIZE 256 +#define MAX_COLLISIONS 2 + +static uint32_t *gator_crc32_table; +static unsigned int translate_buffer_mask; + +struct cookie_args { + struct task_struct *task; + const char *text; +}; + +static DEFINE_PER_CPU(char *, translate_text); +static DEFINE_PER_CPU(uint32_t, cookie_next_key); +static DEFINE_PER_CPU(uint64_t *, cookie_keys); +static DEFINE_PER_CPU(uint32_t *, cookie_values); +static DEFINE_PER_CPU(int, translate_buffer_read); +static DEFINE_PER_CPU(int, translate_buffer_write); +static DEFINE_PER_CPU(struct cookie_args *, translate_buffer); + +static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq); +static void wq_cookie_handler(struct work_struct *unused); +DECLARE_WORK(cookie_work, wq_cookie_handler); +static struct timer_list app_process_wake_up_timer; +static void app_process_wake_up_handler(unsigned long unused_data); + +static uint32_t cookiemap_code(uint64_t value64) +{ + uint32_t value = (uint32_t)((value64 >> 32) + value64); + uint32_t cookiecode = (value >> 24) & 0xff; + cookiecode = cookiecode * 31 + ((value >> 16) & 0xff); + cookiecode = cookiecode * 31 + ((value >> 8) & 0xff); + cookiecode = cookiecode * 31 + ((value >> 0) & 0xff); + cookiecode &= (COOKIEMAP_ENTRIES - 1); + return cookiecode * MAX_COLLISIONS; +} + +static uint32_t gator_chksum_crc32(const char *data) +{ + register unsigned long crc; + const unsigned char *block = data; + int i, length = strlen(data); + + crc = 0xFFFFFFFF; + for (i = 0; i < length; i++) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF]; + } + + return (crc ^ 0xFFFFFFFF); +} + +/* + * Exists + * Pre: [0][1][v][3]..[n-1] + * Post: [v][0][1][3]..[n-1] + */ +static uint32_t cookiemap_exists(uint64_t key) +{ + unsigned long x, flags, retval = 0; + int cpu = get_physical_cpu(); + uint32_t cookiecode = cookiemap_code(key); + uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); + uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); + + // Can be called from interrupt handler or from work queue + local_irq_save(flags); + for (x = 0; x < MAX_COLLISIONS; x++) { + if (keys[x] == key) { + uint32_t value = values[x]; + for (; x > 0; x--) { + keys[x] = keys[x - 1]; + values[x] = values[x - 1]; + } + keys[0] = key; + values[0] = value; + retval = value; + break; + } + } + local_irq_restore(flags); + + return retval; +} + +/* + * Add + * Pre: [0][1][2][3]..[n-1] + * Post: [v][0][1][2]..[n-2] + */ +static void cookiemap_add(uint64_t key, uint32_t value) +{ + int cpu = get_physical_cpu(); + int cookiecode = cookiemap_code(key); + uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); + uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); + int x; + + for (x = MAX_COLLISIONS - 1; x > 0; x--) { + keys[x] = keys[x - 1]; + values[x] = values[x - 1]; + } + keys[0] = key; + values[0] = value; +} + +#ifndef CONFIG_PREEMPT_RT_FULL +static void translate_buffer_write_args(int cpu, struct task_struct *task, const char *text) +{ + unsigned long flags; + int write; + int next_write; + struct cookie_args *args; + + local_irq_save(flags); + + write = per_cpu(translate_buffer_write, cpu); + next_write = (write + 1) & translate_buffer_mask; + + // At least one entry must always remain available as when read == write, the queue is empty not full + if (next_write != per_cpu(translate_buffer_read, cpu)) { + args = &per_cpu(translate_buffer, cpu)[write]; + args->task = task; + args->text = text; + get_task_struct(task); + per_cpu(translate_buffer_write, cpu) = next_write; + } + + local_irq_restore(flags); +} +#endif + +static void translate_buffer_read_args(int cpu, struct cookie_args *args) +{ + unsigned long flags; + int read; + + local_irq_save(flags); + + read = per_cpu(translate_buffer_read, cpu); + *args = per_cpu(translate_buffer, cpu)[read]; + per_cpu(translate_buffer_read, cpu) = (read + 1) & translate_buffer_mask; + + local_irq_restore(flags); +} + +static void wq_cookie_handler(struct work_struct *unused) +{ + struct cookie_args args; + int cpu = get_physical_cpu(), cookie; + + mutex_lock(&start_mutex); + + if (gator_started != 0) { + while (per_cpu(translate_buffer_read, cpu) != per_cpu(translate_buffer_write, cpu)) { + translate_buffer_read_args(cpu, &args); + cookie = get_cookie(cpu, args.task, args.text, true); + marshal_link(cookie, args.task->tgid, args.task->pid); + put_task_struct(args.task); + } + } + + mutex_unlock(&start_mutex); +} + +static void app_process_wake_up_handler(unsigned long unused_data) +{ + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + schedule_work(&cookie_work); +} + +// Retrieve full name from proc/pid/cmdline for java processes on Android +static int translate_app_process(const char **text, int cpu, struct task_struct *task, bool from_wq) +{ + void *maddr; + unsigned int len; + unsigned long addr; + struct mm_struct *mm; + struct page *page = NULL; + struct vm_area_struct *page_vma; + int bytes, offset, retval = 0; + char *buf = per_cpu(translate_text, cpu); + +#ifndef CONFIG_PREEMPT_RT_FULL + // Push work into a work queue if in atomic context as the kernel functions below might sleep + // Rely on the in_interrupt variable rather than in_irq() or in_interrupt() kernel functions, as the value of these functions seems + // inconsistent during a context switch between android/linux versions + if (!from_wq) { + // Check if already in buffer + int pos = per_cpu(translate_buffer_read, cpu); + while (pos != per_cpu(translate_buffer_write, cpu)) { + if (per_cpu(translate_buffer, cpu)[pos].task == task) + goto out; + pos = (pos + 1) & translate_buffer_mask; + } + + translate_buffer_write_args(cpu, task, *text); + + // Not safe to call in RT-Preempt full in schedule switch context + mod_timer(&app_process_wake_up_timer, jiffies + 1); + goto out; + } +#endif + + mm = get_task_mm(task); + if (!mm) + goto out; + if (!mm->arg_end) + goto outmm; + addr = mm->arg_start; + len = mm->arg_end - mm->arg_start; + + if (len > TRANSLATE_TEXT_SIZE) + len = TRANSLATE_TEXT_SIZE; + + down_read(&mm->mmap_sem); + while (len) { + if (get_user_pages(task, mm, addr, 1, 0, 1, &page, &page_vma) <= 0) + goto outsem; + + maddr = kmap(page); + offset = addr & (PAGE_SIZE - 1); + bytes = len; + if (bytes > PAGE_SIZE - offset) + bytes = PAGE_SIZE - offset; + + copy_from_user_page(page_vma, page, addr, buf, maddr + offset, bytes); + + kunmap(page); // release page allocated by get_user_pages() + page_cache_release(page); + + len -= bytes; + buf += bytes; + addr += bytes; + + *text = per_cpu(translate_text, cpu); + retval = 1; + } + + // On app_process startup, /proc/pid/cmdline is initially "zygote" then "" but changes after an initial startup period + if (strcmp(*text, "zygote") == 0 || strcmp(*text, "") == 0) + retval = 0; + +outsem: + up_read(&mm->mmap_sem); +outmm: + mmput(mm); +out: + return retval; +} + +static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq) +{ + unsigned long flags, cookie; + uint64_t key; + + key = gator_chksum_crc32(text); + key = (key << 32) | (uint32_t)task->tgid; + + cookie = cookiemap_exists(key); + if (cookie) { + return cookie; + } + + if (strcmp(text, "app_process") == 0) { + if (!translate_app_process(&text, cpu, task, from_wq)) + return UNRESOLVED_COOKIE; + } + + // Can be called from interrupt handler or from work queue or from scheduler trace + local_irq_save(flags); + + cookie = UNRESOLVED_COOKIE; + if (marshal_cookie_header(text)) { + cookie = per_cpu(cookie_next_key, cpu) += nr_cpu_ids; + cookiemap_add(key, cookie); + marshal_cookie(cookie, text); + } + + local_irq_restore(flags); + + return cookie; +} + +static int get_exec_cookie(int cpu, struct task_struct *task) +{ + struct mm_struct *mm = task->mm; + const char *text; + + // kernel threads have no address space + if (!mm) + return NO_COOKIE; + + if (task && task->mm && task->mm->exe_file) { + text = task->mm->exe_file->f_path.dentry->d_name.name; + return get_cookie(cpu, task, text, false); + } + + return UNRESOLVED_COOKIE; +} + +static unsigned long get_address_cookie(int cpu, struct task_struct *task, unsigned long addr, off_t *offset) +{ + unsigned long cookie = NO_COOKIE; + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma; + const char *text; + + if (!mm) + return cookie; + + for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { + if (addr < vma->vm_start || addr >= vma->vm_end) + continue; + + if (vma->vm_file) { + text = vma->vm_file->f_path.dentry->d_name.name; + cookie = get_cookie(cpu, task, text, false); + *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; + } else { + /* must be an anonymous map */ + *offset = addr; + } + + break; + } + + if (!vma) + cookie = UNRESOLVED_COOKIE; + + return cookie; +} + +static int cookies_initialize(void) +{ + uint32_t crc, poly; + int i, j, cpu, size, err = 0; + + translate_buffer_mask = TRANSLATE_BUFFER_SIZE / sizeof(per_cpu(translate_buffer, 0)[0]) - 1; + + for_each_present_cpu(cpu) { + per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu; + + size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t); + per_cpu(cookie_keys, cpu) = (uint64_t *)kmalloc(size, GFP_KERNEL); + if (!per_cpu(cookie_keys, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + memset(per_cpu(cookie_keys, cpu), 0, size); + + size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t); + per_cpu(cookie_values, cpu) = (uint32_t *)kmalloc(size, GFP_KERNEL); + if (!per_cpu(cookie_values, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + memset(per_cpu(cookie_values, cpu), 0, size); + + per_cpu(translate_buffer, cpu) = (struct cookie_args *)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL); + if (!per_cpu(translate_buffer, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + + per_cpu(translate_buffer_write, cpu) = 0; + per_cpu(translate_buffer_read, cpu) = 0; + + per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_TEXT_SIZE, GFP_KERNEL); + if (!per_cpu(translate_text, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + } + + // build CRC32 table + poly = 0x04c11db7; + gator_crc32_table = (uint32_t *)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL); + if (!gator_crc32_table) { + err = -ENOMEM; + goto cookie_setup_error; + } + for (i = 0; i < 256; i++) { + crc = i; + for (j = 8; j > 0; j--) { + if (crc & 1) { + crc = (crc >> 1) ^ poly; + } else { + crc >>= 1; + } + } + gator_crc32_table[i] = crc; + } + + setup_timer(&app_process_wake_up_timer, app_process_wake_up_handler, 0); + +cookie_setup_error: + return err; +} + +static void cookies_release(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + kfree(per_cpu(cookie_keys, cpu)); + per_cpu(cookie_keys, cpu) = NULL; + + kfree(per_cpu(cookie_values, cpu)); + per_cpu(cookie_values, cpu) = NULL; + + kfree(per_cpu(translate_buffer, cpu)); + per_cpu(translate_buffer, cpu) = NULL; + per_cpu(translate_buffer_read, cpu) = 0; + per_cpu(translate_buffer_write, cpu) = 0; + + kfree(per_cpu(translate_text, cpu)); + per_cpu(translate_text, cpu) = NULL; + } + + del_timer_sync(&app_process_wake_up_timer); + kfree(gator_crc32_table); + gator_crc32_table = NULL; +} diff -Nur linux-3.10.30/drivers/gator/gator_events_armv6.c linux-3.10.30-cubox-i/drivers/gator/gator_events_armv6.c --- linux-3.10.30/drivers/gator/gator_events_armv6.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_armv6.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,237 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +static const char *pmnc_name; + +/* + * Per-CPU PMCR + */ +#define PMCR_E (1 << 0) /* Enable */ +#define PMCR_P (1 << 1) /* Count reset */ +#define PMCR_C (1 << 2) /* Cycle counter reset */ +#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ +#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ +#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ + +#define PMN0 0 +#define PMN1 1 +#define CCNT 2 +#define CNTMAX (CCNT+1) + +static int pmnc_counters = 0; +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +static inline void armv6_pmnc_write(u32 val) +{ + /* upper 4bits and 7, 11 are write-as-0 */ + val &= 0x0ffff77f; + asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); +} + +static inline u32 armv6_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); + return val; +} + +static void armv6_pmnc_reset_counter(unsigned int cnt) +{ + u32 val = 0; + switch (cnt) { + case CCNT: + asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); + break; + case PMN0: + asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); + break; + case PMN1: + asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); + break; + } +} + +int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + pmnc_counters = 3; + + for (i = PMN0; i <= CCNT; i++) { + char buf[40]; + if (i == CCNT) { + snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i != CCNT) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_armv6_online(int **buffer, bool migrate) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + u32 pmnc; + + if (armv6_pmnc_read() & PMCR_E) { + armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); + } + + /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ + armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | + PMCR_C | PMCR_P); + + /* configure control register */ + for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { + unsigned long event; + + if (!pmnc_enabled[cnt]) + continue; + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters) + if (cnt == PMN0) { + pmnc |= event << 20; + } else if (cnt == PMN1) { + pmnc |= event << 12; + } + + // Reset counter + armv6_pmnc_reset_counter(cnt); + } + armv6_pmnc_write(pmnc | PMCR_E); + + // return zero values, no need to read as the counters were just reset + for (cnt = PMN0; cnt <= CCNT; cnt++) { + if (pmnc_enabled[cnt]) { + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_armv6_offline(int **buffer, bool migrate) +{ + unsigned int cnt; + + armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); + for (cnt = PMN0; cnt <= CCNT; cnt++) { + armv6_pmnc_reset_counter(cnt); + } + + return 0; +} + +static void gator_events_armv6_stop(void) +{ + unsigned int cnt; + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_armv6_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(armv6_pmnc_read() & PMCR_E)) { + return 0; + } + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + if (pmnc_enabled[cnt]) { + u32 value = 0; + switch (cnt) { + case CCNT: + asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value)); + break; + case PMN0: + asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value)); + break; + case PMN1: + asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value)); + break; + } + armv6_pmnc_reset_counter(cnt); + + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_armv6_interface = { + .create_files = gator_events_armv6_create_files, + .stop = gator_events_armv6_stop, + .online = gator_events_armv6_online, + .offline = gator_events_armv6_offline, + .read = gator_events_armv6_read, +}; + +int gator_events_armv6_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case ARM1136: + case ARM1156: + case ARM1176: + pmnc_name = "ARM11"; + break; + case ARM11MPCORE: + pmnc_name = "ARM11MPCore"; + break; + default: + return -1; + } + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_armv6_interface); +} + +#endif diff -Nur linux-3.10.30/drivers/gator/gator_events_armv7.c linux-3.10.30-cubox-i/drivers/gator/gator_events_armv7.c --- linux-3.10.30/drivers/gator/gator_events_armv7.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_armv7.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,312 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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. + */ + +/* Disabling interrupts + * Many of the functions below disable interrupts via local_irq_save(). This disabling of interrupts is done to prevent any race conditions + * between multiple entities (e.g. hrtimer interrupts and event based interrupts) calling the same functions. As accessing the pmu involves + * several steps (disable, select, read, enable), these steps must be performed atomically. Normal synchronization routines cannot be used + * as these functions are being called from interrupt context. + */ + +#include "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +// Per-CPU PMNC: config reg +#define PMNC_E (1 << 0) /* Enable all counters */ +#define PMNC_P (1 << 1) /* Reset all counters */ +#define PMNC_C (1 << 2) /* Cycle counter reset */ +#define PMNC_MASK 0x3f /* Mask for writable bits */ + +// ccnt reg +#define CCNT_REG (1 << 31) + +#define CCNT 0 +#define CNT0 1 +#define CNTMAX (6+1) + +static const char *pmnc_name; +static int pmnc_counters; + +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +inline void armv7_pmnc_write(u32 val) +{ + val &= PMNC_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); +} + +inline u32 armv7_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); + return val; +} + +inline u32 armv7_ccnt_read(u32 reset_value) +{ + unsigned long flags; + u32 newval = -reset_value; + u32 den = CCNT_REG; + u32 val; + + local_irq_save(flags); + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); // read + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval)); // new value + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable + local_irq_restore(flags); + + return val; +} + +inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value) +{ + unsigned long flags; + u32 newval = -reset_value; + u32 sel = (cnt - CNT0); + u32 den = 1 << sel; + u32 oldval; + + local_irq_save(flags); + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); // select + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval)); // read + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval)); // new value + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable + local_irq_restore(flags); + + return oldval; +} + +static inline void armv7_pmnc_disable_interrupt(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31); + asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val)); +} + +inline u32 armv7_pmnc_reset_interrupt(void) +{ + // Get and reset overflow status flags + u32 flags; + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags)); + flags &= 0x8000003f; + asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags)); + return flags; +} + +static inline u32 armv7_pmnc_enable_counter(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG; + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); + return cnt; +} + +static inline u32 armv7_pmnc_disable_counter(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG; + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); + return cnt; +} + +static inline int armv7_pmnc_select_counter(unsigned int cnt) +{ + u32 val = (cnt - CNT0); + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + return cnt; +} + +static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val) +{ + if (armv7_pmnc_select_counter(cnt) == cnt) { + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); + } +} + +static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_counters; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i - 1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_armv7_online(int **buffer, bool migrate) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + + if (armv7_pmnc_read() & PMNC_E) { + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); + } + + // Initialize & Reset PMNC: C bit and P bit + armv7_pmnc_write(PMNC_P | PMNC_C); + + // Reset overflow flags + armv7_pmnc_reset_interrupt(); + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + unsigned long event; + + if (!pmnc_enabled[cnt]) + continue; + + // Disable counter + armv7_pmnc_disable_counter(cnt); + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count + if (cnt != CCNT) + armv7_pmnc_write_evtsel(cnt, event); + + armv7_pmnc_disable_interrupt(cnt); + + // Reset counter + cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0); + + // Enable counter + armv7_pmnc_enable_counter(cnt); + } + + // enable + armv7_pmnc_write(armv7_pmnc_read() | PMNC_E); + + // return zero values, no need to read as the counters were just reset + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_armv7_offline(int **buffer, bool migrate) +{ + // disable all counters, including PMCCNTR; overflow IRQs will not be signaled + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); + + return 0; +} + +static void gator_events_armv7_stop(void) +{ + unsigned int cnt; + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_armv7_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(armv7_pmnc_read() & PMNC_E)) { + return 0; + } + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = armv7_ccnt_read(0); + } else { + value = armv7_cntn_read(cnt, 0); + } + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_armv7_interface = { + .create_files = gator_events_armv7_create_files, + .stop = gator_events_armv7_stop, + .online = gator_events_armv7_online, + .offline = gator_events_armv7_offline, + .read = gator_events_armv7_read, +}; + +int gator_events_armv7_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case CORTEX_A5: + pmnc_name = "Cortex-A5"; + pmnc_counters = 2; + break; + case CORTEX_A7: + pmnc_name = "Cortex-A7"; + pmnc_counters = 4; + break; + case CORTEX_A8: + pmnc_name = "Cortex-A8"; + pmnc_counters = 4; + break; + case CORTEX_A9: + pmnc_name = "Cortex-A9"; + pmnc_counters = 6; + break; + case CORTEX_A15: + pmnc_name = "Cortex-A15"; + pmnc_counters = 6; + break; + default: + return -1; + } + + pmnc_counters++; // CNT[n] + CCNT + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_armv7_interface); +} + +#endif diff -Nur linux-3.10.30/drivers/gator/gator_events_block.c linux-3.10.30-cubox-i/drivers/gator/gator_events_block.c --- linux-3.10.30/drivers/gator/gator_events_block.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_block.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,153 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" +#include + +#define BLOCK_RQ_WR 0 +#define BLOCK_RQ_RD 1 + +#define BLOCK_TOTAL (BLOCK_RQ_RD+1) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +#define EVENTWRITE REQ_RW +#else +#define EVENTWRITE REQ_WRITE +#endif + +static ulong block_rq_wr_enabled; +static ulong block_rq_rd_enabled; +static ulong block_rq_wr_key; +static ulong block_rq_rd_key; +static atomic_t blockCnt[BLOCK_TOTAL]; +static int blockGet[BLOCK_TOTAL * 4]; + +GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq)) +{ + int write, size; + + if (!rq) + return; + + write = rq->cmd_flags & EVENTWRITE; + size = rq->resid_len; + + if (!size) + return; + + if (write) { + if (block_rq_wr_enabled) { + atomic_add(size, &blockCnt[BLOCK_RQ_WR]); + } + } else { + if (block_rq_rd_enabled) { + atomic_add(size, &blockCnt[BLOCK_RQ_RD]); + } + } +} + +static int gator_events_block_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* block_complete_wr */ + dir = gatorfs_mkdir(sb, root, "Linux_block_rq_wr"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &block_rq_wr_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_wr_key); + + /* block_complete_rd */ + dir = gatorfs_mkdir(sb, root, "Linux_block_rq_rd"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &block_rq_rd_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_rd_key); + + return 0; +} + +static int gator_events_block_start(void) +{ + // register tracepoints + if (block_rq_wr_enabled || block_rq_rd_enabled) + if (GATOR_REGISTER_TRACE(block_rq_complete)) + goto fail_block_rq_exit; + pr_debug("gator: registered block event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_block_rq_exit: + pr_err("gator: block event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_block_stop(void) +{ + if (block_rq_wr_enabled || block_rq_rd_enabled) + GATOR_UNREGISTER_TRACE(block_rq_complete); + pr_debug("gator: unregistered block event tracepoints\n"); + + block_rq_wr_enabled = 0; + block_rq_rd_enabled = 0; +} + +static int gator_events_block_read(int **buffer) +{ + int len, value, data = 0; + + if (!on_primary_core()) { + return 0; + } + + len = 0; + if (block_rq_wr_enabled && (value = atomic_read(&blockCnt[BLOCK_RQ_WR])) > 0) { + atomic_sub(value, &blockCnt[BLOCK_RQ_WR]); + blockGet[len++] = block_rq_wr_key; + blockGet[len++] = 0; // indicates to Streamline that value bytes were written now, not since the last message + blockGet[len++] = block_rq_wr_key; + blockGet[len++] = value; + data += value; + } + if (block_rq_rd_enabled && (value = atomic_read(&blockCnt[BLOCK_RQ_RD])) > 0) { + atomic_sub(value, &blockCnt[BLOCK_RQ_RD]); + blockGet[len++] = block_rq_rd_key; + blockGet[len++] = 0; // indicates to Streamline that value bytes were read now, not since the last message + blockGet[len++] = block_rq_rd_key; + blockGet[len++] = value; + data += value; + } + + if (buffer) + *buffer = blockGet; + + return len; +} + +static struct gator_interface gator_events_block_interface = { + .create_files = gator_events_block_create_files, + .start = gator_events_block_start, + .stop = gator_events_block_stop, + .read = gator_events_block_read, +}; + +int gator_events_block_init(void) +{ + block_rq_wr_enabled = 0; + block_rq_rd_enabled = 0; + + block_rq_wr_key = gator_events_get_key(); + block_rq_rd_key = gator_events_get_key(); + + return gator_events_install(&gator_events_block_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_ccn-504.c linux-3.10.30-cubox-i/drivers/gator/gator_events_ccn-504.c --- linux-3.10.30/drivers/gator/gator_events_ccn-504.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_ccn-504.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,346 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * 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 +#include + +#include "gator.h" + +#define NUM_REGIONS 256 +#define REGION_SIZE (64*1024) +#define REGION_DEBUG 1 +#define REGION_XP 64 +#define NUM_XPS 11 + +// DT (Debug) region +#define PMEVCNTSR0 0x0150 +#define PMCCNTRSR 0x0190 +#define PMCR 0x01A8 +#define PMSR 0x01B0 +#define PMSR_REQ 0x01B8 +#define PMSR_CLR 0x01C0 + +// XP region +#define DT_CONFIG 0x0300 +#define DT_CONTROL 0x0370 + +// Multiple +#define PMU_EVENT_SEL 0x0600 +#define OLY_ID 0xFF00 + +#define CCNT 4 +#define CNTMAX (CCNT + 1) + +#define get_pmu_event_id(event) (((event) >> 0) & 0xFF) +#define get_node_type(event) (((event) >> 8) & 0xFF) +#define get_region(event) (((event) >> 16) & 0xFF) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + +// From kernel/params.c +#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \ + int param_set_##name(const char *val, struct kernel_param *kp) \ + { \ + tmptype l; \ + int ret; \ + \ + if (!val) return -EINVAL; \ + ret = strtolfn(val, 0, &l); \ + if (ret == -EINVAL || ((type)l != l)) \ + return -EINVAL; \ + *((type *)kp->arg) = l; \ + return 0; \ + } \ + int param_get_##name(char *buffer, struct kernel_param *kp) \ + { \ + return sprintf(buffer, format, *((type *)kp->arg)); \ + } + +#else + +// From kernel/params.c +#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \ + int param_set_##name(const char *val, const struct kernel_param *kp) \ + { \ + tmptype l; \ + int ret; \ + \ + ret = strtolfn(val, 0, &l); \ + if (ret < 0 || ((type)l != l)) \ + return ret < 0 ? ret : -EINVAL; \ + *((type *)kp->arg) = l; \ + return 0; \ + } \ + int param_get_##name(char *buffer, const struct kernel_param *kp) \ + { \ + return scnprintf(buffer, PAGE_SIZE, format, \ + *((type *)kp->arg)); \ + } \ + struct kernel_param_ops param_ops_##name = { \ + .set = param_set_##name, \ + .get = param_get_##name, \ + }; \ + EXPORT_SYMBOL(param_set_##name); \ + EXPORT_SYMBOL(param_get_##name); \ + EXPORT_SYMBOL(param_ops_##name) + +#endif + +STANDARD_PARAM_DEF(u64, u64, "%llu", u64, strict_strtoull); + +// From include/linux/moduleparam.h +#define param_check_u64(name, p) __param_check(name, p, u64) + +MODULE_PARM_DESC(ccn504_addr, "CCN-504 physical base address"); +static u64 ccn504_addr = 0; +module_param(ccn504_addr, u64, 0444); + +static void __iomem *gator_events_ccn504_base; +static bool gator_events_ccn504_global_enabled; +static unsigned long gator_events_ccn504_enabled[CNTMAX]; +static unsigned long gator_events_ccn504_event[CNTMAX]; +static unsigned long gator_events_ccn504_key[CNTMAX]; +static int gator_events_ccn504_buffer[2*CNTMAX]; +static int gator_events_ccn504_prev[CNTMAX]; + +static void gator_events_ccn504_create_shutdown(void) +{ + if (gator_events_ccn504_base != NULL) { + iounmap(gator_events_ccn504_base); + } +} + +static int gator_events_ccn504_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + char buf[32]; + + for (i = 0; i < CNTMAX; ++i) { + if (i == CCNT) { + snprintf(buf, sizeof(buf), "CCN-504_ccnt"); + } else { + snprintf(buf, sizeof(buf), "CCN-504_cnt%i", i); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + + gatorfs_create_ulong(sb, dir, "enabled", &gator_events_ccn504_enabled[i]); + if (i != CCNT) { + gatorfs_create_ulong(sb, dir, "event", &gator_events_ccn504_event[i]); + } + gatorfs_create_ro_ulong(sb, dir, "key", &gator_events_ccn504_key[i]); + } + + return 0; +} + +static void gator_events_ccn504_set_dt_config(int xp_node_id, int event_num, int value) +{ + u32 dt_config; + + dt_config = readl(gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG); + dt_config |= (value + event_num) << (4*event_num); + writel(dt_config, gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG); +} + +static int gator_events_ccn504_start(void) +{ + int i; + + gator_events_ccn504_global_enabled = 0; + for (i = 0; i < CNTMAX; ++i) { + if (gator_events_ccn504_enabled[i]) { + gator_events_ccn504_global_enabled = 1; + break; + } + } + + if (!gator_events_ccn504_global_enabled) { + return 0; + } + + memset(&gator_events_ccn504_prev, 0x80, sizeof(gator_events_ccn504_prev)); + + // Disable INTREQ on overflow + // [6] ovfl_intr_en = 0 + // perhaps set to 1? + // [5] cntr_rst = 0 + // No register paring + // [4:1] cntcfg = 0 + // Enable PMU features + // [0] pmu_en = 1 + writel(0x1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMCR); + + // Configure the XPs + for (i = 0; i < NUM_XPS; ++i) { + int dt_control; + + // Pass on all events + writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG); + + // Enable PMU capability + // [0] dt_enable = 1 + dt_control = readl(gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL); + dt_control |= 0x1; + writel(dt_control, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL); + } + + // Assume no other pmu_event_sel registers are set + + // cycle counter does not need to be enabled + for (i = 0; i < CCNT; ++i) { + int pmu_event_id; + int node_type; + int region; + u32 pmu_event_sel; + u32 oly_id_whole; + u32 oly_id; + u32 node_id; + + if (!gator_events_ccn504_enabled[i]) { + continue; + } + + pmu_event_id = get_pmu_event_id(gator_events_ccn504_event[i]); + node_type = get_node_type(gator_events_ccn504_event[i]); + region = get_region(gator_events_ccn504_event[i]); + + // Verify the node_type + oly_id_whole = readl(gator_events_ccn504_base + region*REGION_SIZE + OLY_ID); + oly_id = oly_id_whole & 0x1F; + node_id = (oly_id_whole >> 8) & 0x7F; + if ((oly_id != node_type) || + ((node_type == 0x16) && ((oly_id != 0x14) && (oly_id != 0x15) && (oly_id != 0x16) && (oly_id != 0x18) && (oly_id != 0x19) && (oly_id != 0x1A)))) { + printk(KERN_ERR "gator: oly_id is 0x%x expected 0x%x\n", oly_id, node_type); + return -1; + } + + // Set the control register + pmu_event_sel = readl(gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL); + switch (node_type) { + case 0x08: // XP + pmu_event_sel |= pmu_event_id << (7*i); + gator_events_ccn504_set_dt_config(node_id, i, 0x4); + break; + case 0x04: // HN-F + case 0x16: // RN-I + case 0x10: // SBAS + pmu_event_sel |= pmu_event_id << (4*i); + gator_events_ccn504_set_dt_config(node_id/2, i, (node_id & 1) == 0 ? 0x8 : 0xC); + break; + } + writel(pmu_event_sel, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL); + } + + return 0; +} + +static void gator_events_ccn504_stop(void) +{ + int i; + + if (!gator_events_ccn504_global_enabled) { + return; + } + + // cycle counter does not need to be disabled + for (i = 0; i < CCNT; ++i) { + int region; + + if (!gator_events_ccn504_enabled[i]) { + continue; + } + + region = get_region(gator_events_ccn504_event[i]); + + writel(0, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL); + } + + // Clear dt_config + for (i = 0; i < NUM_XPS; ++i) { + writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG); + } +} + +static int gator_events_ccn504_read(int **buffer) +{ + int i; + int len = 0; + int value; + + if (!on_primary_core() || !gator_events_ccn504_global_enabled) { + return 0; + } + + // Verify the pmsr register is zero + while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0); + + // Request a PMU snapshot + writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_REQ); + + // Wait for the snapshot + while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0); + + // Read the shadow registers + for (i = 0; i < CNTMAX; ++i) { + if (!gator_events_ccn504_enabled[i]) { + continue; + } + + value = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i)); + if (gator_events_ccn504_prev[i] != 0x80808080) { + gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i]; + gator_events_ccn504_buffer[len++] = value - gator_events_ccn504_prev[i]; + } + gator_events_ccn504_prev[i] = value; + + // Are the counters registers cleared when read? Is that what the cntr_rst bit on the pmcr register does? + } + + // Clear the PMU snapshot status + writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_CLR); + + if (buffer) + *buffer = gator_events_ccn504_buffer; + + return len; +} + +static struct gator_interface gator_events_ccn504_interface = { + .shutdown = gator_events_ccn504_create_shutdown, + .create_files = gator_events_ccn504_create_files, + .start = gator_events_ccn504_start, + .stop = gator_events_ccn504_stop, + .read = gator_events_ccn504_read, +}; + +int gator_events_ccn504_init(void) +{ + int i; + + if (ccn504_addr == 0) { + return -1; + } + + gator_events_ccn504_base = ioremap(ccn504_addr, NUM_REGIONS*REGION_SIZE); + if (gator_events_ccn504_base == NULL) { + printk(KERN_ERR "gator: ioremap returned NULL\n"); + return -1; + } + + for (i = 0; i < CNTMAX; ++i) { + gator_events_ccn504_enabled[i] = 0; + gator_events_ccn504_event[i] = 0; + gator_events_ccn504_key[i] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_ccn504_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_irq.c linux-3.10.30-cubox-i/drivers/gator/gator_events_irq.c --- linux-3.10.30/drivers/gator/gator_events_irq.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_irq.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,165 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" +#include + +#define HARDIRQ 0 +#define SOFTIRQ 1 +#define TOTALIRQ (SOFTIRQ+1) + +static ulong hardirq_enabled; +static ulong softirq_enabled; +static ulong hardirq_key; +static ulong softirq_key; +static DEFINE_PER_CPU(atomic_t[TOTALIRQ], irqCnt); +static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet); + +GATOR_DEFINE_PROBE(irq_handler_exit, + TP_PROTO(int irq, struct irqaction *action, int ret)) +{ + atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[HARDIRQ]); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) +GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softirq_action *vec)) +#else +GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr)) +#endif +{ + atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[SOFTIRQ]); +} + +static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* irq */ + dir = gatorfs_mkdir(sb, root, "Linux_irq_irq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &hardirq_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &hardirq_key); + + /* soft irq */ + dir = gatorfs_mkdir(sb, root, "Linux_irq_softirq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &softirq_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &softirq_key); + + return 0; +} + +static int gator_events_irq_online(int **buffer, bool migrate) +{ + int len = 0, cpu = get_physical_cpu(); + + // synchronization with the irq_exit functions is not necessary as the values are being reset + if (hardirq_enabled) { + atomic_set(&per_cpu(irqCnt, cpu)[HARDIRQ], 0); + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = 0; + } + + if (softirq_enabled) { + atomic_set(&per_cpu(irqCnt, cpu)[SOFTIRQ], 0); + per_cpu(irqGet, cpu)[len++] = softirq_key; + per_cpu(irqGet, cpu)[len++] = 0; + } + + if (buffer) + *buffer = per_cpu(irqGet, cpu); + + return len; +} + +static int gator_events_irq_start(void) +{ + // register tracepoints + if (hardirq_enabled) + if (GATOR_REGISTER_TRACE(irq_handler_exit)) + goto fail_hardirq_exit; + if (softirq_enabled) + if (GATOR_REGISTER_TRACE(softirq_exit)) + goto fail_softirq_exit; + pr_debug("gator: registered irq tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_softirq_exit: + if (hardirq_enabled) + GATOR_UNREGISTER_TRACE(irq_handler_exit); +fail_hardirq_exit: + pr_err("gator: irq tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_irq_stop(void) +{ + if (hardirq_enabled) + GATOR_UNREGISTER_TRACE(irq_handler_exit); + if (softirq_enabled) + GATOR_UNREGISTER_TRACE(softirq_exit); + pr_debug("gator: unregistered irq tracepoints\n"); + + hardirq_enabled = 0; + softirq_enabled = 0; +} + +static int gator_events_irq_read(int **buffer) +{ + int len, value; + int cpu = get_physical_cpu(); + + len = 0; + if (hardirq_enabled) { + value = atomic_read(&per_cpu(irqCnt, cpu)[HARDIRQ]); + atomic_sub(value, &per_cpu(irqCnt, cpu)[HARDIRQ]); + + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + + if (softirq_enabled) { + value = atomic_read(&per_cpu(irqCnt, cpu)[SOFTIRQ]); + atomic_sub(value, &per_cpu(irqCnt, cpu)[SOFTIRQ]); + + per_cpu(irqGet, cpu)[len++] = softirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + + if (buffer) + *buffer = per_cpu(irqGet, cpu); + + return len; +} + +static struct gator_interface gator_events_irq_interface = { + .create_files = gator_events_irq_create_files, + .online = gator_events_irq_online, + .start = gator_events_irq_start, + .stop = gator_events_irq_stop, + .read = gator_events_irq_read, +}; + +int gator_events_irq_init(void) +{ + hardirq_key = gator_events_get_key(); + softirq_key = gator_events_get_key(); + + hardirq_enabled = 0; + softirq_enabled = 0; + + return gator_events_install(&gator_events_irq_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_l2c-310.c linux-3.10.30-cubox-i/drivers/gator/gator_events_l2c-310.c --- linux-3.10.30/drivers/gator/gator_events_l2c-310.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_l2c-310.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,208 @@ +/** + * l2c310 (L2 Cache Controller) event counters for gator + * + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 +#include +#include +#if defined(CONFIG_OF) +#include +#include +#endif +#include + +#include "gator.h" + +#define L2C310_COUNTERS_NUM 2 + +static struct { + unsigned long enabled; + unsigned long event; + unsigned long key; +} l2c310_counters[L2C310_COUNTERS_NUM]; + +static int l2c310_buffer[L2C310_COUNTERS_NUM * 2]; + +static void __iomem *l2c310_base; + +static void gator_events_l2c310_reset_counters(void) +{ + u32 val = readl(l2c310_base + L2X0_EVENT_CNT_CTRL); + + val |= ((1 << L2C310_COUNTERS_NUM) - 1) << 1; + + writel(val, l2c310_base + L2X0_EVENT_CNT_CTRL); +} + +static int gator_events_l2c310_create_files(struct super_block *sb, + struct dentry *root) +{ + int i; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + char buf[16]; + struct dentry *dir; + + snprintf(buf, sizeof(buf), "L2C-310_cnt%d", i); + dir = gatorfs_mkdir(sb, root, buf); + if (WARN_ON(!dir)) + return -1; + gatorfs_create_ulong(sb, dir, "enabled", + &l2c310_counters[i].enabled); + gatorfs_create_ulong(sb, dir, "event", + &l2c310_counters[i].event); + gatorfs_create_ro_ulong(sb, dir, "key", + &l2c310_counters[i].key); + } + + return 0; +} + +static int gator_events_l2c310_start(void) +{ + static const unsigned long l2x0_event_cntx_cfg[L2C310_COUNTERS_NUM] = { + L2X0_EVENT_CNT0_CFG, + L2X0_EVENT_CNT1_CFG, + }; + int i; + + /* Counter event sources */ + for (i = 0; i < L2C310_COUNTERS_NUM; i++) + writel((l2c310_counters[i].event & 0xf) << 2, + l2c310_base + l2x0_event_cntx_cfg[i]); + + gator_events_l2c310_reset_counters(); + + /* Event counter enable */ + writel(1, l2c310_base + L2X0_EVENT_CNT_CTRL); + + return 0; +} + +static void gator_events_l2c310_stop(void) +{ + /* Event counter disable */ + writel(0, l2c310_base + L2X0_EVENT_CNT_CTRL); +} + +static int gator_events_l2c310_read(int **buffer) +{ + static const unsigned long l2x0_event_cntx_val[L2C310_COUNTERS_NUM] = { + L2X0_EVENT_CNT0_VAL, + L2X0_EVENT_CNT1_VAL, + }; + int i; + int len = 0; + + if (!on_primary_core()) + return 0; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + if (l2c310_counters[i].enabled) { + l2c310_buffer[len++] = l2c310_counters[i].key; + l2c310_buffer[len++] = readl(l2c310_base + + l2x0_event_cntx_val[i]); + } + } + + /* l2c310 counters are saturating, not wrapping in case of overflow */ + gator_events_l2c310_reset_counters(); + + if (buffer) + *buffer = l2c310_buffer; + + return len; +} + +static struct gator_interface gator_events_l2c310_interface = { + .create_files = gator_events_l2c310_create_files, + .start = gator_events_l2c310_start, + .stop = gator_events_l2c310_stop, + .read = gator_events_l2c310_read, +}; + +#define L2C310_ADDR_PROBE (~0) + +MODULE_PARM_DESC(l2c310_addr, "L2C310 physical base address (0 to disable)"); +static unsigned long l2c310_addr = L2C310_ADDR_PROBE; +module_param(l2c310_addr, ulong, 0444); + +static void __iomem *gator_events_l2c310_probe(void) +{ + phys_addr_t variants[] = { +#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_S5PV310) + 0x10502000, +#endif +#if defined(CONFIG_ARCH_OMAP4) + 0x48242000, +#endif +#if defined(CONFIG_ARCH_TEGRA) + 0x50043000, +#endif +#if defined(CONFIG_ARCH_U8500) + 0xa0412000, +#endif +#if defined(CONFIG_ARCH_VEXPRESS) + 0x1e00a000, // A9x4 core tile (HBI-0191) + 0x2c0f0000, // New memory map tiles +#endif + }; + int i; + void __iomem *base; +#if defined(CONFIG_OF) + struct device_node *node = of_find_all_nodes(NULL); + + if (node) { + of_node_put(node); + + node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); + base = of_iomap(node, 0); + of_node_put(node); + + return base; + } +#endif + + for (i = 0; i < ARRAY_SIZE(variants); i++) { + base = ioremap(variants[i], SZ_4K); + if (base) { + u32 cache_id = readl(base + L2X0_CACHE_ID); + + if ((cache_id & 0xff0003c0) == 0x410000c0) + return base; + + iounmap(base); + } + } + + return NULL; +} + +int gator_events_l2c310_init(void) +{ + int i; + + if (gator_cpuid() != CORTEX_A5 && gator_cpuid() != CORTEX_A9) + return -1; + + if (l2c310_addr == L2C310_ADDR_PROBE) + l2c310_base = gator_events_l2c310_probe(); + else if (l2c310_addr) + l2c310_base = ioremap(l2c310_addr, SZ_4K); + + if (!l2c310_base) + return -1; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + l2c310_counters[i].enabled = 0; + l2c310_counters[i].key = gator_events_get_key(); + } + + return gator_events_install(&gator_events_l2c310_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_mali_4xx.c linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_4xx.c --- linux-3.10.30/drivers/gator/gator_events_mali_4xx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_4xx.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,723 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" + +#include +#include +#include + +#include "linux/mali_linux_trace.h" + +#include "gator_events_mali_common.h" +#include "gator_events_mali_4xx.h" + +/* + * There are (currently) four different variants of the comms between gator and Mali: + * 1 (deprecated): No software counter support + * 2 (deprecated): Tracepoint called for each separate s/w counter value as it appears + * 3 (default): Single tracepoint for all s/w counters in a bundle. + * Interface style 3 is the default if no other is specified. 1 and 2 will be eliminated when + * existing Mali DDKs are upgraded. + * 4. As above, but for the Utgard (Mali-450) driver. + */ + +#if !defined(GATOR_MALI_INTERFACE_STYLE) +#define GATOR_MALI_INTERFACE_STYLE (3) +#endif + +#if GATOR_MALI_INTERFACE_STYLE < 4 +#include "mali/mali_mjollnir_profiling_gator_api.h" +#else +#include "mali/mali_utgard_profiling_gator_api.h" +#endif + +/* + * Check that the MALI_SUPPORT define is set to one of the allowable device codes. + */ +#if (MALI_SUPPORT != MALI_4xx) +#error MALI_SUPPORT set to an invalid device code: expecting MALI_4xx +#endif + +/* gatorfs variables for counter enable state, + * the event the counter should count and the + * 'key' (a unique id set by gatord and returned + * by gator.ko) + */ +static unsigned long counter_enabled[NUMBER_OF_EVENTS]; +static unsigned long counter_event[NUMBER_OF_EVENTS]; +static unsigned long counter_key[NUMBER_OF_EVENTS]; + +/* The data we have recorded */ +static u32 counter_data[NUMBER_OF_EVENTS]; +/* The address to sample (or 0 if samples are sent to us) */ +static u32 *counter_address[NUMBER_OF_EVENTS]; + +/* An array used to return the data we recorded + * as key,value pairs hence the *2 + */ +static unsigned long counter_dump[NUMBER_OF_EVENTS * 2]; +static unsigned long counter_prev[NUMBER_OF_EVENTS]; + +/* Note whether tracepoints have been registered */ +static int trace_registered; + +/* + * These numbers define the actual numbers of each block type that exist in the system. Initially + * these are set to the maxima defined above; if the driver is capable of being queried (newer + * drivers only) then the values may be revised. + */ +static unsigned int n_vp_cores = MAX_NUM_VP_CORES; +static unsigned int n_l2_cores = MAX_NUM_L2_CACHE_CORES; +static unsigned int n_fp_cores = MAX_NUM_FP_CORES; + +/** + * Calculate the difference and handle the overflow. + */ +static u32 get_difference(u32 start, u32 end) +{ + if (start - end >= 0) { + return start - end; + } + + // Mali counters are unsigned 32 bit values that wrap. + return (4294967295u - end) + start; +} + +/** + * Returns non-zero if the given counter ID is an activity counter. + */ +static inline int is_activity_counter(unsigned int event_id) +{ + return (event_id >= FIRST_ACTIVITY_EVENT && + event_id <= LAST_ACTIVITY_EVENT); +} + +/** + * Returns non-zero if the given counter ID is a hardware counter. + */ +static inline int is_hw_counter(unsigned int event_id) +{ + return (event_id >= FIRST_HW_COUNTER && event_id <= LAST_HW_COUNTER); +} + +/* + * These are provided for utgard compatibility. + */ +typedef void _mali_profiling_get_mali_version_type(struct _mali_profiling_mali_version *values); +typedef u32 _mali_profiling_get_l2_counters_type(_mali_profiling_l2_counter_values *values); + +#if GATOR_MALI_INTERFACE_STYLE == 2 +/** + * Returns non-zero if the given counter ID is a software counter. + */ +static inline int is_sw_counter(unsigned int event_id) +{ + return (event_id >= FIRST_SW_COUNTER && event_id <= LAST_SW_COUNTER); +} +#endif + +#if GATOR_MALI_INTERFACE_STYLE == 2 +/* + * The Mali DDK uses s64 types to contain software counter values, but gator + * can only use a maximum of 32 bits. This function scales a software counter + * to an appropriate range. + */ +static u32 scale_sw_counter_value(unsigned int event_id, signed long long value) +{ + u32 scaled_value; + + switch (event_id) { + case COUNTER_GLES_UPLOAD_TEXTURE_TIME: + case COUNTER_GLES_UPLOAD_VBO_TIME: + scaled_value = (u32)div_s64(value, 1000000); + break; + default: + scaled_value = (u32)value; + break; + } + + return scaled_value; +} +#endif + +/* Probe for continuously sampled counter */ +#if 0 //WE_DONT_CURRENTLY_USE_THIS_SO_SUPPRESS_WARNING +GATOR_DEFINE_PROBE(mali_sample_address, TP_PROTO(unsigned int event_id, u32 *addr)) +{ + /* Turning on too many pr_debug statements in frequently called functions + * can cause stability and/or performance problems + */ + //pr_debug("gator: mali_sample_address %d %d\n", event_id, addr); + if (event_id >= ACTIVITY_VP && event_id <= COUNTER_FP3_C1) { + counter_address[event_id] = addr; + } +} +#endif + +/* Probe for hardware counter events */ +GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int value)) +{ + /* Turning on too many pr_debug statements in frequently called functions + * can cause stability and/or performance problems + */ + //pr_debug("gator: mali_hw_counter %d %d\n", event_id, value); + if (is_hw_counter(event_id)) { + counter_data[event_id] = value; + } +} + +#if GATOR_MALI_INTERFACE_STYLE == 2 +GATOR_DEFINE_PROBE(mali_sw_counter, TP_PROTO(unsigned int event_id, signed long long value)) +{ + if (is_sw_counter(event_id)) { + counter_data[event_id] = scale_sw_counter_value(event_id, value); + } +} +#endif /* GATOR_MALI_INTERFACE_STYLE == 2 */ + +#if GATOR_MALI_INTERFACE_STYLE >= 3 +GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surface_id, unsigned int *counters)) +{ + u32 i; + + /* Copy over the values for those counters which are enabled. */ + for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) { + if (counter_enabled[i]) { + counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]); + } + } +} +#endif /* GATOR_MALI_INTERFACE_STYLE >= 3 */ + +/** + * Create a single filesystem entry for a specified event. + * @param sb the superblock + * @param root Filesystem root + * @param name The name of the entry to create + * @param event The ID of the event + * @param create_event_item boolean indicating whether to create an 'event' filesystem entry. True to create. + * + * @return 0 if ok, non-zero if the create failed. + */ +static int create_fs_entry(struct super_block *sb, struct dentry *root, const char *name, int event, int create_event_item) +{ + struct dentry *dir; + + dir = gatorfs_mkdir(sb, root, name); + + if (!dir) { + return -1; + } + + if (create_event_item) { + gatorfs_create_ulong(sb, dir, "event", &counter_event[event]); + } + + gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]); + gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); + + return 0; +} + +#if GATOR_MALI_INTERFACE_STYLE > 3 +/* + * Read the version info structure if available + */ +static void initialise_version_info(void) +{ + _mali_profiling_get_mali_version_type *mali_profiling_get_mali_version_symbol; + + mali_profiling_get_mali_version_symbol = symbol_get(_mali_profiling_get_mali_version); + + if (mali_profiling_get_mali_version_symbol) { + struct _mali_profiling_mali_version version_info; + + pr_debug("gator: mali online _mali_profiling_get_mali_version symbol @ %p\n", + mali_profiling_get_mali_version_symbol); + + /* + * Revise the number of each different core type using information derived from the DDK. + */ + mali_profiling_get_mali_version_symbol(&version_info); + + n_fp_cores = version_info.num_of_fp_cores; + n_vp_cores = version_info.num_of_vp_cores; + n_l2_cores = version_info.num_of_l2_cores; + + /* Release the function - we're done with it. */ + symbol_put(_mali_profiling_get_mali_version); + } else { + printk("gator: mali online _mali_profiling_get_mali_version symbol not found\n"); + } +} +#endif + +static int create_files(struct super_block *sb, struct dentry *root) +{ + int event; + const char *mali_name = gator_mali_get_mali_name(); + + char buf[40]; + int core_id; + int counter_number; + + pr_debug("gator: Initialising counters with style = %d\n", GATOR_MALI_INTERFACE_STYLE); + +#if GATOR_MALI_INTERFACE_STYLE > 3 + /* + * Initialise first: this sets up the number of cores available (on compatible DDK versions). + * Ideally this would not need guarding but other parts of the code depend on the interface style being set + * correctly; if it is not then the system can enter an inconsistent state. + */ + initialise_version_info(); +#endif + + /* Vertex processor counters */ + for (core_id = 0; core_id < n_vp_cores; core_id++) { + int activity_counter_id = ACTIVITY_VP_0; + snprintf(buf, sizeof buf, "ARM_%s_VP_%d_active", mali_name, core_id); + if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) { + return -1; + } + + for (counter_number = 0; counter_number < 2; counter_number++) { + int counter_id = COUNTER_VP_0_C0 + (2 * core_id) + counter_number; + + snprintf(buf, sizeof buf, "ARM_%s_VP_%d_cnt%d", mali_name, core_id, counter_number); + if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { + return -1; + } + } + } + + /* Fragment processors' counters */ + for (core_id = 0; core_id < n_fp_cores; core_id++) { + int activity_counter_id = ACTIVITY_FP_0 + core_id; + + snprintf(buf, sizeof buf, "ARM_%s_FP_%d_active", mali_name, core_id); + if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) { + return -1; + } + + for (counter_number = 0; counter_number < 2; counter_number++) { + int counter_id = COUNTER_FP_0_C0 + (2 * core_id) + counter_number; + + snprintf(buf, sizeof buf, "ARM_%s_FP_%d_cnt%d", mali_name, core_id, counter_number); + if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { + return -1; + } + } + } + + /* L2 Cache counters */ + for (core_id = 0; core_id < n_l2_cores; core_id++) { + for (counter_number = 0; counter_number < 2; counter_number++) { + int counter_id = COUNTER_L2_0_C0 + (2 * core_id) + counter_number; + + snprintf(buf, sizeof buf, "ARM_%s_L2_%d_cnt%d", mali_name, core_id, counter_number); + if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { + return -1; + } + } + } + + /* Now set up the software counter entries */ + for (event = FIRST_SW_COUNTER; event <= LAST_SW_COUNTER; event++) { + snprintf(buf, sizeof(buf), "ARM_%s_SW_%d", mali_name, event - FIRST_SW_COUNTER); + + if (create_fs_entry(sb, root, buf, event, 0) != 0) { + return -1; + } + } + + /* Now set up the special counter entries */ + snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip_cnt0", mali_name); + if (create_fs_entry(sb, root, buf, COUNTER_FILMSTRIP, 1) != 0) { + return -1; + } + +#ifdef DVFS_REPORTED_BY_DDK + snprintf(buf, sizeof(buf), "ARM_%s_Frequency", mali_name); + if (create_fs_entry(sb, root, buf, COUNTER_FREQUENCY, 1) != 0) { + return -1; + } + + snprintf(buf, sizeof(buf), "ARM_%s_Voltage", mali_name); + if (create_fs_entry(sb, root, buf, COUNTER_VOLTAGE, 1) != 0) { + return -1; + } +#endif + + return 0; +} + +/* + * Local store for the get_counters entry point into the DDK. + * This is stored here since it is used very regularly. + */ +static mali_profiling_get_counters_type *mali_get_counters = NULL; +static _mali_profiling_get_l2_counters_type *mali_get_l2_counters = NULL; + +/* + * Examine list of counters between two index limits and determine if any one is enabled. + * Returns 1 if any counter is enabled, 0 if none is. + */ +static int is_any_counter_enabled(unsigned int first_counter, unsigned int last_counter) +{ + unsigned int i; + + for (i = first_counter; i <= last_counter; i++) { + if (counter_enabled[i]) { + return 1; /* At least one counter is enabled */ + } + } + + return 0; /* No s/w counters enabled */ +} + +static void init_counters(unsigned int from_counter, unsigned int to_counter) +{ + unsigned int counter_id; + + /* If a Mali driver is present and exporting the appropriate symbol + * then we can request the HW counters (of which there are only 2) + * be configured to count the desired events + */ + mali_profiling_set_event_type *mali_set_hw_event; + + mali_set_hw_event = symbol_get(_mali_profiling_set_event); + + if (mali_set_hw_event) { + pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event); + + for (counter_id = from_counter; counter_id <= to_counter; counter_id++) { + if (counter_enabled[counter_id]) { + mali_set_hw_event(counter_id, counter_event[counter_id]); + } else { + mali_set_hw_event(counter_id, 0xFFFFFFFF); + } + } + + symbol_put(_mali_profiling_set_event); + } else { + printk("gator: mali online _mali_profiling_set_event symbol not found\n"); + } +} + +static void mali_counter_initialize(void) +{ + int i; + int core_id; + + mali_profiling_control_type *mali_control; + + init_counters(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores) - 1); + init_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1); + init_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1); + + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + /* The event attribute in the XML file keeps the actual frame rate. */ + unsigned int rate = counter_event[COUNTER_FILMSTRIP] & 0xff; + unsigned int resize_factor = (counter_event[COUNTER_FILMSTRIP] >> 8) & 0xff; + + pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); + + mali_control(SW_COUNTER_ENABLE, (is_any_counter_enabled(FIRST_SW_COUNTER, LAST_SW_COUNTER) ? 1 : 0)); + mali_control(FBDUMP_CONTROL_ENABLE, (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0)); + mali_control(FBDUMP_CONTROL_RATE, rate); + mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); + + pr_debug("gator: sent mali_control enabled=%d, rate=%d\n", (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0), rate); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali online _mali_profiling_control symbol not found\n"); + } + + mali_get_counters = symbol_get(_mali_profiling_get_counters); + if (mali_get_counters) { + pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", mali_get_counters); + + } else { + pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined"); + } + + mali_get_l2_counters = symbol_get(_mali_profiling_get_l2_counters); + if (mali_get_l2_counters) { + pr_debug("gator: mali online _mali_profiling_get_l2_counters symbol @ %p\n", mali_get_l2_counters); + + } else { + pr_debug("gator WARNING: mali _mali_profiling_get_l2_counters symbol not defined"); + } + + if (!mali_get_counters && !mali_get_l2_counters) { + pr_debug("gator: WARNING: no L2 counters available"); + n_l2_cores = 0; + } + + for (core_id = 0; core_id < n_l2_cores; core_id++) { + int counter_id = COUNTER_L2_0_C0 + (2 * core_id); + counter_prev[counter_id] = 0; + counter_prev[counter_id + 1] = 0; + } + + /* Clear counters in the start */ + for (i = 0; i < NUMBER_OF_EVENTS; i++) { + counter_data[i] = 0; + } +} + +static void mali_counter_deinitialize(void) +{ + mali_profiling_set_event_type *mali_set_hw_event; + mali_profiling_control_type *mali_control; + + mali_set_hw_event = symbol_get(_mali_profiling_set_event); + + if (mali_set_hw_event) { + int i; + + pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event); + for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { + mali_set_hw_event(i, 0xFFFFFFFF); + } + + symbol_put(_mali_profiling_set_event); + } else { + printk("gator: mali offline _mali_profiling_set_event symbol not found\n"); + } + + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + + if (mali_control) { + pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_control); + + /* Reset the DDK state - disable counter collection */ + mali_control(SW_COUNTER_ENABLE, 0); + + mali_control(FBDUMP_CONTROL_ENABLE, 0); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali offline _mali_profiling_control symbol not found\n"); + } + + if (mali_get_counters) { + symbol_put(_mali_profiling_get_counters); + } + + if (mali_get_l2_counters) { + symbol_put(_mali_profiling_get_l2_counters); + } +} + +static int start(void) +{ + // register tracepoints + if (GATOR_REGISTER_TRACE(mali_hw_counter)) { + printk("gator: mali_hw_counter tracepoint failed to activate\n"); + return -1; + } + +#if GATOR_MALI_INTERFACE_STYLE == 1 + /* None. */ +#elif GATOR_MALI_INTERFACE_STYLE == 2 + /* For patched Mali driver. */ + if (GATOR_REGISTER_TRACE(mali_sw_counter)) { + printk("gator: mali_sw_counter tracepoint failed to activate\n"); + return -1; + } +#elif GATOR_MALI_INTERFACE_STYLE >= 3 + /* For Mali drivers with built-in support. */ + if (GATOR_REGISTER_TRACE(mali_sw_counters)) { + printk("gator: mali_sw_counters tracepoint failed to activate\n"); + return -1; + } +#else +#error Unknown GATOR_MALI_INTERFACE_STYLE option. +#endif + + trace_registered = 1; + + mali_counter_initialize(); + return 0; +} + +static void stop(void) +{ + unsigned int cnt; + + pr_debug("gator: mali stop\n"); + + if (trace_registered) { + GATOR_UNREGISTER_TRACE(mali_hw_counter); + +#if GATOR_MALI_INTERFACE_STYLE == 1 + /* None. */ +#elif GATOR_MALI_INTERFACE_STYLE == 2 + /* For patched Mali driver. */ + GATOR_UNREGISTER_TRACE(mali_sw_counter); +#elif GATOR_MALI_INTERFACE_STYLE >= 3 + /* For Mali drivers with built-in support. */ + GATOR_UNREGISTER_TRACE(mali_sw_counters); +#else +#error Unknown GATOR_MALI_INTERFACE_STYLE option. +#endif + + pr_debug("gator: mali timeline tracepoint deactivated\n"); + + trace_registered = 0; + } + + for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) { + counter_enabled[cnt] = 0; + counter_event[cnt] = 0; + counter_address[cnt] = NULL; + } + + mali_counter_deinitialize(); +} + +static void dump_counters(unsigned int from_counter, unsigned int to_counter, unsigned int *len) +{ + unsigned int counter_id; + + for (counter_id = from_counter; counter_id <= to_counter; counter_id++) { + if (counter_enabled[counter_id]) { + counter_dump[(*len)++] = counter_key[counter_id]; + counter_dump[(*len)++] = counter_data[counter_id]; + + counter_data[counter_id] = 0; + } + } +} + +static int read(int **buffer) +{ + int len = 0; + + if (!on_primary_core()) + return 0; + + // Read the L2 C0 and C1 here. + if (n_l2_cores > 0 && is_any_counter_enabled(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores))) { + unsigned int unavailable_l2_caches = 0; + _mali_profiling_l2_counter_values cache_values; + unsigned int cache_id; + struct _mali_profiling_core_counters *per_core; + + /* Poke the driver to get the counter values - older style; only one L2 cache */ + if (mali_get_l2_counters) { + unavailable_l2_caches = mali_get_l2_counters(&cache_values); + } else if (mali_get_counters) { + per_core = &cache_values.cores[0]; + mali_get_counters(&per_core->source0, &per_core->value0, &per_core->source1, &per_core->value1); + } else { + /* This should never happen, as n_l2_caches is only set > 0 if one of the above functions is found. */ + } + + /* Fill in the two cache counter values for each cache block. */ + for (cache_id = 0; cache_id < n_l2_cores; cache_id++) { + unsigned int counter_id_0 = COUNTER_L2_0_C0 + (2 * cache_id); + unsigned int counter_id_1 = counter_id_0 + 1; + + if ((1 << cache_id) & unavailable_l2_caches) { + continue; /* This cache is unavailable (powered-off, possibly). */ + } + + per_core = &cache_values.cores[cache_id]; + + if (counter_enabled[counter_id_0]) { + // Calculate and save src0's counter val0 + counter_dump[len++] = counter_key[counter_id_0]; + counter_dump[len++] = get_difference(per_core->value0, counter_prev[counter_id_0]); + } + + if (counter_enabled[counter_id_1]) { + // Calculate and save src1's counter val1 + counter_dump[len++] = counter_key[counter_id_1]; + counter_dump[len++] = get_difference(per_core->value1, counter_prev[counter_id_1]); + } + + // Save the previous values for the counters. + counter_prev[counter_id_0] = per_core->value0; + counter_prev[counter_id_1] = per_core->value1; + } + } + + /* Process other (non-timeline) counters. */ + dump_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1, &len); + dump_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1, &len); + + dump_counters(FIRST_SW_COUNTER, LAST_SW_COUNTER, &len); + +#ifdef DVFS_REPORTED_BY_DDK + { + int cnt; + /* + * Add in the voltage and frequency counters if enabled. Note that, since these are + * actually passed as events, the counter value should not be cleared. + */ + cnt = COUNTER_FREQUENCY; + if (counter_enabled[cnt]) { + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = counter_data[cnt]; + } + + cnt = COUNTER_VOLTAGE; + if (counter_enabled[cnt]) { + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = counter_data[cnt]; + } + } +#endif + + if (buffer) { + *buffer = (int *)counter_dump; + } + + return len; +} + +static struct gator_interface gator_events_mali_interface = { + .create_files = create_files, + .start = start, + .stop = stop, + .read = read, +}; + +extern void gator_events_mali_log_dvfs_event(unsigned int frequency_mhz, unsigned int voltage_mv) +{ +#ifdef DVFS_REPORTED_BY_DDK + counter_data[COUNTER_FREQUENCY] = frequency_mhz; + counter_data[COUNTER_VOLTAGE] = voltage_mv; +#endif +} + +int gator_events_mali_init(void) +{ + unsigned int cnt; + + pr_debug("gator: mali init\n"); + + for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) { + counter_enabled[cnt] = 0; + counter_event[cnt] = 0; + counter_key[cnt] = gator_events_get_key(); + counter_address[cnt] = NULL; + counter_data[cnt] = 0; + } + + trace_registered = 0; + + return gator_events_install(&gator_events_mali_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_mali_4xx.h linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_4xx.h --- linux-3.10.30/drivers/gator/gator_events_mali_4xx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_4xx.h 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,18 @@ +/** + * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * + * 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. + * + */ + +/* + * Header contains common definitions for the Mali-4xx processors. + */ +#if !defined(GATOR_EVENTS_MALI_4xx_H) +#define GATOR_EVENTS_MALI_4xx_H + +extern void gator_events_mali_log_dvfs_event(unsigned int d0, unsigned int d1); + +#endif /* GATOR_EVENTS_MALI_4xx_H */ diff -Nur linux-3.10.30/drivers/gator/gator_events_mali_common.c linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_common.c --- linux-3.10.30/drivers/gator/gator_events_mali_common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_common.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,81 @@ +/** + * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * + * 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 "gator_events_mali_common.h" + +static u32 gator_mali_get_id(void) +{ + return MALI_SUPPORT; +} + +extern const char *gator_mali_get_mali_name(void) +{ + u32 id = gator_mali_get_id(); + + switch (id) { + case MALI_T6xx: + return "Mali-T6xx"; + case MALI_4xx: + return "Mali-4xx"; + default: + pr_debug("gator: Mali-T6xx: unknown Mali ID (%d)\n", id); + return "Mali-Unknown"; + } +} + +extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event) +{ + int err; + char buf[255]; + struct dentry *dir; + + /* If the counter name is empty ignore it */ + if (strlen(event_name) != 0) { + /* Set up the filesystem entry for this event. */ + snprintf(buf, sizeof(buf), "ARM_%s_%s", mali_name, event_name); + + dir = gatorfs_mkdir(sb, root, buf); + + if (dir == NULL) { + pr_debug("gator: Mali-T6xx: error creating file system for: %s (%s)", event_name, buf); + return -1; + } + + err = gatorfs_create_ulong(sb, dir, "enabled", &counter->enabled); + if (err != 0) { + pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ulong for: %s (%s)", event_name, buf); + return -1; + } + err = gatorfs_create_ro_ulong(sb, dir, "key", &counter->key); + if (err != 0) { + pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ro_ulong for: %s (%s)", event_name, buf); + return -1; + } + if (event != NULL) { + err = gatorfs_create_ulong(sb, dir, "event", event); + if (err != 0) { + pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ro_ulong for: %s (%s)", event_name, buf); + return -1; + } + } + } + + return 0; +} + +extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters) +{ + unsigned int cnt; + + for (cnt = 0; cnt < n_counters; cnt++) { + mali_counter *counter = &counters[cnt]; + + counter->key = gator_events_get_key(); + counter->enabled = 0; + } +} diff -Nur linux-3.10.30/drivers/gator/gator_events_mali_common.h linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_common.h --- linux-3.10.30/drivers/gator/gator_events_mali_common.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_common.h 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,86 @@ +/** + * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * + * 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. + * + */ + +#if !defined(GATOR_EVENTS_MALI_COMMON_H) +#define GATOR_EVENTS_MALI_COMMON_H + +#include "gator.h" + +#include +#include +#include +#include +#include + +/* Device codes for each known GPU */ +#define MALI_4xx (0x0b07) +#define MALI_T6xx (0x0056) + +/* Ensure that MALI_SUPPORT has been defined to something. */ +#ifndef MALI_SUPPORT +#error MALI_SUPPORT not defined! +#endif + +/* Values for the supported activity event types */ +#define ACTIVITY_START (1) +#define ACTIVITY_STOP (2) + +/* + * Runtime state information for a counter. + */ +typedef struct { + unsigned long key; /* 'key' (a unique id set by gatord and returned by gator.ko) */ + unsigned long enabled; /* counter enable state */ +} mali_counter; + +/* + * Mali-4xx + */ +typedef int mali_profiling_set_event_type(unsigned int, int); +typedef void mali_profiling_control_type(unsigned int, unsigned int); +typedef void mali_profiling_get_counters_type(unsigned int *, unsigned int *, unsigned int *, unsigned int *); + +/* + * Driver entry points for functions called directly by gator. + */ +extern int _mali_profiling_set_event(unsigned int, int); +extern void _mali_profiling_control(unsigned int, unsigned int); +extern void _mali_profiling_get_counters(unsigned int *, unsigned int *, unsigned int *, unsigned int *); + +/** + * Returns a name which identifies the GPU type (eg Mali-4xx, Mali-T6xx). + * + * @return The name as a constant string. + */ +extern const char *gator_mali_get_mali_name(void); + +/** + * Creates a filesystem entry under /dev/gator relating to the specified event name and key, and + * associate the key/enable values with this entry point. + * + * @param mali_name A name related to the type of GPU, obtained from a call to gator_mali_get_mali_name() + * @param event_name The name of the event. + * @param sb Linux super block + * @param root Directory under which the entry will be created. + * @param counter_key Ptr to location which will be associated with the counter key. + * @param counter_enabled Ptr to location which will be associated with the counter enable state. + * + * @return 0 if entry point was created, non-zero if not. + */ +extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event); + +/** + * Initializes the counter array. + * + * @param keys The array of counters + * @param n_counters The number of entries in each of the arrays. + */ +extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters); + +#endif /* GATOR_EVENTS_MALI_COMMON_H */ diff -Nur linux-3.10.30/drivers/gator/gator_events_mali_t6xx.c linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_t6xx.c --- linux-3.10.30/drivers/gator/gator_events_mali_t6xx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_t6xx.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,560 @@ +/** + * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * + * 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 "gator.h" + +#include +#include +#include +#include +#include + +#include "linux/mali_linux_trace.h" + +#include "gator_events_mali_common.h" + +/* + * Check that the MALI_SUPPORT define is set to one of the allowable device codes. + */ +#if (MALI_SUPPORT != MALI_T6xx) +#error MALI_SUPPORT set to an invalid device code: expecting MALI_T6xx +#endif + +/* Counters for Mali-T6xx: + * + * - Timeline events + * They are tracepoints, but instead of reporting a number they report a START/STOP event. + * They are reported in Streamline as number of microseconds while that particular counter was active. + * + * - SW counters + * They are tracepoints reporting a particular number. + * They are accumulated in sw_counter_data array until they are passed to Streamline, then they are zeroed. + * + * - Accumulators + * They are the same as software counters but their value is not zeroed. + */ + +/* Timeline (start/stop) activity */ +static const char *timeline_event_names[] = { + "PM_SHADER_0", + "PM_SHADER_1", + "PM_SHADER_2", + "PM_SHADER_3", + "PM_SHADER_4", + "PM_SHADER_5", + "PM_SHADER_6", + "PM_SHADER_7", + "PM_TILER_0", + "PM_L2_0", + "PM_L2_1", + "MMU_AS_0", + "MMU_AS_1", + "MMU_AS_2", + "MMU_AS_3" +}; + +enum { + PM_SHADER_0 = 0, + PM_SHADER_1, + PM_SHADER_2, + PM_SHADER_3, + PM_SHADER_4, + PM_SHADER_5, + PM_SHADER_6, + PM_SHADER_7, + PM_TILER_0, + PM_L2_0, + PM_L2_1, + MMU_AS_0, + MMU_AS_1, + MMU_AS_2, + MMU_AS_3 +}; +/* The number of shader blocks in the enum above */ +#define NUM_PM_SHADER (8) + +/* Software Counters */ +static const char *software_counter_names[] = { + "MMU_PAGE_FAULT_0", + "MMU_PAGE_FAULT_1", + "MMU_PAGE_FAULT_2", + "MMU_PAGE_FAULT_3" +}; + +enum { + MMU_PAGE_FAULT_0 = 0, + MMU_PAGE_FAULT_1, + MMU_PAGE_FAULT_2, + MMU_PAGE_FAULT_3 +}; + +/* Software Counters */ +static const char *accumulators_names[] = { + "TOTAL_ALLOC_PAGES" +}; + +enum { + TOTAL_ALLOC_PAGES = 0 +}; + +#define FIRST_TIMELINE_EVENT (0) +#define NUMBER_OF_TIMELINE_EVENTS (sizeof(timeline_event_names) / sizeof(timeline_event_names[0])) +#define FIRST_SOFTWARE_COUNTER (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS) +#define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0])) +#define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS) +#define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0])) +#define FILMSTRIP (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS) +#define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS + 1) + +/* + * gatorfs variables for counter enable state + */ +static mali_counter counters[NUMBER_OF_EVENTS]; +static unsigned long filmstrip_event; + +/* An array used to return the data we recorded + * as key,value pairs hence the *2 + */ +static unsigned long counter_dump[NUMBER_OF_EVENTS * 2]; + +/* + * Array holding counter start times (in ns) for each counter. A zero here + * indicates that the activity monitored by this counter is not running. + */ +static struct timespec timeline_event_starttime[NUMBER_OF_TIMELINE_EVENTS]; + +/* The data we have recorded */ +static unsigned int timeline_data[NUMBER_OF_TIMELINE_EVENTS]; +static unsigned int sw_counter_data[NUMBER_OF_SOFTWARE_COUNTERS]; +static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS]; + +/* Hold the previous timestamp, used to calculate the sample interval. */ +static struct timespec prev_timestamp; + +/** + * Returns the timespan (in microseconds) between the two specified timestamps. + * + * @param start Ptr to the start timestamp + * @param end Ptr to the end timestamp + * + * @return Number of microseconds between the two timestamps (can be negative if start follows end). + */ +static inline long get_duration_us(const struct timespec *start, const struct timespec *end) +{ + long event_duration_us = (end->tv_nsec - start->tv_nsec) / 1000; + event_duration_us += (end->tv_sec - start->tv_sec) * 1000000; + + return event_duration_us; +} + +static void record_timeline_event(unsigned int timeline_index, unsigned int type) +{ + struct timespec event_timestamp; + struct timespec *event_start = &timeline_event_starttime[timeline_index]; + + switch (type) { + case ACTIVITY_START: + /* Get the event time... */ + getnstimeofday(&event_timestamp); + + /* Remember the start time if the activity is not already started */ + if (event_start->tv_sec == 0) { + *event_start = event_timestamp; /* Structure copy */ + } + break; + + case ACTIVITY_STOP: + /* if the counter was started... */ + if (event_start->tv_sec != 0) { + /* Get the event time... */ + getnstimeofday(&event_timestamp); + + /* Accumulate the duration in us */ + timeline_data[timeline_index] += get_duration_us(event_start, &event_timestamp); + + /* Reset the start time to indicate the activity is stopped. */ + event_start->tv_sec = 0; + } + break; + + default: + /* Other activity events are ignored. */ + break; + } +} + +/* + * Documentation about the following tracepoints is in mali_linux_trace.h + */ + +GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long long value)) +{ +#define SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */ +#define TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */ +#define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */ +#define BIT_AT(value, pos) ((value >> pos) & 1) + + static unsigned long long previous_shader_bitmask = 0; + static unsigned long long previous_tiler_bitmask = 0; + static unsigned long long previous_l2_bitmask = 0; + + switch (event_id) { + case SHADER_PRESENT_LO: + { + unsigned long long changed_bitmask = previous_shader_bitmask ^ value; + int pos; + + for (pos = 0; pos < NUM_PM_SHADER; ++pos) { + if (BIT_AT(changed_bitmask, pos)) { + record_timeline_event(PM_SHADER_0 + pos, BIT_AT(value, pos) ? ACTIVITY_START : ACTIVITY_STOP); + } + } + + previous_shader_bitmask = value; + break; + } + + case TILER_PRESENT_LO: + { + unsigned long long changed = previous_tiler_bitmask ^ value; + + if (BIT_AT(changed, 0)) { + record_timeline_event(PM_TILER_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP); + } + + previous_tiler_bitmask = value; + break; + } + + case L2_PRESENT_LO: + { + unsigned long long changed = previous_l2_bitmask ^ value; + + if (BIT_AT(changed, 0)) { + record_timeline_event(PM_L2_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP); + } + if (BIT_AT(changed, 4)) { + record_timeline_event(PM_L2_1, BIT_AT(value, 4) ? ACTIVITY_START : ACTIVITY_STOP); + } + + previous_l2_bitmask = value; + break; + } + + default: + /* No other blocks are supported at present */ + break; + } + +#undef SHADER_PRESENT_LO +#undef TILER_PRESENT_LO +#undef L2_PRESENT_LO +#undef BIT_AT +} + +GATOR_DEFINE_PROBE(mali_page_fault_insert_pages, TP_PROTO(int event_id, unsigned long value)) +{ + /* We add to the previous since we may receive many tracepoints in one sample period */ + sw_counter_data[MMU_PAGE_FAULT_0 + event_id] += value; +} + +GATOR_DEFINE_PROBE(mali_mmu_as_in_use, TP_PROTO(int event_id)) +{ + record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_START); +} + +GATOR_DEFINE_PROBE(mali_mmu_as_released, TP_PROTO(int event_id)) +{ + record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_STOP); +} + +GATOR_DEFINE_PROBE(mali_total_alloc_pages_change, TP_PROTO(long long int event_id)) +{ + accumulators_data[TOTAL_ALLOC_PAGES] = event_id; +} + +static int create_files(struct super_block *sb, struct dentry *root) +{ + int event; + /* + * Create the filesystem for all events + */ + int counter_index = 0; + const char *mali_name = gator_mali_get_mali_name(); + mali_profiling_control_type *mali_control; + + for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) { + if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event], NULL) != 0) { + return -1; + } + counter_index++; + } + counter_index = 0; + for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) { + if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) { + return -1; + } + counter_index++; + } + counter_index = 0; + for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) { + if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event], NULL) != 0) { + return -1; + } + counter_index++; + } + + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + if (gator_mali_create_file_system(mali_name, "Filmstrip_cnt0", sb, root, &counters[FILMSTRIP], &filmstrip_event) != 0) { + return -1; + } + symbol_put(_mali_profiling_control); + } + + return 0; +} + +static int register_tracepoints(void) +{ + if (GATOR_REGISTER_TRACE(mali_pm_status)) { + pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) { + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) { + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) { + pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) { + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint failed to activate\n"); + return 0; + } + + pr_debug("gator: Mali-T6xx: start\n"); + pr_debug("gator: Mali-T6xx: mali_pm_status probe is at %p\n", &probe_mali_pm_status); + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages probe is at %p\n", &probe_mali_page_fault_insert_pages); + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use probe is at %p\n", &probe_mali_mmu_as_in_use); + pr_debug("gator: Mali-T6xx: mali_mmu_as_released probe is at %p\n", &probe_mali_mmu_as_released); + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change probe is at %p\n", &probe_mali_total_alloc_pages_change); + + return 1; +} + +static int start(void) +{ + unsigned int cnt; + mali_profiling_control_type *mali_control; + + /* Clean all data for the next capture */ + for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) { + timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0; + timeline_data[cnt] = 0; + } + + for (cnt = 0; cnt < NUMBER_OF_SOFTWARE_COUNTERS; cnt++) { + sw_counter_data[cnt] = 0; + } + + for (cnt = 0; cnt < NUMBER_OF_ACCUMULATORS; cnt++) { + accumulators_data[cnt] = 0; + } + + /* Register tracepoints */ + if (register_tracepoints() == 0) { + return -1; + } + + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + /* The event attribute in the XML file keeps the actual frame rate. */ + unsigned int enabled = counters[FILMSTRIP].enabled ? 1 : 0; + unsigned int rate = filmstrip_event & 0xff; + unsigned int resize_factor = (filmstrip_event >> 8) & 0xff; + + pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); + +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + mali_control(FBDUMP_CONTROL_ENABLE, enabled); + mali_control(FBDUMP_CONTROL_RATE, rate); + mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); + + pr_debug("gator: sent mali_control enabled=%d, rate=%d, resize_factor=%d\n", enabled, rate, resize_factor); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali online _mali_profiling_control symbol not found\n"); + } + + /* + * Set the first timestamp for calculating the sample interval. The first interval could be quite long, + * since it will be the time between 'start' and the first 'read'. + * This means that timeline values will be divided by a big number for the first sample. + */ + getnstimeofday(&prev_timestamp); + + return 0; +} + +static void stop(void) +{ + mali_profiling_control_type *mali_control; + + pr_debug("gator: Mali-T6xx: stop\n"); + + /* + * It is safe to unregister traces even if they were not successfully + * registered, so no need to check. + */ + GATOR_UNREGISTER_TRACE(mali_pm_status); + pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_page_fault_insert_pages); + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_mmu_as_in_use); + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_mmu_as_released); + pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change); + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint deactivated\n"); + + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_control); + + mali_control(FBDUMP_CONTROL_ENABLE, 0); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali offline _mali_profiling_control symbol not found\n"); + } +} + +static int read(int **buffer) +{ + int cnt; + int len = 0; + long sample_interval_us = 0; + struct timespec read_timestamp; + + if (!on_primary_core()) { + return 0; + } + + /* Get the start of this sample period. */ + getnstimeofday(&read_timestamp); + + /* + * Calculate the sample interval if the previous sample time is valid. + * We use tv_sec since it will not be 0. + */ + if (prev_timestamp.tv_sec != 0) { + sample_interval_us = get_duration_us(&prev_timestamp, &read_timestamp); + } + + /* Structure copy. Update the previous timestamp. */ + prev_timestamp = read_timestamp; + + /* + * Report the timeline counters (ACTIVITY_START/STOP) + */ + for (cnt = FIRST_TIMELINE_EVENT; cnt < (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS); cnt++) { + mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + const int index = cnt - FIRST_TIMELINE_EVENT; + unsigned int value; + + /* If the activity is still running, reset its start time to the start of this sample period + * to correct the count. Add the time up to the end of the sample onto the count. */ + if (timeline_event_starttime[index].tv_sec != 0) { + const long event_duration = get_duration_us(&timeline_event_starttime[index], &read_timestamp); + timeline_data[index] += event_duration; + timeline_event_starttime[index] = read_timestamp; /* Activity is still running. */ + } + + if (sample_interval_us != 0) { + /* Convert the counter to a percent-of-sample value */ + value = (timeline_data[index] * 100) / sample_interval_us; + } else { + pr_debug("gator: Mali-T6xx: setting value to zero\n"); + value = 0; + } + + /* Clear the counter value ready for the next sample. */ + timeline_data[index] = 0; + + counter_dump[len++] = counter->key; + counter_dump[len++] = value; + } + } + + /* Report the software counters */ + for (cnt = FIRST_SOFTWARE_COUNTER; cnt < (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS); cnt++) { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + const int index = cnt - FIRST_SOFTWARE_COUNTER; + counter_dump[len++] = counter->key; + counter_dump[len++] = sw_counter_data[index]; + /* Set the value to zero for the next time */ + sw_counter_data[index] = 0; + } + } + + /* Report the accumulators */ + for (cnt = FIRST_ACCUMULATOR; cnt < (FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS); cnt++) { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + const int index = cnt - FIRST_ACCUMULATOR; + counter_dump[len++] = counter->key; + counter_dump[len++] = accumulators_data[index]; + /* Do not zero the accumulator */ + } + } + + /* Update the buffer */ + if (buffer) { + *buffer = (int *)counter_dump; + } + + return len; +} + +static struct gator_interface gator_events_mali_t6xx_interface = { + .create_files = create_files, + .start = start, + .stop = stop, + .read = read +}; + +extern int gator_events_mali_t6xx_init(void) +{ + pr_debug("gator: Mali-T6xx: sw_counters init\n"); + + gator_mali_initialise_counters(counters, NUMBER_OF_EVENTS); + + return gator_events_install(&gator_events_mali_t6xx_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_mali_t6xx_hw.c linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_t6xx_hw.c --- linux-3.10.30/drivers/gator/gator_events_mali_t6xx_hw.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_t6xx_hw.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,784 @@ +/** + * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * + * 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 "gator.h" + +#include +#include +#include +#include +#include + +/* Mali T6xx DDK includes */ +#include "linux/mali_linux_trace.h" +#include "kbase/src/common/mali_kbase.h" +#include "kbase/src/linux/mali_kbase_mem_linux.h" + +#include "gator_events_mali_common.h" + +/* If API version is not specified then assume API version 1. */ +#ifndef MALI_DDK_GATOR_API_VERSION +#define MALI_DDK_GATOR_API_VERSION 1 +#endif + +#if (MALI_DDK_GATOR_API_VERSION != 1) && (MALI_DDK_GATOR_API_VERSION != 2) +#error MALI_DDK_GATOR_API_VERSION is invalid (must be 1 for r1/r2 DDK, or 2 for r3 DDK). +#endif + +/* + * Mali-T6xx + */ +typedef struct kbase_device *kbase_find_device_type(int); +typedef kbase_context *kbase_create_context_type(kbase_device *); +typedef void kbase_destroy_context_type(kbase_context *); + +#if MALI_DDK_GATOR_API_VERSION == 1 +typedef void *kbase_va_alloc_type(kbase_context *, u32); +typedef void kbase_va_free_type(kbase_context *, void *); +#elif MALI_DDK_GATOR_API_VERSION == 2 +typedef void *kbase_va_alloc_type(kbase_context *, u32, kbase_hwc_dma_mapping * handle); +typedef void kbase_va_free_type(kbase_context *, kbase_hwc_dma_mapping * handle); +#endif + +typedef mali_error kbase_instr_hwcnt_enable_type(kbase_context *, kbase_uk_hwcnt_setup *); +typedef mali_error kbase_instr_hwcnt_disable_type(kbase_context *); +typedef mali_error kbase_instr_hwcnt_clear_type(kbase_context *); +typedef mali_error kbase_instr_hwcnt_dump_irq_type(kbase_context *); +typedef mali_bool kbase_instr_hwcnt_dump_complete_type(kbase_context *, mali_bool *); + +static kbase_find_device_type *kbase_find_device_symbol; +static kbase_create_context_type *kbase_create_context_symbol; +static kbase_va_alloc_type *kbase_va_alloc_symbol; +static kbase_instr_hwcnt_enable_type *kbase_instr_hwcnt_enable_symbol; +static kbase_instr_hwcnt_clear_type *kbase_instr_hwcnt_clear_symbol; +static kbase_instr_hwcnt_dump_irq_type *kbase_instr_hwcnt_dump_irq_symbol; +static kbase_instr_hwcnt_dump_complete_type *kbase_instr_hwcnt_dump_complete_symbol; +static kbase_instr_hwcnt_disable_type *kbase_instr_hwcnt_disable_symbol; +static kbase_va_free_type *kbase_va_free_symbol; +static kbase_destroy_context_type *kbase_destroy_context_symbol; + +static long shader_present_low = 0; + +/** The interval between reads, in ns. + * + * Earlier we introduced + * a 'hold off for 1ms after last read' to resolve MIDBASE-2178 and MALINE-724. + * However, the 1ms hold off is too long if no context switches occur as there is a race + * between this value and the tick of the read clock in gator which is also 1ms. If we 'miss' the + * current read, the counter values are effectively 'spread' over 2ms and the values seen are half + * what they should be (since Streamline averages over sample time). In the presence of context switches + * this spread can vary and markedly affect the counters. Currently there is no 'proper' solution to + * this, but empirically we have found that reducing the minimum read interval to 950us causes the + * counts to be much more stable. + */ +static const int READ_INTERVAL_NSEC = 950000; + +#if GATOR_TEST +#include "gator_events_mali_t6xx_hw_test.c" +#endif + +/* Blocks for HW counters */ +enum { + JM_BLOCK = 0, + TILER_BLOCK, + SHADER_BLOCK, + MMU_BLOCK +}; + +/* Counters for Mali-T6xx: + * + * - HW counters, 4 blocks + * For HW counters we need strings to create /dev/gator/events files. + * Enums are not needed because the position of the HW name in the array is the same + * of the corresponding value in the received block of memory. + * HW counters are requested by calculating a bitmask, passed then to the driver. + * Every millisecond a HW counters dump is requested, and if the previous has been completed they are read. + */ + +/* Hardware Counters */ +static const char *const hardware_counter_names[] = { + /* Job Manager */ + "", + "", + "", + "", + "MESSAGES_SENT", + "MESSAGES_RECEIVED", + "GPU_ACTIVE", /* 6 */ + "IRQ_ACTIVE", + "JS0_JOBS", + "JS0_TASKS", + "JS0_ACTIVE", + "", + "JS0_WAIT_READ", + "JS0_WAIT_ISSUE", + "JS0_WAIT_DEPEND", + "JS0_WAIT_FINISH", + "JS1_JOBS", + "JS1_TASKS", + "JS1_ACTIVE", + "", + "JS1_WAIT_READ", + "JS1_WAIT_ISSUE", + "JS1_WAIT_DEPEND", + "JS1_WAIT_FINISH", + "JS2_JOBS", + "JS2_TASKS", + "JS2_ACTIVE", + "", + "JS2_WAIT_READ", + "JS2_WAIT_ISSUE", + "JS2_WAIT_DEPEND", + "JS2_WAIT_FINISH", + "JS3_JOBS", + "JS3_TASKS", + "JS3_ACTIVE", + "", + "JS3_WAIT_READ", + "JS3_WAIT_ISSUE", + "JS3_WAIT_DEPEND", + "JS3_WAIT_FINISH", + "JS4_JOBS", + "JS4_TASKS", + "JS4_ACTIVE", + "", + "JS4_WAIT_READ", + "JS4_WAIT_ISSUE", + "JS4_WAIT_DEPEND", + "JS4_WAIT_FINISH", + "JS5_JOBS", + "JS5_TASKS", + "JS5_ACTIVE", + "", + "JS5_WAIT_READ", + "JS5_WAIT_ISSUE", + "JS5_WAIT_DEPEND", + "JS5_WAIT_FINISH", + "JS6_JOBS", + "JS6_TASKS", + "JS6_ACTIVE", + "", + "JS6_WAIT_READ", + "JS6_WAIT_ISSUE", + "JS6_WAIT_DEPEND", + "JS6_WAIT_FINISH", + + /*Tiler */ + "", + "", + "", + "JOBS_PROCESSED", + "TRIANGLES", + "QUADS", + "POLYGONS", + "POINTS", + "LINES", + "VCACHE_HIT", + "VCACHE_MISS", + "FRONT_FACING", + "BACK_FACING", + "PRIM_VISIBLE", + "PRIM_CULLED", + "PRIM_CLIPPED", + "LEVEL0", + "LEVEL1", + "LEVEL2", + "LEVEL3", + "LEVEL4", + "LEVEL5", + "LEVEL6", + "LEVEL7", + "COMMAND_1", + "COMMAND_2", + "COMMAND_3", + "COMMAND_4", + "COMMAND_4_7", + "COMMAND_8_15", + "COMMAND_16_63", + "COMMAND_64", + "COMPRESS_IN", + "COMPRESS_OUT", + "COMPRESS_FLUSH", + "TIMESTAMPS", + "PCACHE_HIT", + "PCACHE_MISS", + "PCACHE_LINE", + "PCACHE_STALL", + "WRBUF_HIT", + "WRBUF_MISS", + "WRBUF_LINE", + "WRBUF_PARTIAL", + "WRBUF_STALL", + "ACTIVE", + "LOADING_DESC", + "INDEX_WAIT", + "INDEX_RANGE_WAIT", + "VERTEX_WAIT", + "PCACHE_WAIT", + "WRBUF_WAIT", + "BUS_READ", + "BUS_WRITE", + "", + "", + "", + "", + "", + "UTLB_STALL", + "UTLB_REPLAY_MISS", + "UTLB_REPLAY_FULL", + "UTLB_NEW_MISS", + "UTLB_HIT", + + /* Shader Core */ + "", + "", + "", + "SHADER_CORE_ACTIVE", + "FRAG_ACTIVE", + "FRAG_PRIMATIVES", + "FRAG_PRIMATIVES_DROPPED", + "FRAG_CYCLE_DESC", + "FRAG_CYCLES_PLR", + "FRAG_CYCLES_VERT", + "FRAG_CYCLES_TRISETUP", + "FRAG_CYCLES_RAST", + "FRAG_THREADS", + "FRAG_DUMMY_THREADS", + "FRAG_QUADS_RAST", + "FRAG_QUADS_EZS_TEST", + "FRAG_QUADS_EZS_KILLED", + "FRAG_QUADS_LZS_TEST", + "FRAG_QUADS_LZS_KILLED", + "FRAG_CYCLE_NO_TILE", + "FRAG_NUM_TILES", + "FRAG_TRANS_ELIM", + "COMPUTE_ACTIVE", + "COMPUTE_TASKS", + "COMPUTE_THREADS", + "COMPUTE_CYCLES_DESC", + "TRIPIPE_ACTIVE", + "ARITH_WORDS", + "ARITH_CYCLES_REG", + "ARITH_CYCLES_L0", + "ARITH_FRAG_DEPEND", + "LS_WORDS", + "LS_ISSUES", + "LS_RESTARTS", + "LS_REISSUES_MISS", + "LS_REISSUES_VD", + "LS_REISSUE_ATTRIB_MISS", + "LS_NO_WB", + "TEX_WORDS", + "TEX_BUBBLES", + "TEX_WORDS_L0", + "TEX_WORDS_DESC", + "TEX_THREADS", + "TEX_RECIRC_FMISS", + "TEX_RECIRC_DESC", + "TEX_RECIRC_MULTI", + "TEX_RECIRC_PMISS", + "TEX_RECIRC_CONF", + "LSC_READ_HITS", + "LSC_READ_MISSES", + "LSC_WRITE_HITS", + "LSC_WRITE_MISSES", + "LSC_ATOMIC_HITS", + "LSC_ATOMIC_MISSES", + "LSC_LINE_FETCHES", + "LSC_DIRTY_LINE", + "LSC_SNOOPS", + "AXI_TLB_STALL", + "AXI_TLB_MIESS", + "AXI_TLB_TRANSACTION", + "LS_TLB_MISS", + "LS_TLB_HIT", + "AXI_BEATS_READ", + "AXI_BEATS_WRITTEN", + + /*L2 and MMU */ + "", + "", + "", + "", + "MMU_HIT", + "MMU_NEW_MISS", + "MMU_REPLAY_FULL", + "MMU_REPLAY_MISS", + "MMU_TABLE_WALK", + "", + "", + "", + "", + "", + "", + "", + "UTLB_HIT", + "UTLB_NEW_MISS", + "UTLB_REPLAY_FULL", + "UTLB_REPLAY_MISS", + "UTLB_STALL", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "L2_WRITE_BEATS", + "L2_READ_BEATS", + "L2_ANY_LOOKUP", + "L2_READ_LOOKUP", + "L2_SREAD_LOOKUP", + "L2_READ_REPLAY", + "L2_READ_SNOOP", + "L2_READ_HIT", + "L2_CLEAN_MISS", + "L2_WRITE_LOOKUP", + "L2_SWRITE_LOOKUP", + "L2_WRITE_REPLAY", + "L2_WRITE_SNOOP", + "L2_WRITE_HIT", + "L2_EXT_READ_FULL", + "L2_EXT_READ_HALF", + "L2_EXT_WRITE_FULL", + "L2_EXT_WRITE_HALF", + "L2_EXT_READ", + "L2_EXT_READ_LINE", + "L2_EXT_WRITE", + "L2_EXT_WRITE_LINE", + "L2_EXT_WRITE_SMALL", + "L2_EXT_BARRIER", + "L2_EXT_AR_STALL", + "L2_EXT_R_BUF_FULL", + "L2_EXT_RD_BUF_FULL", + "L2_EXT_R_RAW", + "L2_EXT_W_STALL", + "L2_EXT_W_BUF_FULL", + "L2_EXT_R_W_HAZARD", + "L2_TAG_HAZARD", + "L2_SNOOP_FULL", + "L2_REPLAY_FULL" +}; + +#define NUMBER_OF_HARDWARE_COUNTERS (sizeof(hardware_counter_names) / sizeof(hardware_counter_names[0])) + +#define GET_HW_BLOCK(c) (((c) >> 6) & 0x3) +#define GET_COUNTER_OFFSET(c) ((c) & 0x3f) + +/* Memory to dump hardware counters into */ +static void *kernel_dump_buffer; + +#if MALI_DDK_GATOR_API_VERSION == 2 +/* DMA state used to manage lifetime of the buffer */ +kbase_hwc_dma_mapping kernel_dump_buffer_handle; +#endif + +/* kbase context and device */ +static kbase_context *kbcontext = NULL; +static struct kbase_device *kbdevice = NULL; + +/* + * The following function has no external prototype in older DDK revisions. When the DDK + * is updated then this should be removed. + */ +struct kbase_device *kbase_find_device(int minor); + +static volatile bool kbase_device_busy = false; +static unsigned int num_hardware_counters_enabled; + +/* + * gatorfs variables for counter enable state + */ +static mali_counter counters[NUMBER_OF_HARDWARE_COUNTERS]; + +/* An array used to return the data we recorded + * as key,value pairs hence the *2 + */ +static unsigned long counter_dump[NUMBER_OF_HARDWARE_COUNTERS * 2]; + +#define SYMBOL_GET(FUNCTION, ERROR_COUNT) \ + if(FUNCTION ## _symbol) \ + { \ + printk("gator: mali " #FUNCTION " symbol was already registered\n"); \ + (ERROR_COUNT)++; \ + } \ + else \ + { \ + FUNCTION ## _symbol = symbol_get(FUNCTION); \ + if(! FUNCTION ## _symbol) \ + { \ + printk("gator: mali online " #FUNCTION " symbol not found\n"); \ + (ERROR_COUNT)++; \ + } \ + } + +#define SYMBOL_CLEANUP(FUNCTION) \ + if(FUNCTION ## _symbol) \ + { \ + symbol_put(FUNCTION); \ + FUNCTION ## _symbol = NULL; \ + } + +/** + * Execute symbol_get for all the Mali symbols and check for success. + * @return the number of symbols not loaded. + */ +static int init_symbols(void) +{ + int error_count = 0; + SYMBOL_GET(kbase_find_device, error_count); + SYMBOL_GET(kbase_create_context, error_count); + SYMBOL_GET(kbase_va_alloc, error_count); + SYMBOL_GET(kbase_instr_hwcnt_enable, error_count); + SYMBOL_GET(kbase_instr_hwcnt_clear, error_count); + SYMBOL_GET(kbase_instr_hwcnt_dump_irq, error_count); + SYMBOL_GET(kbase_instr_hwcnt_dump_complete, error_count); + SYMBOL_GET(kbase_instr_hwcnt_disable, error_count); + SYMBOL_GET(kbase_va_free, error_count); + SYMBOL_GET(kbase_destroy_context, error_count); + + return error_count; +} + +/** + * Execute symbol_put for all the registered Mali symbols. + */ +static void clean_symbols(void) +{ + SYMBOL_CLEANUP(kbase_find_device); + SYMBOL_CLEANUP(kbase_create_context); + SYMBOL_CLEANUP(kbase_va_alloc); + SYMBOL_CLEANUP(kbase_instr_hwcnt_enable); + SYMBOL_CLEANUP(kbase_instr_hwcnt_clear); + SYMBOL_CLEANUP(kbase_instr_hwcnt_dump_irq); + SYMBOL_CLEANUP(kbase_instr_hwcnt_dump_complete); + SYMBOL_CLEANUP(kbase_instr_hwcnt_disable); + SYMBOL_CLEANUP(kbase_va_free); + SYMBOL_CLEANUP(kbase_destroy_context); +} + +/** + * Determines whether a read should take place + * @param current_time The current time, obtained from getnstimeofday() + * @param prev_time_s The number of seconds at the previous read attempt. + * @param next_read_time_ns The time (in ns) when the next read should be allowed. + * + * Note that this function has been separated out here to allow it to be tested. + */ +static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns) +{ + /* If the current ns count rolls over a second, roll the next read time too. */ + if (current_time->tv_sec != *prev_time_s) { + *next_read_time_ns = *next_read_time_ns - NSEC_PER_SEC; + } + + /* Abort the read if the next read time has not arrived. */ + if (current_time->tv_nsec < *next_read_time_ns) { + return 0; + } + + /* Set the next read some fixed time after this one, and update the read timestamp. */ + *next_read_time_ns = current_time->tv_nsec + READ_INTERVAL_NSEC; + + *prev_time_s = current_time->tv_sec; + return 1; +} + +static int start(void) +{ + kbase_uk_hwcnt_setup setup; + mali_error err; + int cnt; + u16 bitmask[] = { 0, 0, 0, 0 }; + unsigned long long shadersPresent = 0; + + /* Setup HW counters */ + num_hardware_counters_enabled = 0; + + if (NUMBER_OF_HARDWARE_COUNTERS != 256) { + pr_debug("Unexpected number of hardware counters defined: expecting 256, got %d\n", NUMBER_OF_HARDWARE_COUNTERS); + } + + /* Calculate enable bitmasks based on counters_enabled array */ + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + int block = GET_HW_BLOCK(cnt); + int enable_bit = GET_COUNTER_OFFSET(cnt) / 4; + bitmask[block] |= (1 << enable_bit); + pr_debug("gator: Mali-T6xx: hardware counter %s selected [%d]\n", hardware_counter_names[cnt], cnt); + num_hardware_counters_enabled++; + } + } + + /* Create a kbase context for HW counters */ + if (num_hardware_counters_enabled > 0) { + if (init_symbols() > 0) { + clean_symbols(); + /* No Mali driver code entrypoints found - not a fault. */ + return 0; + } + + kbdevice = kbase_find_device_symbol(-1); + + /* If we already got a context, fail */ + if (kbcontext) { + pr_debug("gator: Mali-T6xx: error context already present\n"); + goto out; + } + + /* kbcontext will only be valid after all the Mali symbols are loaded successfully */ + kbcontext = kbase_create_context_symbol(kbdevice); + if (!kbcontext) { + pr_debug("gator: Mali-T6xx: error creating kbase context\n"); + goto out; + } + + + /* See if we can get the number of shader cores */ + shadersPresent = kbdevice->shader_present_bitmap; + shader_present_low = (unsigned long)shadersPresent; + + /* + * The amount of memory needed to store the dump (bytes) + * DUMP_SIZE = number of core groups + * * number of blocks (always 8 for midgard) + * * number of counters per block (always 64 for midgard) + * * number of bytes per counter (always 4 in midgard) + * For a Mali-T6xx with a single core group = 1 * 8 * 64 * 4 = 2048 + * For a Mali-T6xx with a dual core group = 2 * 8 * 64 * 4 = 4096 + */ +#if MALI_DDK_GATOR_API_VERSION == 1 + kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096); +#elif MALI_DDK_GATOR_API_VERSION == 2 + kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096, &kernel_dump_buffer_handle); +#endif + if (!kernel_dump_buffer) { + pr_debug("gator: Mali-T6xx: error trying to allocate va\n"); + goto destroy_context; + } + + setup.dump_buffer = (uintptr_t)kernel_dump_buffer; + setup.jm_bm = bitmask[JM_BLOCK]; + setup.tiler_bm = bitmask[TILER_BLOCK]; + setup.shader_bm = bitmask[SHADER_BLOCK]; + setup.mmu_l2_bm = bitmask[MMU_BLOCK]; + /* These counters do not exist on Mali-T60x */ + setup.l3_cache_bm = 0; + + /* Use kbase API to enable hardware counters and provide dump buffer */ + err = kbase_instr_hwcnt_enable_symbol(kbcontext, &setup); + if (err != MALI_ERROR_NONE) { + pr_debug("gator: Mali-T6xx: can't setup hardware counters\n"); + goto free_buffer; + } + pr_debug("gator: Mali-T6xx: hardware counters enabled\n"); + kbase_instr_hwcnt_clear_symbol(kbcontext); + pr_debug("gator: Mali-T6xx: hardware counters cleared \n"); + + kbase_device_busy = false; + } + + return 0; + +free_buffer: +#if MALI_DDK_GATOR_API_VERSION == 1 + kbase_va_free_symbol(kbcontext, kernel_dump_buffer); +#elif MALI_DDK_GATOR_API_VERSION == 2 + kbase_va_free_symbol(kbcontext, &kernel_dump_buffer_handle); +#endif + +destroy_context: + kbase_destroy_context_symbol(kbcontext); + +out: + clean_symbols(); + return -1; +} + +static void stop(void) +{ + unsigned int cnt; + kbase_context *temp_kbcontext; + + pr_debug("gator: Mali-T6xx: stop\n"); + + /* Set all counters as disabled */ + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { + counters[cnt].enabled = 0; + } + + /* Destroy the context for HW counters */ + if (num_hardware_counters_enabled > 0 && kbcontext != NULL) { + /* + * Set the global variable to NULL before destroying it, because + * other function will check this before using it. + */ + temp_kbcontext = kbcontext; + kbcontext = NULL; + + kbase_instr_hwcnt_disable_symbol(temp_kbcontext); + +#if MALI_DDK_GATOR_API_VERSION == 1 + kbase_va_free_symbol(temp_kbcontext, kernel_dump_buffer); +#elif MALI_DDK_GATOR_API_VERSION == 2 + kbase_va_free_symbol(temp_kbcontext, &kernel_dump_buffer_handle); +#endif + + kbase_destroy_context_symbol(temp_kbcontext); + + pr_debug("gator: Mali-T6xx: hardware counters stopped\n"); + + clean_symbols(); + } +} + +static int read(int **buffer) +{ + int cnt; + int len = 0; + u32 value = 0; + mali_bool success; + + struct timespec current_time; + static u32 prev_time_s = 0; + static s32 next_read_time_ns = 0; + + if (!on_primary_core()) { + return 0; + } + + getnstimeofday(¤t_time); + + /* + * Discard reads unless a respectable time has passed. This reduces the load on the GPU without sacrificing + * accuracy on the Streamline display. + */ + if (!is_read_scheduled(¤t_time, &prev_time_s, &next_read_time_ns)) { + return 0; + } + + /* + * Report the HW counters + * Only process hardware counters if at least one of the hardware counters is enabled. + */ + if (num_hardware_counters_enabled > 0) { + const unsigned int vithar_blocks[] = { + 0x700, /* VITHAR_JOB_MANAGER, Block 0 */ + 0x400, /* VITHAR_TILER, Block 1 */ + 0x000, /* VITHAR_SHADER_CORE, Block 2 */ + 0x500 /* VITHAR_MEMORY_SYSTEM, Block 3 */ + }; + + if (!kbcontext) { + return -1; + } + + /* Mali symbols can be called safely since a kbcontext is valid */ + if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success) == MALI_TRUE) { + kbase_device_busy = false; + + if (success == MALI_TRUE) { + /* Cycle through hardware counters and accumulate totals */ + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + const int block = GET_HW_BLOCK(cnt); + const int counter_offset = GET_COUNTER_OFFSET(cnt); + + const char* block_base_address = (char*)kernel_dump_buffer + vithar_blocks[block]; + + /* If counter belongs to shader block need to take into account all cores */ + if (block == SHADER_BLOCK) { + int i = 0; + int shader_core_count = 0; + value = 0; + + for (i = 0; i < 4; i++) { + if ((shader_present_low >> i) & 1) { + value += *((u32*) (block_base_address + (0x100 * i)) + counter_offset); + shader_core_count++; + } + } + + for (i = 0; i < 4; i++) { + if((shader_present_low >> (i+4)) & 1) { + value += *((u32*)(block_base_address + (0x100 * i) + 0x800) + counter_offset); + shader_core_count++; + } + } + + /* Need to total by number of cores to produce an average */ + if (shader_core_count != 0) { + value /= shader_core_count; + } + } else { + value = *((u32*)block_base_address + counter_offset); + } + + counter_dump[len++] = counter->key; + counter_dump[len++] = value; + } + } + } + } + + if (!kbase_device_busy) { + kbase_device_busy = true; + kbase_instr_hwcnt_dump_irq_symbol(kbcontext); + } + } + + /* Update the buffer */ + if (buffer) { + *buffer = (int *)counter_dump; + } + + return len; +} + +static int create_files(struct super_block *sb, struct dentry *root) +{ + unsigned int event; + /* + * Create the filesystem for all events + */ + int counter_index = 0; + const char *mali_name = gator_mali_get_mali_name(); + + for (event = 0; event < NUMBER_OF_HARDWARE_COUNTERS; event++) { + if (gator_mali_create_file_system(mali_name, hardware_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) + return -1; + counter_index++; + } + + return 0; +} + +static struct gator_interface gator_events_mali_t6xx_interface = { + .create_files = create_files, + .start = start, + .stop = stop, + .read = read +}; + +int gator_events_mali_t6xx_hw_init(void) +{ + pr_debug("gator: Mali-T6xx: sw_counters init\n"); + +#if GATOR_TEST + test_all_is_read_scheduled(); +#endif + + gator_mali_initialise_counters(counters, NUMBER_OF_HARDWARE_COUNTERS); + + return gator_events_install(&gator_events_mali_t6xx_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_mali_t6xx_hw_test.c linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_t6xx_hw_test.c --- linux-3.10.30/drivers/gator/gator_events_mali_t6xx_hw_test.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_mali_t6xx_hw_test.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,55 @@ +/** + * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * + * 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. + * + */ + +/** + * Test functions for mali_t600_hw code. + */ + +static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns); + +static int test_is_read_scheduled(u32 s, u32 ns, u32 prev_s, s32 next_ns, int expected_result, s32 expected_next_ns) +{ + struct timespec current_time; + u32 prev_time_s = prev_s; + s32 next_read_time_ns = next_ns; + + current_time.tv_sec = s; + current_time.tv_nsec = ns; + + if (is_read_scheduled(¤t_time, &prev_time_s, &next_read_time_ns) != expected_result) { + printk("Failed do_read(%u, %u, %u, %d): expected %d\n", s, ns, prev_s, next_ns, expected_result); + return 0; + } + + if (next_read_time_ns != expected_next_ns) { + printk("Failed: next_read_ns expected=%d, actual=%d\n", expected_next_ns, next_read_time_ns); + return 0; + } + + return 1; +} + +static void test_all_is_read_scheduled(void) +{ + const int HIGHEST_NS = 999999999; + int n_tests_passed = 0; + + printk("gator: running tests on %s\n", __FILE__); + + n_tests_passed += test_is_read_scheduled(0, 0, 0, 0, 1, READ_INTERVAL_NSEC); /* Null time */ + n_tests_passed += test_is_read_scheduled(100, 1000, 0, 0, 1, READ_INTERVAL_NSEC + 1000); /* Initial values */ + + n_tests_passed += test_is_read_scheduled(100, HIGHEST_NS, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500); + n_tests_passed += test_is_read_scheduled(101, 0001, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500 - NSEC_PER_SEC); + n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500 - NSEC_PER_SEC, 1, 600 + READ_INTERVAL_NSEC); + + n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500, 1, 600 + READ_INTERVAL_NSEC); + + printk("gator: %d tests passed\n", n_tests_passed); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_meminfo.c linux-3.10.30-cubox-i/drivers/gator/gator_events_meminfo.c --- linux-3.10.30/drivers/gator/gator_events_meminfo.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_meminfo.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,387 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" + +#include +#include +#include +#include +#include +#include + +enum { + MEMINFO_MEMFREE, + MEMINFO_MEMUSED, + MEMINFO_BUFFERRAM, + MEMINFO_TOTAL, +}; + +enum { + PROC_SIZE, + PROC_SHARE, + PROC_TEXT, + PROC_DATA, + PROC_COUNT, +}; + +static const char * const meminfo_names[] = { + "Linux_meminfo_memfree", + "Linux_meminfo_memused", + "Linux_meminfo_bufferram", +}; + +static const char * const proc_names[] = { + "Linux_proc_statm_size", + "Linux_proc_statm_share", + "Linux_proc_statm_text", + "Linux_proc_statm_data", +}; + +static bool meminfo_global_enabled; +static ulong meminfo_enabled[MEMINFO_TOTAL]; +static ulong meminfo_keys[MEMINFO_TOTAL]; +static long long meminfo_buffer[2 * (MEMINFO_TOTAL + 2)]; +static int meminfo_length = 0; +static bool new_data_avail; + +static bool proc_global_enabled; +static ulong proc_enabled[PROC_COUNT]; +static ulong proc_keys[PROC_COUNT]; +static DEFINE_PER_CPU(long long, proc_buffer[2 * (PROC_COUNT + 3)]); + +static int gator_meminfo_func(void *data); +static bool gator_meminfo_run; +// Initialize semaphore unlocked to initialize memory values +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +static DECLARE_MUTEX(gator_meminfo_sem); +#else +static DEFINE_SEMAPHORE(gator_meminfo_sem); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) +GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) +#else +GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order)) +#endif +{ + up(&gator_meminfo_sem); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) +GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) +#else +GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold)) +#endif +{ + up(&gator_meminfo_sem); +} + +GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) +{ + up(&gator_meminfo_sem); +} + +static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < MEMINFO_TOTAL; i++) { + dir = gatorfs_mkdir(sb, root, meminfo_names[i]); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_keys[i]); + } + + for (i = 0; i < PROC_COUNT; ++i) { + dir = gatorfs_mkdir(sb, root, proc_names[i]); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &proc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &proc_keys[i]); + } + + return 0; +} + +static int gator_events_meminfo_start(void) +{ + int i; + + new_data_avail = false; + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + meminfo_global_enabled = 1; + break; + } + } + + proc_global_enabled = 0; + for (i = 0; i < PROC_COUNT; ++i) { + if (proc_enabled[i]) { + proc_global_enabled = 1; + break; + } + } + if (meminfo_enabled[MEMINFO_MEMUSED]) { + proc_global_enabled = 1; + } + + if (meminfo_global_enabled == 0) + return 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + if (GATOR_REGISTER_TRACE(mm_page_free_direct)) +#else + if (GATOR_REGISTER_TRACE(mm_page_free)) +#endif + goto mm_page_free_exit; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + if (GATOR_REGISTER_TRACE(mm_pagevec_free)) +#else + if (GATOR_REGISTER_TRACE(mm_page_free_batched)) +#endif + goto mm_page_free_batched_exit; + if (GATOR_REGISTER_TRACE(mm_page_alloc)) + goto mm_page_alloc_exit; + + // Start worker thread + gator_meminfo_run = true; + // Since the mutex starts unlocked, memory values will be initialized + if (IS_ERR(kthread_run(gator_meminfo_func, NULL, "gator_meminfo"))) + goto kthread_run_exit; + + return 0; + +kthread_run_exit: + GATOR_UNREGISTER_TRACE(mm_page_alloc); +mm_page_alloc_exit: +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + GATOR_UNREGISTER_TRACE(mm_pagevec_free); +#else + GATOR_UNREGISTER_TRACE(mm_page_free_batched); +#endif +mm_page_free_batched_exit: +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + GATOR_UNREGISTER_TRACE(mm_page_free_direct); +#else + GATOR_UNREGISTER_TRACE(mm_page_free); +#endif +mm_page_free_exit: + return -1; +} + +static void gator_events_meminfo_stop(void) +{ + if (meminfo_global_enabled) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + GATOR_UNREGISTER_TRACE(mm_page_free_direct); + GATOR_UNREGISTER_TRACE(mm_pagevec_free); +#else + GATOR_UNREGISTER_TRACE(mm_page_free); + GATOR_UNREGISTER_TRACE(mm_page_free_batched); +#endif + GATOR_UNREGISTER_TRACE(mm_page_alloc); + + // Stop worker thread + gator_meminfo_run = false; + up(&gator_meminfo_sem); + } +} + +// Must be run in process context as the kernel function si_meminfo() can sleep +static int gator_meminfo_func(void *data) +{ + struct sysinfo info; + int i, len; + unsigned long long value; + + for (;;) { + if (down_killable(&gator_meminfo_sem)) { + break; + } + + // Eat up any pending events + while (!down_trylock(&gator_meminfo_sem)); + + if (!gator_meminfo_run) { + break; + } + + meminfo_length = len = 0; + + si_meminfo(&info); + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + switch (i) { + case MEMINFO_MEMFREE: + value = info.freeram * PAGE_SIZE; + break; + case MEMINFO_MEMUSED: + // pid -1 means system wide + meminfo_buffer[len++] = 1; + meminfo_buffer[len++] = -1; + // Emit value + meminfo_buffer[len++] = meminfo_keys[MEMINFO_MEMUSED]; + meminfo_buffer[len++] = (info.totalram - info.freeram) * PAGE_SIZE; + // Clear pid + meminfo_buffer[len++] = 1; + meminfo_buffer[len++] = 0; + continue; + case MEMINFO_BUFFERRAM: + value = info.bufferram * PAGE_SIZE; + break; + default: + value = 0; + break; + } + meminfo_buffer[len++] = meminfo_keys[i]; + meminfo_buffer[len++] = value; + } + } + + meminfo_length = len; + new_data_avail = true; + } + + return 0; +} + +static int gator_events_meminfo_read(long long **buffer) +{ + if (!on_primary_core() || !meminfo_global_enabled) + return 0; + + if (!new_data_avail) + return 0; + + new_data_avail = false; + + if (buffer) + *buffer = meminfo_buffer; + + return meminfo_length; +} + +static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task) +{ + struct mm_struct *mm; + u64 share = 0; + int i; + long long value; + int len = 0; + int cpu = get_physical_cpu(); + long long *buf = per_cpu(proc_buffer, cpu); + + if (!proc_global_enabled) { + return 0; + } + + // Collect the memory stats of the process instead of the thread + if (task->group_leader != NULL) { + task = task->group_leader; + } + + // get_task_mm/mmput is not needed in this context because the task and it's mm are required as part of the sched_switch + mm = task->mm; + if (mm == NULL) { + return 0; + } + + // Derived from task_statm in fs/proc/task_mmu.c + if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) { + share = get_mm_counter(mm, +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32) + file_rss +#else + MM_FILEPAGES +#endif + ); + } + + // key of 1 indicates a pid + buf[len++] = 1; + buf[len++] = task->pid; + + for (i = 0; i < PROC_COUNT; ++i) { + if (proc_enabled[i]) { + switch (i) { + case PROC_SIZE: + value = mm->total_vm; + break; + case PROC_SHARE: + value = share; + break; + case PROC_TEXT: + value = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT; + break; + case PROC_DATA: + value = mm->total_vm - mm->shared_vm; + break; + } + + buf[len++] = proc_keys[i]; + buf[len++] = value * PAGE_SIZE; + } + } + + if (meminfo_enabled[MEMINFO_MEMUSED]) { + value = share + get_mm_counter(mm, +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32) + anon_rss +#else + MM_ANONPAGES +#endif + ); + // Send resident for this pid + buf[len++] = meminfo_keys[MEMINFO_MEMUSED]; + buf[len++] = value * PAGE_SIZE; + } + + // Clear pid + buf[len++] = 1; + buf[len++] = 0; + + if (buffer) + *buffer = buf; + + return len; +} + +static struct gator_interface gator_events_meminfo_interface = { + .create_files = gator_events_meminfo_create_files, + .start = gator_events_meminfo_start, + .stop = gator_events_meminfo_stop, + .read64 = gator_events_meminfo_read, + .read_proc = gator_events_meminfo_read_proc, +}; + +int gator_events_meminfo_init(void) +{ + int i; + + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + meminfo_enabled[i] = 0; + meminfo_keys[i] = gator_events_get_key(); + } + + proc_global_enabled = 0; + for (i = 0; i < PROC_COUNT; ++i) { + proc_enabled[i] = 0; + proc_keys[i] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_meminfo_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_mmapped.c linux-3.10.30-cubox-i/drivers/gator/gator_events_mmapped.c --- linux-3.10.30/drivers/gator/gator_events_mmapped.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_mmapped.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,209 @@ +/* + * Example events provider + * + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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. + * + * Similar entries to those below must be present in the events.xml file. + * To add them to the events.xml, create an events-mmap.xml with the + * following contents and rebuild gatord: + * + * + * + * + * + * + * + * + * When adding custom events, be sure do the following + * - add any needed .c files to the gator driver Makefile + * - call gator_events_install in the events init function + * - add the init function to GATOR_EVENTS_LIST in gator_main.c + * - add a new events-*.xml file to the gator daemon and rebuild + */ + +#include +#include +#include + +#include "gator.h" + +#define MMAPPED_COUNTERS_NUM 3 + +static int mmapped_global_enabled; + +static struct { + unsigned long enabled; + unsigned long event; + unsigned long key; +} mmapped_counters[MMAPPED_COUNTERS_NUM]; + +static int mmapped_buffer[MMAPPED_COUNTERS_NUM * 2]; + +static s64 prev_time; + +/* Adds mmapped_cntX directories and enabled, event, and key files to /dev/gator/events */ +static int gator_events_mmapped_create_files(struct super_block *sb, + struct dentry *root) +{ + int i; + + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + char buf[16]; + struct dentry *dir; + + snprintf(buf, sizeof(buf), "mmapped_cnt%d", i); + dir = gatorfs_mkdir(sb, root, buf); + if (WARN_ON(!dir)) + return -1; + gatorfs_create_ulong(sb, dir, "enabled", + &mmapped_counters[i].enabled); + gatorfs_create_ulong(sb, dir, "event", + &mmapped_counters[i].event); + gatorfs_create_ro_ulong(sb, dir, "key", + &mmapped_counters[i].key); + } + + return 0; +} + +static int gator_events_mmapped_start(void) +{ + int i; + struct timespec ts; + + getnstimeofday(&ts); + prev_time = timespec_to_ns(&ts); + + mmapped_global_enabled = 0; + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + if (mmapped_counters[i].enabled) { + mmapped_global_enabled = 1; + break; + } + } + + return 0; +} + +static void gator_events_mmapped_stop(void) +{ +} + +/* This function "simulates" counters, generating values of fancy + * functions like sine or triangle... */ +static int mmapped_simulate(int counter, int delta_in_us) +{ + int result = 0; + + switch (counter) { + case 0: /* sort-of-sine */ + { + static int t = 0; + int x; + + t += delta_in_us; + if (t > 2048000) + t = 0; + + if (t % 1024000 < 512000) + x = 512000 - (t % 512000); + else + x = t % 512000; + + result = 32 * x / 512000; + result = result * result; + + if (t < 1024000) + result = 1922 - result; + } + break; + case 1: /* triangle */ + { + static int v, d = 1; + + v = v + d * delta_in_us; + if (v < 0) { + v = 0; + d = 1; + } else if (v > 1000000) { + v = 1000000; + d = -1; + } + + result = v; + } + break; + case 2: /* PWM signal */ + { + static int dc, x, t = 0; + + t += delta_in_us; + if (t > 1000000) + t = 0; + if (x / 1000000 != (x + delta_in_us) / 1000000) + dc = (dc + 100000) % 1000000; + x += delta_in_us; + + result = t < dc ? 0 : 10; + } + break; + } + + return result; +} + +static int gator_events_mmapped_read(int **buffer) +{ + int i; + int len = 0; + int delta_in_us; + struct timespec ts; + s64 time; + + /* System wide counters - read from one core only */ + if (!on_primary_core() || !mmapped_global_enabled) + return 0; + + getnstimeofday(&ts); + time = timespec_to_ns(&ts); + delta_in_us = (int)(time - prev_time) / 1000; + prev_time = time; + + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + if (mmapped_counters[i].enabled) { + mmapped_buffer[len++] = mmapped_counters[i].key; + mmapped_buffer[len++] = + mmapped_simulate(mmapped_counters[i].event, + delta_in_us); + } + } + + if (buffer) + *buffer = mmapped_buffer; + + return len; +} + +static struct gator_interface gator_events_mmapped_interface = { + .create_files = gator_events_mmapped_create_files, + .start = gator_events_mmapped_start, + .stop = gator_events_mmapped_stop, + .read = gator_events_mmapped_read, +}; + +/* Must not be static! */ +int __init gator_events_mmapped_init(void) +{ + int i; + + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + mmapped_counters[i].enabled = 0; + mmapped_counters[i].key = gator_events_get_key(); + } + + return gator_events_install(&gator_events_mmapped_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_net.c linux-3.10.30-cubox-i/drivers/gator/gator_events_net.c --- linux-3.10.30/drivers/gator/gator_events_net.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_net.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,172 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" +#include +#include + +#define NETRX 0 +#define NETTX 1 +#define TOTALNET 2 + +static ulong netrx_enabled; +static ulong nettx_enabled; +static ulong netrx_key; +static ulong nettx_key; +static int rx_total, tx_total; +static ulong netPrev[TOTALNET]; +static int netGet[TOTALNET * 4]; + +static struct timer_list net_wake_up_timer; + +// Must be run in process context as the kernel function dev_get_stats() can sleep +static void get_network_stats(struct work_struct *wsptr) +{ + int rx = 0, tx = 0; + struct net_device *dev; + + for_each_netdev(&init_net, dev) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + const struct net_device_stats *stats = dev_get_stats(dev); +#else + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); +#endif + rx += stats->rx_bytes; + tx += stats->tx_bytes; + } + rx_total = rx; + tx_total = tx; +} + +DECLARE_WORK(wq_get_stats, get_network_stats); + +static void net_wake_up_handler(unsigned long unused_data) +{ + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + schedule_work(&wq_get_stats); +} + +static void calculate_delta(int *rx, int *tx) +{ + int rx_calc, tx_calc; + + rx_calc = (int)(rx_total - netPrev[NETRX]); + if (rx_calc < 0) + rx_calc = 0; + netPrev[NETRX] += rx_calc; + + tx_calc = (int)(tx_total - netPrev[NETTX]); + if (tx_calc < 0) + tx_calc = 0; + netPrev[NETTX] += tx_calc; + + *rx = rx_calc; + *tx = tx_calc; +} + +static int gator_events_net_create_files(struct super_block *sb, struct dentry *root) +{ + // Network counters are not currently supported in RT-Preempt full because mod_timer is used +#ifndef CONFIG_PREEMPT_RT_FULL + struct dentry *dir; + + dir = gatorfs_mkdir(sb, root, "Linux_net_rx"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &netrx_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &netrx_key); + + dir = gatorfs_mkdir(sb, root, "Linux_net_tx"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &nettx_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &nettx_key); +#endif + + return 0; +} + +static int gator_events_net_start(void) +{ + get_network_stats(0); + netPrev[NETRX] = rx_total; + netPrev[NETTX] = tx_total; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + setup_timer(&net_wake_up_timer, net_wake_up_handler, 0); +#else + setup_deferrable_timer_on_stack(&net_wake_up_timer, net_wake_up_handler, 0); +#endif + return 0; +} + +static void gator_events_net_stop(void) +{ + del_timer_sync(&net_wake_up_timer); + netrx_enabled = 0; + nettx_enabled = 0; +} + +static int gator_events_net_read(int **buffer) +{ + int len, rx_delta, tx_delta; + static int last_rx_delta = 0, last_tx_delta = 0; + + if (!on_primary_core()) + return 0; + + if (!netrx_enabled && !nettx_enabled) + return 0; + + mod_timer(&net_wake_up_timer, jiffies + 1); + + calculate_delta(&rx_delta, &tx_delta); + + len = 0; + if (netrx_enabled && last_rx_delta != rx_delta) { + last_rx_delta = rx_delta; + netGet[len++] = netrx_key; + netGet[len++] = 0; // indicates to Streamline that rx_delta bytes were transmitted now, not since the last message + netGet[len++] = netrx_key; + netGet[len++] = rx_delta; + } + + if (nettx_enabled && last_tx_delta != tx_delta) { + last_tx_delta = tx_delta; + netGet[len++] = nettx_key; + netGet[len++] = 0; // indicates to Streamline that tx_delta bytes were transmitted now, not since the last message + netGet[len++] = nettx_key; + netGet[len++] = tx_delta; + } + + if (buffer) + *buffer = netGet; + + return len; +} + +static struct gator_interface gator_events_net_interface = { + .create_files = gator_events_net_create_files, + .start = gator_events_net_start, + .stop = gator_events_net_stop, + .read = gator_events_net_read, +}; + +int gator_events_net_init(void) +{ + netrx_key = gator_events_get_key(); + nettx_key = gator_events_get_key(); + + netrx_enabled = 0; + nettx_enabled = 0; + + return gator_events_install(&gator_events_net_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_perf_pmu.c linux-3.10.30-cubox-i/drivers/gator/gator_events_perf_pmu.c --- linux-3.10.30/drivers/gator/gator_events_perf_pmu.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_perf_pmu.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,587 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" + +// gator_events_armvX.c is used for Linux 2.6.x +#if GATOR_PERF_PMU_SUPPORT + +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include + +extern bool event_based_sampling; + +// Maximum number of per-core counters - currently reserves enough space for two full hardware PMUs for big.LITTLE +#define CNTMAX 16 +#define CCI_400 4 +// Maximum number of uncore counters +// + 1 for the cci-400 cycles counter +#define UCCNT (CCI_400 + 1) + +// Default to 0 if unable to probe the revision which was the previous behavior +#define DEFAULT_CCI_REVISION 0 + +// A gator_attr is needed for every counter +struct gator_attr { + // Set once in gator_events_perf_pmu_*_init - the name of the event in the gatorfs + char name[40]; + // Exposed in gatorfs - set by gatord to enable this counter + unsigned long enabled; + // Set once in gator_events_perf_pmu_*_init - the perf type to use, see perf_type_id in the perf_event.h header file. + unsigned long type; + // Exposed in gatorfs - set by gatord to select the event to collect + unsigned long event; + // Exposed in gatorfs - set by gatord with the sample period to use and enable EBS for this counter + unsigned long count; + // Exposed as read only in gatorfs - set once in __attr_init as the key to use in the APC data + unsigned long key; +}; + +// Per-core counter attributes +static struct gator_attr attrs[CNTMAX]; +// Number of initialized per-core counters +static int attr_count; +// Uncore counter attributes +static struct gator_attr uc_attrs[UCCNT]; +// Number of initialized uncore counters +static int uc_attr_count; + +struct gator_event { + int curr; + int prev; + int prev_delta; + bool zero; + struct perf_event *pevent; + struct perf_event_attr *pevent_attr; +}; + +static DEFINE_PER_CPU(struct gator_event[CNTMAX], events); +static struct gator_event uc_events[UCCNT]; +static DEFINE_PER_CPU(int[(CNTMAX + UCCNT)*2], perf_cnt); + +static void gator_events_perf_pmu_stop(void); + +static int __create_files(struct super_block *sb, struct dentry *root, struct gator_attr *const attr) +{ + struct dentry *dir; + + if (attr->name[0] == '\0') { + return 0; + } + dir = gatorfs_mkdir(sb, root, attr->name); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &attr->enabled); + gatorfs_create_ulong(sb, dir, "count", &attr->count); + gatorfs_create_ro_ulong(sb, dir, "key", &attr->key); + gatorfs_create_ulong(sb, dir, "event", &attr->event); + + return 0; +} + +static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root) +{ + int cnt; + + for (cnt = 0; cnt < attr_count; cnt++) { + if (__create_files(sb, root, &attrs[cnt]) != 0) { + return -1; + } + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + if (__create_files(sb, root, &uc_attrs[cnt]) != 0) { + return -1; + } + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void ebs_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif +{ + gator_backtrace_handler(regs); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void dummy_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void dummy_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif +{ +// Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll +} + +static int gator_events_perf_pmu_read(int **buffer); + +static int gator_events_perf_pmu_online(int **buffer, bool migrate) +{ + return gator_events_perf_pmu_read(buffer); +} + +static void __online_dispatch(int cpu, bool migrate, struct gator_attr *const attr, struct gator_event *const event) +{ + perf_overflow_handler_t handler; + + event->zero = true; + + if (event->pevent != NULL || event->pevent_attr == 0 || migrate) { + return; + } + + if (attr->count > 0) { + handler = ebs_overflow_handler; + } else { + handler = dummy_handler; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler); +#else + event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler, 0); +#endif + if (IS_ERR(event->pevent)) { + pr_debug("gator: unable to online a counter on cpu %d\n", cpu); + event->pevent = NULL; + return; + } + + if (event->pevent->state != PERF_EVENT_STATE_ACTIVE) { + pr_debug("gator: inactive counter on cpu %d\n", cpu); + perf_event_release_kernel(event->pevent); + event->pevent = NULL; + return; + } +} + +static void gator_events_perf_pmu_online_dispatch(int cpu, bool migrate) +{ + int cnt; + + cpu = pcpu_to_lcpu(cpu); + + for (cnt = 0; cnt < attr_count; cnt++) { + __online_dispatch(cpu, migrate, &attrs[cnt], &per_cpu(events, cpu)[cnt]); + } + + if (cpu == 0) { + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __online_dispatch(cpu, migrate, &uc_attrs[cnt], &uc_events[cnt]); + } + } +} + +static void __offline_dispatch(int cpu, struct gator_event *const event) +{ + struct perf_event *pe = NULL; + + if (event->pevent) { + pe = event->pevent; + event->pevent = NULL; + } + + if (pe) { + perf_event_release_kernel(pe); + } +} + +static void gator_events_perf_pmu_offline_dispatch(int cpu, bool migrate) +{ + int cnt; + + if (migrate) { + return; + } + cpu = pcpu_to_lcpu(cpu); + + for (cnt = 0; cnt < attr_count; cnt++) { + __offline_dispatch(cpu, &per_cpu(events, cpu)[cnt]); + } + + if (cpu == 0) { + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __offline_dispatch(cpu, &uc_events[cnt]); + } + } +} + +static int __check_ebs(struct gator_attr *const attr) +{ + if (attr->count > 0) { + if (!event_based_sampling) { + event_based_sampling = true; + } else { + printk(KERN_WARNING "gator: Only one ebs counter is allowed\n"); + return -1; + } + } + + return 0; +} + +static int __start(struct gator_attr *const attr, struct gator_event *const event) +{ + u32 size = sizeof(struct perf_event_attr); + + event->pevent = NULL; + if (!attr->enabled) { // Skip disabled counters + return 0; + } + + event->prev = 0; + event->curr = 0; + event->prev_delta = 0; + event->pevent_attr = kmalloc(size, GFP_KERNEL); + if (!event->pevent_attr) { + gator_events_perf_pmu_stop(); + return -1; + } + + memset(event->pevent_attr, 0, size); + event->pevent_attr->type = attr->type; + event->pevent_attr->size = size; + event->pevent_attr->config = attr->event; + event->pevent_attr->sample_period = attr->count; + event->pevent_attr->pinned = 1; + + return 0; +} + +static int gator_events_perf_pmu_start(void) +{ + int cnt, cpu; + + event_based_sampling = false; + for (cnt = 0; cnt < attr_count; cnt++) { + if (__check_ebs(&attrs[cnt]) != 0) { + return -1; + } + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + if (__check_ebs(&uc_attrs[cnt]) != 0) { + return -1; + } + } + + for_each_present_cpu(cpu) { + for (cnt = 0; cnt < attr_count; cnt++) { + if (__start(&attrs[cnt], &per_cpu(events, cpu)[cnt]) != 0) { + return -1; + } + } + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + if (__start(&uc_attrs[cnt], &uc_events[cnt]) != 0) { + return -1; + } + } + + return 0; +} + +static void __event_stop(struct gator_event *const event) +{ + if (event->pevent_attr) { + kfree(event->pevent_attr); + event->pevent_attr = NULL; + } +} + +static void __attr_stop(struct gator_attr *const attr) +{ + attr->enabled = 0; + attr->event = 0; + attr->count = 0; +} + +static void gator_events_perf_pmu_stop(void) +{ + unsigned int cnt, cpu; + + for_each_present_cpu(cpu) { + for (cnt = 0; cnt < attr_count; cnt++) { + __event_stop(&per_cpu(events, cpu)[cnt]); + } + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __event_stop(&uc_events[cnt]); + } + + for (cnt = 0; cnt < attr_count; cnt++) { + __attr_stop(&attrs[cnt]); + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __attr_stop(&uc_attrs[cnt]); + } +} + +static void __read(int *const len, int cpu, struct gator_attr *const attr, struct gator_event *const event) +{ + int delta; + + struct perf_event *const ev = event->pevent; + if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + /* After creating the perf counter in __online_dispatch, there + * is a race condition between gator_events_perf_pmu_online and + * gator_events_perf_pmu_read. So have + * gator_events_perf_pmu_online call gator_events_perf_pmu_read + * and in __read check to see if it's the first call after + * __online_dispatch and if so, run the online code. + */ + if (event->zero) { + ev->pmu->read(ev); + event->prev = event->curr = local64_read(&ev->count); + event->prev_delta = 0; + per_cpu(perf_cnt, cpu)[(*len)++] = attr->key; + per_cpu(perf_cnt, cpu)[(*len)++] = 0; + event->zero = false; + } else { + ev->pmu->read(ev); + event->curr = local64_read(&ev->count); + delta = event->curr - event->prev; + if (delta != 0 || delta != event->prev_delta) { + event->prev_delta = delta; + event->prev = event->curr; + per_cpu(perf_cnt, cpu)[(*len)++] = attr->key; + if (delta < 0) { + delta *= -1; + } + per_cpu(perf_cnt, cpu)[(*len)++] = delta; + } + } + } +} + +static int gator_events_perf_pmu_read(int **buffer) +{ + int cnt, len = 0; + const int cpu = get_logical_cpu(); + + for (cnt = 0; cnt < attr_count; cnt++) { + __read(&len, cpu, &attrs[cnt], &per_cpu(events, cpu)[cnt]); + } + + if (cpu == 0) { + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __read(&len, cpu, &uc_attrs[cnt], &uc_events[cnt]); + } + } + + if (buffer) { + *buffer = per_cpu(perf_cnt, cpu); + } + + return len; +} + +static struct gator_interface gator_events_perf_pmu_interface = { + .create_files = gator_events_perf_pmu_create_files, + .start = gator_events_perf_pmu_start, + .stop = gator_events_perf_pmu_stop, + .online = gator_events_perf_pmu_online, + .online_dispatch = gator_events_perf_pmu_online_dispatch, + .offline_dispatch = gator_events_perf_pmu_offline_dispatch, + .read = gator_events_perf_pmu_read, +}; + +static void __attr_init(struct gator_attr *const attr) +{ + attr->name[0] = '\0'; + attr->enabled = 0; + attr->type = 0; + attr->event = 0; + attr->count = 0; + attr->key = gator_events_get_key(); +} + +#ifdef CONFIG_OF + +static const struct of_device_id arm_cci_matches[] = { + {.compatible = "arm,cci-400" }, + {}, +}; + +static int probe_cci_revision(void) +{ + struct device_node *np; + struct resource res; + void __iomem *cci_ctrl_base; + int rev; + int ret = DEFAULT_CCI_REVISION; + + np = of_find_matching_node(NULL, arm_cci_matches); + if (!np) { + return ret; + } + + if (of_address_to_resource(np, 0, &res)) { + goto node_put; + } + + cci_ctrl_base = ioremap(res.start, resource_size(&res)); + + rev = (readl_relaxed(cci_ctrl_base + 0xfe8) >> 4) & 0xf; + + if (rev <= 4) { + ret = 0; + } else if (rev <= 6) { + ret = 1; + } + + iounmap(cci_ctrl_base); + + node_put: + of_node_put(np); + + return ret; +} + +#else + +static int probe_cci_revision(void) +{ + return DEFAULT_CCI_REVISION; +} + +#endif + +static void gator_events_perf_pmu_cci_init(const int type) +{ + int cnt; + const char *cci_name; + + switch (probe_cci_revision()) { + case 0: + cci_name = "cci-400"; + break; + case 1: + cci_name = "cci-400-r1"; + break; + default: + pr_debug("gator: unrecognized cci-400 revision\n"); + return; + } + + snprintf(uc_attrs[uc_attr_count].name, sizeof(uc_attrs[uc_attr_count].name), "%s_ccnt", cci_name); + uc_attrs[uc_attr_count].type = type; + ++uc_attr_count; + + for (cnt = 0; cnt < CCI_400; ++cnt, ++uc_attr_count) { + struct gator_attr *const attr = &uc_attrs[uc_attr_count]; + snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", cci_name, cnt); + attr->type = type; + } +} + +static void gator_events_perf_pmu_cpu_init(const struct gator_cpu *const gator_cpu, const int type) +{ + int cnt; + + snprintf(attrs[attr_count].name, sizeof(attrs[attr_count].name), "%s_ccnt", gator_cpu->pmnc_name); + attrs[attr_count].type = type; + ++attr_count; + + for (cnt = 0; cnt < gator_cpu->pmnc_counters; ++cnt, ++attr_count) { + struct gator_attr *const attr = &attrs[attr_count]; + snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", gator_cpu->pmnc_name, cnt); + attr->type = type; + } +} + +int gator_events_perf_pmu_init(void) +{ + struct perf_event_attr pea; + struct perf_event *pe; + const struct gator_cpu *gator_cpu; + int type; + int cpu; + int cnt; + bool found_cpu = false; + + for (cnt = 0; cnt < CNTMAX; cnt++) { + __attr_init(&attrs[cnt]); + } + for (cnt = 0; cnt < UCCNT; cnt++) { + __attr_init(&uc_attrs[cnt]); + } + + memset(&pea, 0, sizeof(pea)); + pea.size = sizeof(pea); + pea.config = 0xFF; + attr_count = 0; + uc_attr_count = 0; + for (type = PERF_TYPE_MAX; type < 0x20; ++type) { + pea.type = type; + + // A particular PMU may work on some but not all cores, so try on each core + pe = NULL; + for_each_present_cpu(cpu) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler); +#else + pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler, 0); +#endif + if (!IS_ERR(pe)) { + break; + } + } + // Assume that valid PMUs are contiguous + if (IS_ERR(pe)) { + break; + } + + if (pe->pmu != NULL && type == pe->pmu->type) { + if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0) { + gator_events_perf_pmu_cci_init(type); + } else if ((gator_cpu = gator_find_cpu_by_pmu_name(pe->pmu->name)) != NULL) { + found_cpu = true; + gator_events_perf_pmu_cpu_init(gator_cpu, type); + } + // Initialize gator_attrs for dynamic PMUs here + } + + perf_event_release_kernel(pe); + } + + if (!found_cpu) { + const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(gator_cpuid()); + if (gator_cpu == NULL) { + return -1; + } + gator_events_perf_pmu_cpu_init(gator_cpu, PERF_TYPE_RAW); + } + + // Initialize gator_attrs for non-dynamic PMUs here + + if (attr_count > CNTMAX) { + printk(KERN_ERR "gator: Too many perf counters\n"); + return -1; + } + + if (uc_attr_count > UCCNT) { + printk(KERN_ERR "gator: Too many perf uncore counters\n"); + return -1; + } + + return gator_events_install(&gator_events_perf_pmu_interface); +} + +#endif diff -Nur linux-3.10.30/drivers/gator/gator_events_sched.c linux-3.10.30-cubox-i/drivers/gator/gator_events_sched.c --- linux-3.10.30/drivers/gator/gator_events_sched.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_sched.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,113 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" +#include + +#define SCHED_SWITCH 0 +#define SCHED_TOTAL (SCHED_SWITCH+1) + +static ulong sched_switch_enabled; +static ulong sched_switch_key; +static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt); +static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_sched_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(schedCnt, get_physical_cpu())[SCHED_SWITCH]++; + local_irq_restore(flags); +} + +static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* switch */ + dir = gatorfs_mkdir(sb, root, "Linux_sched_switch"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key); + + return 0; +} + +static int gator_events_sched_start(void) +{ + // register tracepoints + if (sched_switch_enabled) + if (GATOR_REGISTER_TRACE(sched_switch)) + goto sched_switch_exit; + pr_debug("gator: registered scheduler event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +sched_switch_exit: + pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_sched_stop(void) +{ + if (sched_switch_enabled) + GATOR_UNREGISTER_TRACE(sched_switch); + pr_debug("gator: unregistered scheduler event tracepoints\n"); + + sched_switch_enabled = 0; +} + +static int gator_events_sched_read(int **buffer) +{ + unsigned long flags; + int len, value; + int cpu = get_physical_cpu(); + + len = 0; + if (sched_switch_enabled) { + local_irq_save(flags); + value = per_cpu(schedCnt, cpu)[SCHED_SWITCH]; + per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0; + local_irq_restore(flags); + per_cpu(schedGet, cpu)[len++] = sched_switch_key; + per_cpu(schedGet, cpu)[len++] = value; + } + + if (buffer) + *buffer = per_cpu(schedGet, cpu); + + return len; +} + +static struct gator_interface gator_events_sched_interface = { + .create_files = gator_events_sched_create_files, + .start = gator_events_sched_start, + .stop = gator_events_sched_stop, + .read = gator_events_sched_read, +}; + +int gator_events_sched_init(void) +{ + sched_switch_enabled = 0; + + sched_switch_key = gator_events_get_key(); + + return gator_events_install(&gator_events_sched_interface); +} diff -Nur linux-3.10.30/drivers/gator/gator_events_scorpion.c linux-3.10.30-cubox-i/drivers/gator/gator_events_scorpion.c --- linux-3.10.30/drivers/gator/gator_events_scorpion.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_events_scorpion.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,669 @@ +/** + * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * + * 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 "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +static const char *pmnc_name; +static int pmnc_counters; + +// Per-CPU PMNC: config reg +#define PMNC_E (1 << 0) /* Enable all counters */ +#define PMNC_P (1 << 1) /* Reset all counters */ +#define PMNC_C (1 << 2) /* Cycle counter reset */ +#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */ +#define PMNC_X (1 << 4) /* Export to ETM */ +#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug */ +#define PMNC_MASK 0x3f /* Mask for writable bits */ + +// ccnt reg +#define CCNT_REG (1 << 31) + +#define CCNT 0 +#define CNT0 1 +#define CNTMAX (4+1) + +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +enum scorpion_perf_types { + SCORPION_ICACHE_EXPL_INV = 0x4c, + SCORPION_ICACHE_MISS = 0x4d, + SCORPION_ICACHE_ACCESS = 0x4e, + SCORPION_ICACHE_CACHEREQ_L2 = 0x4f, + SCORPION_ICACHE_NOCACHE_L2 = 0x50, + SCORPION_HIQUP_NOPED = 0x51, + SCORPION_DATA_ABORT = 0x52, + SCORPION_IRQ = 0x53, + SCORPION_FIQ = 0x54, + SCORPION_ALL_EXCPT = 0x55, + SCORPION_UNDEF = 0x56, + SCORPION_SVC = 0x57, + SCORPION_SMC = 0x58, + SCORPION_PREFETCH_ABORT = 0x59, + SCORPION_INDEX_CHECK = 0x5a, + SCORPION_NULL_CHECK = 0x5b, + SCORPION_EXPL_ICIALLU = 0x5c, + SCORPION_IMPL_ICIALLU = 0x5d, + SCORPION_NONICIALLU_BTAC_INV = 0x5e, + SCORPION_ICIMVAU_IMPL_ICIALLU = 0x5f, + SCORPION_SPIPE_ONLY_CYCLES = 0x60, + SCORPION_XPIPE_ONLY_CYCLES = 0x61, + SCORPION_DUAL_CYCLES = 0x62, + SCORPION_DISPATCH_ANY_CYCLES = 0x63, + SCORPION_FIFO_FULLBLK_CMT = 0x64, + SCORPION_FAIL_COND_INST = 0x65, + SCORPION_PASS_COND_INST = 0x66, + SCORPION_ALLOW_VU_CLK = 0x67, + SCORPION_VU_IDLE = 0x68, + SCORPION_ALLOW_L2_CLK = 0x69, + SCORPION_L2_IDLE = 0x6a, + SCORPION_DTLB_IMPL_INV_SCTLR_DACR = 0x6b, + SCORPION_DTLB_EXPL_INV = 0x6c, + SCORPION_DTLB_MISS = 0x6d, + SCORPION_DTLB_ACCESS = 0x6e, + SCORPION_ITLB_MISS = 0x6f, + SCORPION_ITLB_IMPL_INV = 0x70, + SCORPION_ITLB_EXPL_INV = 0x71, + SCORPION_UTLB_D_MISS = 0x72, + SCORPION_UTLB_D_ACCESS = 0x73, + SCORPION_UTLB_I_MISS = 0x74, + SCORPION_UTLB_I_ACCESS = 0x75, + SCORPION_UTLB_INV_ASID = 0x76, + SCORPION_UTLB_INV_MVA = 0x77, + SCORPION_UTLB_INV_ALL = 0x78, + SCORPION_S2_HOLD_RDQ_UNAVAIL = 0x79, + SCORPION_S2_HOLD = 0x7a, + SCORPION_S2_HOLD_DEV_OP = 0x7b, + SCORPION_S2_HOLD_ORDER = 0x7c, + SCORPION_S2_HOLD_BARRIER = 0x7d, + SCORPION_VIU_DUAL_CYCLE = 0x7e, + SCORPION_VIU_SINGLE_CYCLE = 0x7f, + SCORPION_VX_PIPE_WAR_STALL_CYCLES = 0x80, + SCORPION_VX_PIPE_WAW_STALL_CYCLES = 0x81, + SCORPION_VX_PIPE_RAW_STALL_CYCLES = 0x82, + SCORPION_VX_PIPE_LOAD_USE_STALL = 0x83, + SCORPION_VS_PIPE_WAR_STALL_CYCLES = 0x84, + SCORPION_VS_PIPE_WAW_STALL_CYCLES = 0x85, + SCORPION_VS_PIPE_RAW_STALL_CYCLES = 0x86, + SCORPION_EXCEPTIONS_INV_OPERATION = 0x87, + SCORPION_EXCEPTIONS_DIV_BY_ZERO = 0x88, + SCORPION_COND_INST_FAIL_VX_PIPE = 0x89, + SCORPION_COND_INST_FAIL_VS_PIPE = 0x8a, + SCORPION_EXCEPTIONS_OVERFLOW = 0x8b, + SCORPION_EXCEPTIONS_UNDERFLOW = 0x8c, + SCORPION_EXCEPTIONS_DENORM = 0x8d, +#ifdef CONFIG_ARCH_MSM_SCORPIONMP + SCORPIONMP_NUM_BARRIERS = 0x8e, + SCORPIONMP_BARRIER_CYCLES = 0x8f, +#else + SCORPION_BANK_AB_HIT = 0x8e, + SCORPION_BANK_AB_ACCESS = 0x8f, + SCORPION_BANK_CD_HIT = 0x90, + SCORPION_BANK_CD_ACCESS = 0x91, + SCORPION_BANK_AB_DSIDE_HIT = 0x92, + SCORPION_BANK_AB_DSIDE_ACCESS = 0x93, + SCORPION_BANK_CD_DSIDE_HIT = 0x94, + SCORPION_BANK_CD_DSIDE_ACCESS = 0x95, + SCORPION_BANK_AB_ISIDE_HIT = 0x96, + SCORPION_BANK_AB_ISIDE_ACCESS = 0x97, + SCORPION_BANK_CD_ISIDE_HIT = 0x98, + SCORPION_BANK_CD_ISIDE_ACCESS = 0x99, + SCORPION_ISIDE_RD_WAIT = 0x9a, + SCORPION_DSIDE_RD_WAIT = 0x9b, + SCORPION_BANK_BYPASS_WRITE = 0x9c, + SCORPION_BANK_AB_NON_CASTOUT = 0x9d, + SCORPION_BANK_AB_L2_CASTOUT = 0x9e, + SCORPION_BANK_CD_NON_CASTOUT = 0x9f, + SCORPION_BANK_CD_L2_CASTOUT = 0xa0, +#endif + MSM_MAX_EVT +}; + +struct scorp_evt { + u32 evt_type; + u32 val; + u8 grp; + u32 evt_type_act; +}; + +static const struct scorp_evt sc_evt[] = { + {SCORPION_ICACHE_EXPL_INV, 0x80000500, 0, 0x4d}, + {SCORPION_ICACHE_MISS, 0x80050000, 0, 0x4e}, + {SCORPION_ICACHE_ACCESS, 0x85000000, 0, 0x4f}, + {SCORPION_ICACHE_CACHEREQ_L2, 0x86000000, 0, 0x4f}, + {SCORPION_ICACHE_NOCACHE_L2, 0x87000000, 0, 0x4f}, + {SCORPION_HIQUP_NOPED, 0x80080000, 0, 0x4e}, + {SCORPION_DATA_ABORT, 0x8000000a, 0, 0x4c}, + {SCORPION_IRQ, 0x80000a00, 0, 0x4d}, + {SCORPION_FIQ, 0x800a0000, 0, 0x4e}, + {SCORPION_ALL_EXCPT, 0x8a000000, 0, 0x4f}, + {SCORPION_UNDEF, 0x8000000b, 0, 0x4c}, + {SCORPION_SVC, 0x80000b00, 0, 0x4d}, + {SCORPION_SMC, 0x800b0000, 0, 0x4e}, + {SCORPION_PREFETCH_ABORT, 0x8b000000, 0, 0x4f}, + {SCORPION_INDEX_CHECK, 0x8000000c, 0, 0x4c}, + {SCORPION_NULL_CHECK, 0x80000c00, 0, 0x4d}, + {SCORPION_EXPL_ICIALLU, 0x8000000d, 0, 0x4c}, + {SCORPION_IMPL_ICIALLU, 0x80000d00, 0, 0x4d}, + {SCORPION_NONICIALLU_BTAC_INV, 0x800d0000, 0, 0x4e}, + {SCORPION_ICIMVAU_IMPL_ICIALLU, 0x8d000000, 0, 0x4f}, + + {SCORPION_SPIPE_ONLY_CYCLES, 0x80000600, 1, 0x51}, + {SCORPION_XPIPE_ONLY_CYCLES, 0x80060000, 1, 0x52}, + {SCORPION_DUAL_CYCLES, 0x86000000, 1, 0x53}, + {SCORPION_DISPATCH_ANY_CYCLES, 0x89000000, 1, 0x53}, + {SCORPION_FIFO_FULLBLK_CMT, 0x8000000d, 1, 0x50}, + {SCORPION_FAIL_COND_INST, 0x800d0000, 1, 0x52}, + {SCORPION_PASS_COND_INST, 0x8d000000, 1, 0x53}, + {SCORPION_ALLOW_VU_CLK, 0x8000000e, 1, 0x50}, + {SCORPION_VU_IDLE, 0x80000e00, 1, 0x51}, + {SCORPION_ALLOW_L2_CLK, 0x800e0000, 1, 0x52}, + {SCORPION_L2_IDLE, 0x8e000000, 1, 0x53}, + + {SCORPION_DTLB_IMPL_INV_SCTLR_DACR, 0x80000001, 2, 0x54}, + {SCORPION_DTLB_EXPL_INV, 0x80000100, 2, 0x55}, + {SCORPION_DTLB_MISS, 0x80010000, 2, 0x56}, + {SCORPION_DTLB_ACCESS, 0x81000000, 2, 0x57}, + {SCORPION_ITLB_MISS, 0x80000200, 2, 0x55}, + {SCORPION_ITLB_IMPL_INV, 0x80020000, 2, 0x56}, + {SCORPION_ITLB_EXPL_INV, 0x82000000, 2, 0x57}, + {SCORPION_UTLB_D_MISS, 0x80000003, 2, 0x54}, + {SCORPION_UTLB_D_ACCESS, 0x80000300, 2, 0x55}, + {SCORPION_UTLB_I_MISS, 0x80030000, 2, 0x56}, + {SCORPION_UTLB_I_ACCESS, 0x83000000, 2, 0x57}, + {SCORPION_UTLB_INV_ASID, 0x80000400, 2, 0x55}, + {SCORPION_UTLB_INV_MVA, 0x80040000, 2, 0x56}, + {SCORPION_UTLB_INV_ALL, 0x84000000, 2, 0x57}, + {SCORPION_S2_HOLD_RDQ_UNAVAIL, 0x80000800, 2, 0x55}, + {SCORPION_S2_HOLD, 0x88000000, 2, 0x57}, + {SCORPION_S2_HOLD_DEV_OP, 0x80000900, 2, 0x55}, + {SCORPION_S2_HOLD_ORDER, 0x80090000, 2, 0x56}, + {SCORPION_S2_HOLD_BARRIER, 0x89000000, 2, 0x57}, + + {SCORPION_VIU_DUAL_CYCLE, 0x80000001, 4, 0x5c}, + {SCORPION_VIU_SINGLE_CYCLE, 0x80000100, 4, 0x5d}, + {SCORPION_VX_PIPE_WAR_STALL_CYCLES, 0x80000005, 4, 0x5c}, + {SCORPION_VX_PIPE_WAW_STALL_CYCLES, 0x80000500, 4, 0x5d}, + {SCORPION_VX_PIPE_RAW_STALL_CYCLES, 0x80050000, 4, 0x5e}, + {SCORPION_VX_PIPE_LOAD_USE_STALL, 0x80000007, 4, 0x5c}, + {SCORPION_VS_PIPE_WAR_STALL_CYCLES, 0x80000008, 4, 0x5c}, + {SCORPION_VS_PIPE_WAW_STALL_CYCLES, 0x80000800, 4, 0x5d}, + {SCORPION_VS_PIPE_RAW_STALL_CYCLES, 0x80080000, 4, 0x5e}, + {SCORPION_EXCEPTIONS_INV_OPERATION, 0x8000000b, 4, 0x5c}, + {SCORPION_EXCEPTIONS_DIV_BY_ZERO, 0x80000b00, 4, 0x5d}, + {SCORPION_COND_INST_FAIL_VX_PIPE, 0x800b0000, 4, 0x5e}, + {SCORPION_COND_INST_FAIL_VS_PIPE, 0x8b000000, 4, 0x5f}, + {SCORPION_EXCEPTIONS_OVERFLOW, 0x8000000c, 4, 0x5c}, + {SCORPION_EXCEPTIONS_UNDERFLOW, 0x80000c00, 4, 0x5d}, + {SCORPION_EXCEPTIONS_DENORM, 0x8c000000, 4, 0x5f}, + +#ifdef CONFIG_ARCH_MSM_SCORPIONMP + {SCORPIONMP_NUM_BARRIERS, 0x80000e00, 3, 0x59}, + {SCORPIONMP_BARRIER_CYCLES, 0x800e0000, 3, 0x5a}, +#else + {SCORPION_BANK_AB_HIT, 0x80000001, 3, 0x58}, + {SCORPION_BANK_AB_ACCESS, 0x80000100, 3, 0x59}, + {SCORPION_BANK_CD_HIT, 0x80010000, 3, 0x5a}, + {SCORPION_BANK_CD_ACCESS, 0x81000000, 3, 0x5b}, + {SCORPION_BANK_AB_DSIDE_HIT, 0x80000002, 3, 0x58}, + {SCORPION_BANK_AB_DSIDE_ACCESS, 0x80000200, 3, 0x59}, + {SCORPION_BANK_CD_DSIDE_HIT, 0x80020000, 3, 0x5a}, + {SCORPION_BANK_CD_DSIDE_ACCESS, 0x82000000, 3, 0x5b}, + {SCORPION_BANK_AB_ISIDE_HIT, 0x80000003, 3, 0x58}, + {SCORPION_BANK_AB_ISIDE_ACCESS, 0x80000300, 3, 0x59}, + {SCORPION_BANK_CD_ISIDE_HIT, 0x80030000, 3, 0x5a}, + {SCORPION_BANK_CD_ISIDE_ACCESS, 0x83000000, 3, 0x5b}, + {SCORPION_ISIDE_RD_WAIT, 0x80000009, 3, 0x58}, + {SCORPION_DSIDE_RD_WAIT, 0x80090000, 3, 0x5a}, + {SCORPION_BANK_BYPASS_WRITE, 0x8000000a, 3, 0x58}, + {SCORPION_BANK_AB_NON_CASTOUT, 0x8000000c, 3, 0x58}, + {SCORPION_BANK_AB_L2_CASTOUT, 0x80000c00, 3, 0x59}, + {SCORPION_BANK_CD_NON_CASTOUT, 0x800c0000, 3, 0x5a}, + {SCORPION_BANK_CD_L2_CASTOUT, 0x8c000000, 3, 0x5b}, +#endif +}; + +static inline void scorpion_pmnc_write(u32 val) +{ + val &= PMNC_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); +} + +static inline u32 scorpion_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); + return val; +} + +static inline u32 scorpion_ccnt_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); + return val; +} + +static inline u32 scorpion_cntn_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); + return val; +} + +static inline u32 scorpion_pmnc_enable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = CCNT_REG; + else + val = (1 << (cnt - CNT0)); + + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); + + return cnt; +} + +static inline u32 scorpion_pmnc_disable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = CCNT_REG; + else + val = (1 << (cnt - CNT0)); + + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); + + return cnt; +} + +static inline int scorpion_pmnc_select_counter(unsigned int cnt) +{ + u32 val; + + if ((cnt == CCNT) || (cnt >= CNTMAX)) { + pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + val = (cnt - CNT0); + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + + return cnt; +} + +static u32 scorpion_read_lpm0(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm0(u32 val) +{ + asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_lpm1(void) +{ + u32 val; + asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm1(u32 val) +{ + asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_lpm2(void) +{ + u32 val; + asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm2(u32 val) +{ + asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_l2lpm(void) +{ + u32 val; + asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_l2lpm(u32 val) +{ + asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val)); +} + +static u32 scorpion_read_vlpm(void) +{ + u32 val; + asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_vlpm(u32 val) +{ + asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val)); +} + +struct scorpion_access_funcs { + u32(*read)(void); + void (*write)(u32); +}; + +struct scorpion_access_funcs scor_func[] = { + {scorpion_read_lpm0, scorpion_write_lpm0}, + {scorpion_read_lpm1, scorpion_write_lpm1}, + {scorpion_read_lpm2, scorpion_write_lpm2}, + {scorpion_read_l2lpm, scorpion_write_l2lpm}, + {scorpion_read_vlpm, scorpion_write_vlpm}, +}; + +u32 venum_orig_val; +u32 fp_orig_val; + +static void scorpion_pre_vlpm(void) +{ + u32 venum_new_val; + u32 fp_new_val; + + /* CPACR Enable CP10 access */ + asm volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (venum_orig_val)); + venum_new_val = venum_orig_val | 0x00300000; + asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_new_val)); + /* Enable FPEXC */ + asm volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (fp_orig_val)); + fp_new_val = fp_orig_val | 0x40000000; + asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_new_val)); +} + +static void scorpion_post_vlpm(void) +{ + /* Restore FPEXC */ + asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_orig_val)); + /* Restore CPACR */ + asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_orig_val)); +} + +#define COLMN0MASK 0x000000ff +#define COLMN1MASK 0x0000ff00 +#define COLMN2MASK 0x00ff0000 +static u32 scorpion_get_columnmask(u32 setval) +{ + if (setval & COLMN0MASK) + return 0xffffff00; + else if (setval & COLMN1MASK) + return 0xffff00ff; + else if (setval & COLMN2MASK) + return 0xff00ffff; + else + return 0x80ffffff; +} + +static void scorpion_evt_setup(u32 gr, u32 setval) +{ + u32 val; + if (gr == 4) + scorpion_pre_vlpm(); + val = scorpion_get_columnmask(setval) & scor_func[gr].read(); + val = val | setval; + scor_func[gr].write(val); + if (gr == 4) + scorpion_post_vlpm(); +} + +static int get_scorpion_evtinfo(unsigned int evt_type, struct scorp_evt *evtinfo) +{ + u32 idx; + if ((evt_type < 0x4c) || (evt_type >= MSM_MAX_EVT)) + return 0; + idx = evt_type - 0x4c; + if (sc_evt[idx].evt_type == evt_type) { + evtinfo->val = sc_evt[idx].val; + evtinfo->grp = sc_evt[idx].grp; + evtinfo->evt_type_act = sc_evt[idx].evt_type_act; + return 1; + } + return 0; +} + +static inline void scorpion_pmnc_write_evtsel(unsigned int cnt, u32 val) +{ + if (scorpion_pmnc_select_counter(cnt) == cnt) { + if (val < 0x40) { + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); + } else { + u32 zero = 0; + struct scorp_evt evtinfo; + // extract evtinfo.grp and evtinfo.tevt_type_act from val + if (get_scorpion_evtinfo(val, &evtinfo) == 0) + return; + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (evtinfo.evt_type_act)); + asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (zero)); + scorpion_evt_setup(evtinfo.grp, val); + } + } +} + +static void scorpion_pmnc_reset_counter(unsigned int cnt) +{ + u32 val = 0; + + if (cnt == CCNT) { + scorpion_pmnc_disable_counter(cnt); + + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val)); + + if (pmnc_enabled[cnt] != 0) + scorpion_pmnc_enable_counter(cnt); + + } else if (cnt >= CNTMAX) { + pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt); + } else { + scorpion_pmnc_disable_counter(cnt); + + if (scorpion_pmnc_select_counter(cnt) == cnt) + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val)); + + if (pmnc_enabled[cnt] != 0) + scorpion_pmnc_enable_counter(cnt); + } +} + +static int gator_events_scorpion_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_counters; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i - 1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_scorpion_online(int **buffer, bool migrate) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + + if (scorpion_pmnc_read() & PMNC_E) { + scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); + } + + /* Initialize & Reset PMNC: C bit and P bit */ + scorpion_pmnc_write(PMNC_P | PMNC_C); + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + unsigned long event; + + if (!pmnc_enabled[cnt]) + continue; + + // disable counter + scorpion_pmnc_disable_counter(cnt); + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters), We don't need to set the event if it's a cycle count + if (cnt != CCNT) + scorpion_pmnc_write_evtsel(cnt, event); + + // reset counter + scorpion_pmnc_reset_counter(cnt); + + // Enable counter, do not enable interrupt for this counter + scorpion_pmnc_enable_counter(cnt); + } + + // enable + scorpion_pmnc_write(scorpion_pmnc_read() | PMNC_E); + + // read the counters and toss the invalid data, return zero instead + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + if (cnt == CCNT) { + scorpion_ccnt_read(); + } else if (scorpion_pmnc_select_counter(cnt) == cnt) { + scorpion_cntn_read(); + } + scorpion_pmnc_reset_counter(cnt); + + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_scorpion_offline(int **buffer, bool migrate) +{ + scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); + return 0; +} + +static void gator_events_scorpion_stop(void) +{ + unsigned int cnt; + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_scorpion_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(scorpion_pmnc_read() & PMNC_E)) { + return 0; + } + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = scorpion_ccnt_read(); + } else if (scorpion_pmnc_select_counter(cnt) == cnt) { + value = scorpion_cntn_read(); + } else { + value = 0; + } + scorpion_pmnc_reset_counter(cnt); + + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_scorpion_interface = { + .create_files = gator_events_scorpion_create_files, + .stop = gator_events_scorpion_stop, + .online = gator_events_scorpion_online, + .offline = gator_events_scorpion_offline, + .read = gator_events_scorpion_read, +}; + +int gator_events_scorpion_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case SCORPION: + pmnc_name = "Scorpion"; + pmnc_counters = 4; + break; + case SCORPIONMP: + pmnc_name = "ScorpionMP"; + pmnc_counters = 4; + break; + default: + return -1; + } + + pmnc_counters++; // CNT[n] + CCNT + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_scorpion_interface); +} + +#endif diff -Nur linux-3.10.30/drivers/gator/gator_fs.c linux-3.10.30-cubox-i/drivers/gator/gator_fs.c --- linux-3.10.30/drivers/gator/gator_fs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_fs.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,382 @@ +/** + * @file gatorfs.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * A simple filesystem for configuration and + * access of oprofile. + */ + +#include +#include +#include +#include +#include + +#define gatorfs_MAGIC 0x24051020 +#define TMPBUFSIZE 50 +DEFINE_SPINLOCK(gatorfs_lock); + +static struct inode *gatorfs_get_inode(struct super_block *sb, int mode) +{ + struct inode *inode = new_inode(sb); + + if (inode) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + inode->i_ino = get_next_ino(); +#endif + inode->i_mode = mode; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } + return inode; +} + +static const struct super_operations s_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, +}; + +ssize_t gatorfs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset) +{ + return simple_read_from_buffer(buf, count, offset, str, strlen(str)); +} + +ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset) +{ + char tmpbuf[TMPBUFSIZE]; + size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val); + if (maxlen > TMPBUFSIZE) + maxlen = TMPBUFSIZE; + return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen); +} + +ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *offset) +{ + char tmpbuf[TMPBUFSIZE]; + size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%llu\n", val); + if (maxlen > TMPBUFSIZE) + maxlen = TMPBUFSIZE; + return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen); +} + +int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count) +{ + char tmpbuf[TMPBUFSIZE]; + unsigned long flags; + + if (!count) + return 0; + + if (count > TMPBUFSIZE - 1) + return -EINVAL; + + memset(tmpbuf, 0x0, TMPBUFSIZE); + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + spin_lock_irqsave(&gatorfs_lock, flags); + *val = simple_strtoul(tmpbuf, NULL, 0); + spin_unlock_irqrestore(&gatorfs_lock, flags); + return 0; +} + +int gatorfs_u64_from_user(u64 *val, char const __user *buf, size_t count) +{ + char tmpbuf[TMPBUFSIZE]; + unsigned long flags; + + if (!count) + return 0; + + if (count > TMPBUFSIZE - 1) + return -EINVAL; + + memset(tmpbuf, 0x0, TMPBUFSIZE); + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + spin_lock_irqsave(&gatorfs_lock, flags); + *val = simple_strtoull(tmpbuf, NULL, 0); + spin_unlock_irqrestore(&gatorfs_lock, flags); + return 0; +} + +static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + unsigned long *val = file->private_data; + return gatorfs_ulong_to_user(*val, buf, count, offset); +} + +static ssize_t u64_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + u64 *val = file->private_data; + return gatorfs_u64_to_user(*val, buf, count, offset); +} + +static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long *value = file->private_data; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(value, buf, count); + + if (retval) + return retval; + return count; +} + +static ssize_t u64_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + u64 *value = file->private_data; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_u64_from_user(value, buf, count); + + if (retval) + return retval; + return count; +} + +static int default_open(struct inode *inode, struct file *filp) +{ + if (inode->i_private) + filp->private_data = inode->i_private; + return 0; +} + +static const struct file_operations ulong_fops = { + .read = ulong_read_file, + .write = ulong_write_file, + .open = default_open, +}; + +static const struct file_operations u64_fops = { + .read = u64_read_file, + .write = u64_write_file, + .open = default_open, +}; + +static const struct file_operations ulong_ro_fops = { + .read = ulong_read_file, + .open = default_open, +}; + +static const struct file_operations u64_ro_fops = { + .read = u64_read_file, + .open = default_open, +}; + +static struct dentry *__gatorfs_create_file(struct super_block *sb, + struct dentry *root, + char const *name, + const struct file_operations *fops, + int perm) +{ + struct dentry *dentry; + struct inode *inode; + + dentry = d_alloc_name(root, name); + if (!dentry) + return NULL; + inode = gatorfs_get_inode(sb, S_IFREG | perm); + if (!inode) { + dput(dentry); + return NULL; + } + inode->i_fop = fops; + d_add(dentry, inode); + return dentry; +} + +int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &ulong_fops, 0644); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_u64(struct super_block *sb, struct dentry *root, + char const *name, u64 *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &u64_fops, 0644); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &ulong_ro_fops, 0444); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_ro_u64(struct super_block *sb, struct dentry *root, + char const *name, u64 * val) +{ + struct dentry *d = + __gatorfs_create_file(sb, root, name, &u64_ro_fops, 0444); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + atomic_t *val = file->private_data; + return gatorfs_ulong_to_user(atomic_read(val), buf, count, offset); +} + +static const struct file_operations atomic_ro_fops = { + .read = atomic_read_file, + .open = default_open, +}; + +int gatorfs_create_ro_atomic(struct super_block *sb, struct dentry *root, + char const *name, atomic_t *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &atomic_ro_fops, 0444); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_file(struct super_block *sb, struct dentry *root, + char const *name, const struct file_operations *fops) +{ + if (!__gatorfs_create_file(sb, root, name, fops, 0644)) + return -EFAULT; + return 0; +} + +int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, + char const *name, + const struct file_operations *fops, int perm) +{ + if (!__gatorfs_create_file(sb, root, name, fops, perm)) + return -EFAULT; + return 0; +} + +struct dentry *gatorfs_mkdir(struct super_block *sb, + struct dentry *root, char const *name) +{ + struct dentry *dentry; + struct inode *inode; + + dentry = d_alloc_name(root, name); + if (!dentry) + return NULL; + inode = gatorfs_get_inode(sb, S_IFDIR | 0755); + if (!inode) { + dput(dentry); + return NULL; + } + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + d_add(dentry, inode); + return dentry; +} + +static int gatorfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode; + struct dentry *root_dentry; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = gatorfs_MAGIC; + sb->s_op = &s_ops; + sb->s_time_gran = 1; + + root_inode = gatorfs_get_inode(sb, S_IFDIR | 0755); + if (!root_inode) + return -ENOMEM; + root_inode->i_op = &simple_dir_inode_operations; + root_inode->i_fop = &simple_dir_operations; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + root_dentry = d_alloc_root(root_inode); +#else + root_dentry = d_make_root(root_inode); +#endif + + if (!root_dentry) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + iput(root_inode); +#endif + return -ENOMEM; + } + + sb->s_root = root_dentry; + + gator_op_create_files(sb, root_dentry); + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) +static int gatorfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt); +} +#else +static struct dentry *gatorfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return mount_nodev(fs_type, flags, data, gatorfs_fill_super); +} +#endif + +static struct file_system_type gatorfs_type = { + .owner = THIS_MODULE, + .name = "gatorfs", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + .get_sb = gatorfs_get_sb, +#else + .mount = gatorfs_mount, +#endif + + .kill_sb = kill_litter_super, +}; + +int __init gatorfs_register(void) +{ + return register_filesystem(&gatorfs_type); +} + +void gatorfs_unregister(void) +{ + unregister_filesystem(&gatorfs_type); +} diff -Nur linux-3.10.30/drivers/gator/gator_hrtimer_gator.c linux-3.10.30-cubox-i/drivers/gator/gator_hrtimer_gator.c --- linux-3.10.30/drivers/gator/gator_hrtimer_gator.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_hrtimer_gator.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,86 @@ +/** + * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * + * 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. + * + */ + +// gator_hrtimer_perf.c is used if perf is supported +// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers +#if 1 + +void (*callback)(void); +DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); +DEFINE_PER_CPU(ktime_t, hrtimer_expire); +DEFINE_PER_CPU(int, hrtimer_is_active); +static ktime_t profiling_interval; +static void gator_hrtimer_online(void); +static void gator_hrtimer_offline(void); + +static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer) +{ + int cpu = get_logical_cpu(); + hrtimer_forward(hrtimer, per_cpu(hrtimer_expire, cpu), profiling_interval); + per_cpu(hrtimer_expire, cpu) = ktime_add(per_cpu(hrtimer_expire, cpu), profiling_interval); + (*callback)(); + return HRTIMER_RESTART; +} + +static void gator_hrtimer_online(void) +{ + int cpu = get_logical_cpu(); + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + + if (per_cpu(hrtimer_is_active, cpu) || profiling_interval.tv64 == 0) + return; + + per_cpu(hrtimer_is_active, cpu) = 1; + hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + hrtimer->function = gator_hrtimer_notify; +#ifdef CONFIG_PREEMPT_RT_BASE + hrtimer->irqsafe = 1; +#endif + per_cpu(hrtimer_expire, cpu) = ktime_add(hrtimer->base->get_time(), profiling_interval); + hrtimer_start(hrtimer, per_cpu(hrtimer_expire, cpu), HRTIMER_MODE_ABS_PINNED); +} + +static void gator_hrtimer_offline(void) +{ + int cpu = get_logical_cpu(); + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + + if (!per_cpu(hrtimer_is_active, cpu)) + return; + + per_cpu(hrtimer_is_active, cpu) = 0; + hrtimer_cancel(hrtimer); +} + +static int gator_hrtimer_init(int interval, void (*func)(void)) +{ + int cpu; + + (callback) = (func); + + for_each_present_cpu(cpu) { + per_cpu(hrtimer_is_active, cpu) = 0; + } + + // calculate profiling interval + if (interval > 0) { + profiling_interval = ns_to_ktime(1000000000UL / interval); + } else { + profiling_interval.tv64 = 0; + } + + return 0; +} + +static void gator_hrtimer_shutdown(void) +{ + /* empty */ +} + +#endif diff -Nur linux-3.10.30/drivers/gator/gator_hrtimer_perf.c linux-3.10.30-cubox-i/drivers/gator/gator_hrtimer_perf.c --- linux-3.10.30/drivers/gator/gator_hrtimer_perf.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_hrtimer_perf.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,113 @@ +/** + * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * + * 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. + * + */ + +// gator_hrtimer_gator.c is used if perf is not supported +// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers +#if 0 + +// Note: perf Cortex support added in 2.6.35 and PERF_COUNT_SW_CPU_CLOCK/hrtimer broken on 2.6.35 and 2.6.36 +// not relevant as this code is not active until 3.0.0, but wanted to document the issue + +void (*callback)(void); +static int profiling_interval; +static DEFINE_PER_CPU(struct perf_event *, perf_hrtimer); +static DEFINE_PER_CPU(struct perf_event_attr *, perf_hrtimer_attr); + +static void gator_hrtimer_shutdown(void); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void hrtimer_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void hrtimer_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif +{ + (*callback)(); +} + +static int gator_online_single_hrtimer(int cpu) +{ + if (per_cpu(perf_hrtimer, cpu) != 0 || per_cpu(perf_hrtimer_attr, cpu) == 0) + return 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler); +#else + per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler, 0); +#endif + if (IS_ERR(per_cpu(perf_hrtimer, cpu))) { + per_cpu(perf_hrtimer, cpu) = NULL; + return -1; + } + + if (per_cpu(perf_hrtimer, cpu)->state != PERF_EVENT_STATE_ACTIVE) { + perf_event_release_kernel(per_cpu(perf_hrtimer, cpu)); + per_cpu(perf_hrtimer, cpu) = NULL; + return -1; + } + + return 0; +} + +static void gator_hrtimer_online(int cpu) +{ + if (gator_online_single_hrtimer(cpu) < 0) { + pr_debug("gator: unable to online the hrtimer on cpu%d\n", cpu); + } +} + +static void gator_hrtimer_offline(int cpu) +{ + if (per_cpu(perf_hrtimer, cpu)) { + perf_event_release_kernel(per_cpu(perf_hrtimer, cpu)); + per_cpu(perf_hrtimer, cpu) = NULL; + } +} + +static int gator_hrtimer_init(int interval, void (*func)(void)) +{ + u32 size = sizeof(struct perf_event_attr); + int cpu; + + callback = func; + + // calculate profiling interval + profiling_interval = 1000000000 / interval; + + for_each_present_cpu(cpu) { + per_cpu(perf_hrtimer, cpu) = 0; + per_cpu(perf_hrtimer_attr, cpu) = kmalloc(size, GFP_KERNEL); + if (per_cpu(perf_hrtimer_attr, cpu) == 0) { + gator_hrtimer_shutdown(); + return -1; + } + + memset(per_cpu(perf_hrtimer_attr, cpu), 0, size); + per_cpu(perf_hrtimer_attr, cpu)->type = PERF_TYPE_SOFTWARE; + per_cpu(perf_hrtimer_attr, cpu)->size = size; + per_cpu(perf_hrtimer_attr, cpu)->config = PERF_COUNT_SW_CPU_CLOCK; + per_cpu(perf_hrtimer_attr, cpu)->sample_period = profiling_interval; + per_cpu(perf_hrtimer_attr, cpu)->pinned = 1; + } + + return 0; +} + +static void gator_hrtimer_shutdown(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + if (per_cpu(perf_hrtimer_attr, cpu)) { + kfree(per_cpu(perf_hrtimer_attr, cpu)); + per_cpu(perf_hrtimer_attr, cpu) = NULL; + } + } +} + +#endif diff -Nur linux-3.10.30/drivers/gator/gator_iks.c linux-3.10.30-cubox-i/drivers/gator/gator_iks.c --- linux-3.10.30/drivers/gator/gator_iks.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_iks.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,197 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * 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. + * + */ + +#if GATOR_IKS_SUPPORT + +#include +#include +#include +#include + +static bool map_cpuids; +static int mpidr_cpuids[NR_CPUS]; +static const struct gator_cpu * mpidr_cpus[NR_CPUS]; +static int __lcpu_to_pcpu[NR_CPUS]; + +static const struct gator_cpu *gator_find_cpu_by_dt_name(const char *const name) +{ + int i; + + for (i = 0; gator_cpus[i].cpuid != 0; ++i) { + const struct gator_cpu *const gator_cpu = &gator_cpus[i]; + if (gator_cpu->dt_name != NULL && strcmp(gator_cpu->dt_name, name) == 0) { + return gator_cpu; + } + } + + return NULL; +} + +static void calc_first_cluster_size(void) +{ + int len; + const u32 *val; + const char *compatible; + struct device_node *cn = NULL; + int mpidr_cpuids_count = 0; + + // Zero is a valid cpuid, so initialize the array to 0xff's + memset(&mpidr_cpuids, 0xff, sizeof(mpidr_cpuids)); + memset(&mpidr_cpus, 0, sizeof(mpidr_cpus)); + + while ((cn = of_find_node_by_type(cn, "cpu"))) { + BUG_ON(mpidr_cpuids_count >= NR_CPUS); + + val = of_get_property(cn, "reg", &len); + if (!val || len != 4) { + pr_err("%s missing reg property\n", cn->full_name); + continue; + } + compatible = of_get_property(cn, "compatible", NULL); + if (compatible == NULL) { + pr_err("%s missing compatible property\n", cn->full_name); + continue; + } + + mpidr_cpuids[mpidr_cpuids_count] = be32_to_cpup(val); + mpidr_cpus[mpidr_cpuids_count] = gator_find_cpu_by_dt_name(compatible); + ++mpidr_cpuids_count; + } + + map_cpuids = (mpidr_cpuids_count == nr_cpu_ids); +} + +static int linearize_mpidr(int mpidr) +{ + int i; + for (i = 0; i < nr_cpu_ids; ++i) { + if (mpidr_cpuids[i] == mpidr) { + return i; + } + } + + BUG(); +} + +int lcpu_to_pcpu(const int lcpu) +{ + int pcpu; + + if (!map_cpuids) + return lcpu; + + BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0); + pcpu = __lcpu_to_pcpu[lcpu]; + BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0); + return pcpu; +} + +int pcpu_to_lcpu(const int pcpu) +{ + int lcpu; + + if (!map_cpuids) + return pcpu; + + BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0); + for (lcpu = 0; lcpu < nr_cpu_ids; ++lcpu) { + if (__lcpu_to_pcpu[lcpu] == pcpu) { + BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0); + return lcpu; + } + } + BUG(); +} + +static void gator_update_cpu_mapping(u32 cpu_hwid) +{ + int lcpu = smp_processor_id(); + int pcpu = linearize_mpidr(cpu_hwid & MPIDR_HWID_BITMASK); + BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0); + BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0); + __lcpu_to_pcpu[lcpu] = pcpu; +} + +GATOR_DEFINE_PROBE(cpu_migrate_begin, TP_PROTO(u64 timestamp, u32 cpu_hwid)) +{ + const int cpu = get_physical_cpu(); + + gator_timer_offline((void *)1); + gator_timer_offline_dispatch(cpu, true); +} + +GATOR_DEFINE_PROBE(cpu_migrate_finish, TP_PROTO(u64 timestamp, u32 cpu_hwid)) +{ + int cpu; + + gator_update_cpu_mapping(cpu_hwid); + + // get_physical_cpu must be called after gator_update_cpu_mapping + cpu = get_physical_cpu(); + gator_timer_online_dispatch(cpu, true); + gator_timer_online((void *)1); +} + +GATOR_DEFINE_PROBE(cpu_migrate_current, TP_PROTO(u64 timestamp, u32 cpu_hwid)) +{ + gator_update_cpu_mapping(cpu_hwid); +} + +static void gator_send_iks_core_names(void) +{ + int cpu; + // Send the cpu names + preempt_disable(); + for (cpu = 0; cpu < nr_cpu_ids; ++cpu) { + if (mpidr_cpus[cpu] != NULL) { + gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid, mpidr_cpus[cpu]); + } + } + preempt_enable(); +} + +static int gator_migrate_start(void) +{ + int retval = 0; + + if (!map_cpuids) + return retval; + + if (retval == 0) + retval = GATOR_REGISTER_TRACE(cpu_migrate_begin); + if (retval == 0) + retval = GATOR_REGISTER_TRACE(cpu_migrate_finish); + if (retval == 0) + retval = GATOR_REGISTER_TRACE(cpu_migrate_current); + if (retval == 0) { + // Initialize the logical to physical cpu mapping + memset(&__lcpu_to_pcpu, 0xff, sizeof(__lcpu_to_pcpu)); + bL_switcher_trace_trigger(); + } + return retval; +} + +static void gator_migrate_stop(void) +{ + if (!map_cpuids) + return; + + GATOR_UNREGISTER_TRACE(cpu_migrate_current); + GATOR_UNREGISTER_TRACE(cpu_migrate_finish); + GATOR_UNREGISTER_TRACE(cpu_migrate_begin); +} + +#else + +#define calc_first_cluster_size() +#define gator_send_iks_core_names() +#define gator_migrate_start() 0 +#define gator_migrate_stop() + +#endif diff -Nur linux-3.10.30/drivers/gator/gator_main.c linux-3.10.30-cubox-i/drivers/gator/gator_main.c --- linux-3.10.30/drivers/gator/gator_main.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_main.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,1532 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 version must match the gator daemon version +#define PROTOCOL_VERSION 17 +static unsigned long gator_protocol_version = PROTOCOL_VERSION; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gator.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +#error kernels prior to 2.6.32 are not supported +#endif + +#if defined(MODULE) && !defined(CONFIG_MODULES) +#error Cannot build a module against a kernel that does not support modules. To resolve, either rebuild the kernel to support modules or build gator as part of the kernel. +#endif + +#if !defined(CONFIG_GENERIC_TRACER) && !defined(CONFIG_TRACING) +#error gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined +#endif + +#ifndef CONFIG_PROFILING +#error gator requires the kernel to have CONFIG_PROFILING defined +#endif + +#ifndef CONFIG_HIGH_RES_TIMERS +#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined to support PC sampling +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS) +#error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems +#endif + +#if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT)) +#ifndef CONFIG_PERF_EVENTS +#warning gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters +#elif !defined CONFIG_HW_PERF_EVENTS +#warning gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters +#endif +#endif + +/****************************************************************************** + * DEFINES + ******************************************************************************/ +#define SUMMARY_BUFFER_SIZE (1*1024) +#define BACKTRACE_BUFFER_SIZE (128*1024) +#define NAME_BUFFER_SIZE (64*1024) +#define COUNTER_BUFFER_SIZE (64*1024) // counters have the core as part of the data and the core value in the frame header may be discarded +#define BLOCK_COUNTER_BUFFER_SIZE (128*1024) +#define ANNOTATE_BUFFER_SIZE (128*1024) // annotate counters have the core as part of the data and the core value in the frame header may be discarded +#define SCHED_TRACE_BUFFER_SIZE (128*1024) +#define GPU_TRACE_BUFFER_SIZE (64*1024) // gpu trace counters have the core as part of the data and the core value in the frame header may be discarded +#define IDLE_BUFFER_SIZE (32*1024) // idle counters have the core as part of the data and the core value in the frame header may be discarded + +#define NO_COOKIE 0U +#define UNRESOLVED_COOKIE ~0U + +#define FRAME_SUMMARY 1 +#define FRAME_BACKTRACE 2 +#define FRAME_NAME 3 +#define FRAME_COUNTER 4 +#define FRAME_BLOCK_COUNTER 5 +#define FRAME_ANNOTATE 6 +#define FRAME_SCHED_TRACE 7 +#define FRAME_GPU_TRACE 8 +#define FRAME_IDLE 9 + +#define MESSAGE_END_BACKTRACE 1 + +#define MESSAGE_COOKIE 1 +#define MESSAGE_THREAD_NAME 2 +#define HRTIMER_CORE_NAME 3 +#define MESSAGE_LINK 4 + +#define MESSAGE_GPU_START 1 +#define MESSAGE_GPU_STOP 2 + +#define MESSAGE_SCHED_SWITCH 1 +#define MESSAGE_SCHED_EXIT 2 +#define MESSAGE_SCHED_START 3 + +#define MESSAGE_IDLE_ENTER 1 +#define MESSAGE_IDLE_EXIT 2 + +#define MAXSIZE_PACK32 5 +#define MAXSIZE_PACK64 10 + +#define FRAME_HEADER_SIZE 3 + +#if defined(__arm__) +#define PC_REG regs->ARM_pc +#elif defined(__aarch64__) +#define PC_REG regs->pc +#else +#define PC_REG regs->ip +#endif + +enum { + SUMMARY_BUF, + BACKTRACE_BUF, + NAME_BUF, + COUNTER_BUF, + BLOCK_COUNTER_BUF, + ANNOTATE_BUF, + SCHED_TRACE_BUF, + GPU_TRACE_BUF, + IDLE_BUF, + NUM_GATOR_BUFS +}; + +/****************************************************************************** + * Globals + ******************************************************************************/ +static unsigned long gator_cpu_cores; +// Size of the largest buffer. Effectively constant, set in gator_op_create_files +static unsigned long userspace_buffer_size; +static unsigned long gator_backtrace_depth; +// How often to commit the buffers for live in nanoseconds +static u64 gator_live_rate; + +static unsigned long gator_started; +static u64 gator_monotonic_started; +static u64 gator_hibernate_time; +static unsigned long gator_buffer_opened; +static unsigned long gator_timer_count; +static unsigned long gator_response_type; +static DEFINE_MUTEX(start_mutex); +static DEFINE_MUTEX(gator_buffer_mutex); + +bool event_based_sampling; + +static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); +static DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait); +static struct timer_list gator_buffer_wake_up_timer; +static bool gator_buffer_wake_stop; +static struct task_struct *gator_buffer_wake_thread; +static LIST_HEAD(gator_events); + +static DEFINE_PER_CPU(u64, last_timestamp); + +static bool printed_monotonic_warning; + +static bool sent_core_name[NR_CPUS]; + +/****************************************************************************** + * Prototypes + ******************************************************************************/ +static void buffer_check(int cpu, int buftype, u64 time); +static void gator_commit_buffer(int cpu, int buftype, u64 time); +static int buffer_bytes_available(int cpu, int buftype); +static bool buffer_check_space(int cpu, int buftype, int bytes); +static int contiguous_space_available(int cpu, int bufytpe); +static void gator_buffer_write_packed_int(int cpu, int buftype, int x); +static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x); +static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len); +static void gator_buffer_write_string(int cpu, int buftype, const char *x); +static void gator_add_trace(int cpu, unsigned long address); +static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time); +static u64 gator_get_time(void); + +// Size of the buffer, must be a power of 2. Effectively constant, set in gator_op_setup. +static uint32_t gator_buffer_size[NUM_GATOR_BUFS]; +// gator_buffer_size - 1, bitwise and with pos to get offset into the array. Effectively constant, set in gator_op_setup. +static uint32_t gator_buffer_mask[NUM_GATOR_BUFS]; +// Read position in the buffer. Initialized to zero in gator_op_setup and incremented after bytes are read by userspace in userspace_buffer_read +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_read); +// Write position in the buffer. Initialized to zero in gator_op_setup and incremented after bytes are written to the buffer +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_write); +// Commit position in the buffer. Initialized to zero in gator_op_setup and incremented after a frame is ready to be read by userspace +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_commit); +// If set to false, decreases the number of bytes returned by buffer_bytes_available. Set in buffer_check_space if no space is remaining. Initialized to true in gator_op_setup +// This means that if we run out of space, continue to report that no space is available until bytes are read by userspace +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], buffer_space_available); +// The buffer. Allocated in gator_op_setup +static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer); +// The time after which the buffer should be committed for live display +static DEFINE_PER_CPU(u64, gator_buffer_commit_time); + +// List of all gator events - new events must be added to this list +#define GATOR_EVENTS_LIST \ + GATOR_EVENT(gator_events_armv6_init) \ + GATOR_EVENT(gator_events_armv7_init) \ + GATOR_EVENT(gator_events_block_init) \ + GATOR_EVENT(gator_events_ccn504_init) \ + GATOR_EVENT(gator_events_irq_init) \ + GATOR_EVENT(gator_events_l2c310_init) \ + GATOR_EVENT(gator_events_mali_init) \ + GATOR_EVENT(gator_events_mali_t6xx_hw_init) \ + GATOR_EVENT(gator_events_mali_t6xx_init) \ + GATOR_EVENT(gator_events_meminfo_init) \ + GATOR_EVENT(gator_events_mmapped_init) \ + GATOR_EVENT(gator_events_net_init) \ + GATOR_EVENT(gator_events_perf_pmu_init) \ + GATOR_EVENT(gator_events_sched_init) \ + GATOR_EVENT(gator_events_scorpion_init) \ + +#define GATOR_EVENT(EVENT_INIT) __weak int EVENT_INIT(void); +GATOR_EVENTS_LIST +#undef GATOR_EVENT + +static int (*gator_events_list[])(void) = { +#define GATOR_EVENT(EVENT_INIT) EVENT_INIT, +GATOR_EVENTS_LIST +#undef GATOR_EVENT +}; + +/****************************************************************************** + * Application Includes + ******************************************************************************/ +#include "gator_marshaling.c" +#include "gator_hrtimer_perf.c" +#include "gator_hrtimer_gator.c" +#include "gator_cookies.c" +#include "gator_annotate.c" +#include "gator_trace_sched.c" +#include "gator_trace_power.c" +#include "gator_trace_gpu.c" +#include "gator_backtrace.c" +#include "gator_fs.c" +#include "gator_pack.c" + +/****************************************************************************** + * Misc + ******************************************************************************/ + +const struct gator_cpu gator_cpus[] = { + { + .cpuid = ARM1136, + .core_name = "ARM1136", + .pmnc_name = "ARM_ARM11", + .dt_name = "arm,arm1136", + .pmnc_counters = 3, + }, + { + .cpuid = ARM1156, + .core_name = "ARM1156", + .pmnc_name = "ARM_ARM11", + .dt_name = "arm,arm1156", + .pmnc_counters = 3, + }, + { + .cpuid = ARM1176, + .core_name = "ARM1176", + .pmnc_name = "ARM_ARM11", + .dt_name = "arm,arm1176", + .pmnc_counters = 3, + }, + { + .cpuid = ARM11MPCORE, + .core_name = "ARM11MPCore", + .pmnc_name = "ARM_ARM11MPCore", + .dt_name = "arm,arm11mpcore", + .pmnc_counters = 3, + }, + { + .cpuid = CORTEX_A5, + .core_name = "Cortex-A5", + .pmu_name = "ARMv7_Cortex_A5", + .pmnc_name = "ARM_Cortex-A5", + .dt_name = "arm,cortex-a5", + .pmnc_counters = 2, + }, + { + .cpuid = CORTEX_A7, + .core_name = "Cortex-A7", + .pmu_name = "ARMv7_Cortex_A7", + .pmnc_name = "ARM_Cortex-A7", + .dt_name = "arm,cortex-a7", + .pmnc_counters = 4, + }, + { + .cpuid = CORTEX_A8, + .core_name = "Cortex-A8", + .pmu_name = "ARMv7_Cortex_A8", + .pmnc_name = "ARM_Cortex-A8", + .dt_name = "arm,cortex-a8", + .pmnc_counters = 4, + }, + { + .cpuid = CORTEX_A9, + .core_name = "Cortex-A9", + .pmu_name = "ARMv7_Cortex_A9", + .pmnc_name = "ARM_Cortex-A9", + .dt_name = "arm,cortex-a9", + .pmnc_counters = 6, + }, + { + .cpuid = CORTEX_A12, + .core_name = "Cortex-A12", + .pmu_name = "ARMv7_Cortex_A12", + .pmnc_name = "ARM_Cortex-A12", + .dt_name = "arm,cortex-a12", + .pmnc_counters = 6, + }, + { + .cpuid = CORTEX_A15, + .core_name = "Cortex-A15", + .pmu_name = "ARMv7_Cortex_A15", + .pmnc_name = "ARM_Cortex-A15", + .dt_name = "arm,cortex-a15", + .pmnc_counters = 6, + }, + { + .cpuid = SCORPION, + .core_name = "Scorpion", + .pmnc_name = "Scorpion", + .pmnc_counters = 4, + }, + { + .cpuid = SCORPIONMP, + .core_name = "ScorpionMP", + .pmnc_name = "ScorpionMP", + .pmnc_counters = 4, + }, + { + .cpuid = KRAITSIM, + .core_name = "KraitSIM", + .pmnc_name = "Krait", + .pmnc_counters = 4, + }, + { + .cpuid = KRAIT, + .core_name = "Krait", + .pmnc_name = "Krait", + .pmnc_counters = 4, + }, + { + .cpuid = KRAIT_S4_PRO, + .core_name = "Krait S4 Pro", + .pmnc_name = "Krait", + .pmnc_counters = 4, + }, + { + .cpuid = CORTEX_A53, + .core_name = "Cortex-A53", + .pmnc_name = "ARM_Cortex-A53", + .dt_name = "arm,cortex-a53", + .pmnc_counters = 6, + }, + { + .cpuid = CORTEX_A57, + .core_name = "Cortex-A57", + .pmnc_name = "ARM_Cortex-A57", + .dt_name = "arm,cortex-a57", + .pmnc_counters = 6, + }, + { + .cpuid = AARCH64, + .core_name = "AArch64", + .pmnc_name = "ARM_AArch64", + .pmnc_counters = 6, + }, + { + .cpuid = OTHER, + .core_name = "Other", + .pmnc_name = "Other", + .pmnc_counters = 6, + }, + {} +}; + +const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid) +{ + int i; + + for (i = 0; gator_cpus[i].cpuid != 0; ++i) { + const struct gator_cpu *const gator_cpu = &gator_cpus[i]; + if (gator_cpu->cpuid == cpuid) { + return gator_cpu; + } + } + + return NULL; +} + +const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name) +{ + int i; + + for (i = 0; gator_cpus[i].cpuid != 0; ++i) { + const struct gator_cpu *const gator_cpu = &gator_cpus[i]; + if (gator_cpu->pmu_name != NULL && strcmp(gator_cpu->pmu_name, name) == 0) { + return gator_cpu; + } + } + + return NULL; +} + +u32 gator_cpuid(void) +{ +#if defined(__arm__) || defined(__aarch64__) + u32 val; +#if !defined(__aarch64__) + asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val)); +#else + asm volatile("mrs %0, midr_el1" : "=r" (val)); +#endif + return (val >> 4) & 0xfff; +#else + return OTHER; +#endif +} + +static void gator_buffer_wake_up(unsigned long data) +{ + wake_up(&gator_buffer_wait); +} + +static int gator_buffer_wake_func(void *data) +{ + while (!gator_buffer_wake_stop) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (gator_buffer_wake_stop) { + break; + } + + gator_buffer_wake_up(0); + } + + return 0; +} + +/****************************************************************************** + * Commit interface + ******************************************************************************/ +static bool buffer_commit_ready(int *cpu, int *buftype) +{ + int cpu_x, x; + for_each_present_cpu(cpu_x) { + for (x = 0; x < NUM_GATOR_BUFS; x++) + if (per_cpu(gator_buffer_commit, cpu_x)[x] != per_cpu(gator_buffer_read, cpu_x)[x]) { + *cpu = cpu_x; + *buftype = x; + return true; + } + } + *cpu = -1; + *buftype = -1; + return false; +} + +/****************************************************************************** + * Buffer management + ******************************************************************************/ +static int buffer_bytes_available(int cpu, int buftype) +{ + int remaining, filled; + + filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype]; + if (filled < 0) { + filled += gator_buffer_size[buftype]; + } + + remaining = gator_buffer_size[buftype] - filled; + + if (per_cpu(buffer_space_available, cpu)[buftype]) { + // Give some extra room; also allows space to insert the overflow error packet + remaining -= 200; + } else { + // Hysteresis, prevents multiple overflow messages + remaining -= 2000; + } + + return remaining; +} + +static int contiguous_space_available(int cpu, int buftype) +{ + int remaining = buffer_bytes_available(cpu, buftype); + int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype]; + if (remaining < contiguous) + return remaining; + else + return contiguous; +} + +static bool buffer_check_space(int cpu, int buftype, int bytes) +{ + int remaining = buffer_bytes_available(cpu, buftype); + + if (remaining < bytes) { + per_cpu(buffer_space_available, cpu)[buftype] = false; + } else { + per_cpu(buffer_space_available, cpu)[buftype] = true; + } + + return per_cpu(buffer_space_available, cpu)[buftype]; +} + +static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len) +{ + int i; + u32 write = per_cpu(gator_buffer_write, cpu)[buftype]; + u32 mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + + for (i = 0; i < len; i++) { + buffer[write] = x[i]; + write = (write + 1) & mask; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = write; +} + +static void gator_buffer_write_string(int cpu, int buftype, const char *x) +{ + int len = strlen(x); + gator_buffer_write_packed_int(cpu, buftype, len); + gator_buffer_write_bytes(cpu, buftype, x, len); +} + +static void gator_commit_buffer(int cpu, int buftype, u64 time) +{ + int type_length, commit, length, byte; + + if (!per_cpu(gator_buffer, cpu)[buftype]) + return; + + // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload + type_length = gator_response_type ? 1 : 0; + commit = per_cpu(gator_buffer_commit, cpu)[buftype]; + length = per_cpu(gator_buffer_write, cpu)[buftype] - commit; + if (length < 0) { + length += gator_buffer_size[buftype]; + } + length = length - type_length - sizeof(s32); + + if (length <= FRAME_HEADER_SIZE) { + // Nothing to write, only the frame header is present + return; + } + + for (byte = 0; byte < sizeof(s32); byte++) { + per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF; + } + + per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype]; + + if (gator_live_rate > 0) { + while (time > per_cpu(gator_buffer_commit_time, cpu)) { + per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate; + } + } + + marshal_frame(cpu, buftype); + + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + if (per_cpu(in_scheduler_context, cpu)) { +#ifndef CONFIG_PREEMPT_RT_FULL + // mod_timer can not be used in interrupt context in RT-Preempt full + mod_timer(&gator_buffer_wake_up_timer, jiffies + 1); +#endif + } else { + wake_up_process(gator_buffer_wake_thread); + } +} + +static void buffer_check(int cpu, int buftype, u64 time) +{ + int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype]; + if (filled < 0) { + filled += gator_buffer_size[buftype]; + } + if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) { + gator_commit_buffer(cpu, buftype, time); + } +} + +static void gator_add_trace(int cpu, unsigned long address) +{ + off_t offset = 0; + unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset); + + if (cookie == NO_COOKIE || cookie == UNRESOLVED_COOKIE) { + offset = address; + } + + marshal_backtrace(offset & ~1, cookie); +} + +static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time) +{ + bool inKernel; + unsigned long exec_cookie; + + if (!regs) + return; + + inKernel = !user_mode(regs); + exec_cookie = get_exec_cookie(cpu, current); + + if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, inKernel, time)) + return; + + if (inKernel) { + kernel_backtrace(cpu, regs); + } else { + // Cookie+PC + gator_add_trace(cpu, PC_REG); + + // Backtrace + if (gator_backtrace_depth) + arm_backtrace_eabi(cpu, regs, gator_backtrace_depth); + } + + marshal_backtrace_footer(time); +} + +/****************************************************************************** + * hrtimer interrupt processing + ******************************************************************************/ +static void gator_timer_interrupt(void) +{ + struct pt_regs *const regs = get_irq_regs(); + gator_backtrace_handler(regs); +} + +void gator_backtrace_handler(struct pt_regs *const regs) +{ + u64 time = gator_get_time(); + int cpu = get_physical_cpu(); + + // Output backtrace + gator_add_sample(cpu, regs, time); + + // Collect counters + if (!per_cpu(collecting, cpu)) { + collect_counters(time, NULL); + } + + // No buffer flushing occurs during sched switch for RT-Preempt full. The block counter frame will be flushed by collect_counters, but the sched buffer needs to be explicitly flushed +#ifdef CONFIG_PREEMPT_RT_FULL + buffer_check(cpu, SCHED_TRACE_BUF, time); +#endif +} + +static int gator_running; + +// This function runs in interrupt context and on the appropriate core +static void gator_timer_offline(void *migrate) +{ + struct gator_interface *gi; + int i, len, cpu = get_physical_cpu(); + int *buffer; + u64 time; + + gator_trace_sched_offline(); + gator_trace_power_offline(); + + if (!migrate) { + gator_hrtimer_offline(); + } + + // Offline any events and output counters + time = gator_get_time(); + if (marshal_event_header(time)) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->offline) { + len = gi->offline(&buffer, migrate); + marshal_event(len, buffer); + } + } + // Only check after writing all counters so that time and corresponding counters appear in the same frame + buffer_check(cpu, BLOCK_COUNTER_BUF, time); + } + + // Flush all buffers on this core + for (i = 0; i < NUM_GATOR_BUFS; i++) + gator_commit_buffer(cpu, i, time); +} + +// This function runs in interrupt context and may be running on a core other than core 'cpu' +static void gator_timer_offline_dispatch(int cpu, bool migrate) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) { + if (gi->offline_dispatch) { + gi->offline_dispatch(cpu, migrate); + } + } +} + +static void gator_timer_stop(void) +{ + int cpu; + + if (gator_running) { + on_each_cpu(gator_timer_offline, NULL, 1); + for_each_online_cpu(cpu) { + gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false); + } + + gator_running = 0; + gator_hrtimer_shutdown(); + } +} + +#if defined(__arm__) || defined(__aarch64__) +static void gator_send_core_name(int cpu, const u32 cpuid, const struct gator_cpu *const gator_cpu) { + const char *core_name = NULL; + char core_name_buf[32]; + + if (!sent_core_name[cpu]) { + if (gator_cpu != NULL) { + core_name = gator_cpu->core_name; + } else { + snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.3x)", cpuid); + core_name = core_name_buf; + } + + marshal_core_name(cpu, cpuid, core_name); + sent_core_name[cpu] = true; + } +} +#endif + +// This function runs in interrupt context and on the appropriate core +static void gator_timer_online(void *migrate) +{ + struct gator_interface *gi; + int len, cpu = get_physical_cpu(); + int *buffer; + u64 time; + + gator_trace_power_online(); + + // online any events and output counters + time = gator_get_time(); + if (marshal_event_header(time)) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->online) { + len = gi->online(&buffer, migrate); + marshal_event(len, buffer); + } + } + // Only check after writing all counters so that time and corresponding counters appear in the same frame + buffer_check(cpu, BLOCK_COUNTER_BUF, time); + } + + if (!migrate) { + gator_hrtimer_online(); + } + +#if defined(__arm__) || defined(__aarch64__) + if (!sent_core_name[cpu]) { + const u32 cpuid = gator_cpuid(); + gator_send_core_name(cpu, cpuid, gator_find_cpu_by_cpuid(cpuid)); + } +#endif +} + +// This function runs in interrupt context and may be running on a core other than core 'cpu' +static void gator_timer_online_dispatch(int cpu, bool migrate) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) { + if (gi->online_dispatch) { + gi->online_dispatch(cpu, migrate); + } + } +} + +#include "gator_iks.c" + +int gator_timer_start(unsigned long sample_rate) +{ + int cpu; + + if (gator_running) { + pr_notice("gator: already running\n"); + return 0; + } + + gator_running = 1; + + // event based sampling trumps hr timer based sampling + if (event_based_sampling) { + sample_rate = 0; + } + + if (gator_hrtimer_init(sample_rate, gator_timer_interrupt) == -1) + return -1; + + gator_send_iks_core_names(); + for_each_online_cpu(cpu) { + gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false); + } + on_each_cpu(gator_timer_online, NULL, 1); + + return 0; +} + +static u64 gator_get_time(void) +{ + struct timespec ts; + u64 timestamp; + u64 prev_timestamp; + u64 delta; + int cpu = smp_processor_id(); + + // Match clock_gettime(CLOCK_MONOTONIC_RAW, &ts) from userspace + getrawmonotonic(&ts); + timestamp = timespec_to_ns(&ts); + + // getrawmonotonic is not monotonic on all systems. Detect and attempt to correct these cases. + // up to 0.5ms delta has been seen on some systems, which can skew Streamline data when viewing at high resolution. + // This doesn't work well with interrupts, but that it's OK - the real concern is to catch big jumps in time + prev_timestamp = per_cpu(last_timestamp, cpu); + if (prev_timestamp <= timestamp) { + per_cpu(last_timestamp, cpu) = timestamp; + } else { + delta = prev_timestamp - timestamp; + // Log the error once + if (!printed_monotonic_warning && delta > 500000) { + printk(KERN_ERR "%s: getrawmonotonic is not monotonic cpu: %i delta: %lli\nSkew in Streamline data may be present at the fine zoom levels\n", __FUNCTION__, cpu, delta); + printed_monotonic_warning = true; + } + timestamp = prev_timestamp; + } + + return timestamp - gator_monotonic_started; +} + +/****************************************************************************** + * cpu hotplug and pm notifiers + ******************************************************************************/ +static int __cpuinit gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) +{ + int cpu = lcpu_to_pcpu((long)hcpu); + + switch (action) { + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + smp_call_function_single(cpu, gator_timer_offline, NULL, 1); + gator_timer_offline_dispatch(cpu, false); + break; + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + gator_timer_online_dispatch(cpu, false); + smp_call_function_single(cpu, gator_timer_online, NULL, 1); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __refdata gator_hotcpu_notifier = { + .notifier_call = gator_hotcpu_notify, +}; + +// n.b. calling "on_each_cpu" only runs on those that are online +// Registered linux events are not disabled, so their counters will continue to collect +static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) +{ + int cpu; + struct timespec ts; + + switch (event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + unregister_hotcpu_notifier(&gator_hotcpu_notifier); + unregister_scheduler_tracepoints(); + on_each_cpu(gator_timer_offline, NULL, 1); + for_each_online_cpu(cpu) { + gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false); + } + + // Record the wallclock hibernate time + getnstimeofday(&ts); + gator_hibernate_time = timespec_to_ns(&ts) - gator_get_time(); + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + // Adjust gator_monotonic_started for the time spent sleeping, as gator_get_time does not account for it + if (gator_hibernate_time > 0) { + getnstimeofday(&ts); + gator_monotonic_started += gator_hibernate_time + gator_get_time() - timespec_to_ns(&ts); + gator_hibernate_time = 0; + } + + for_each_online_cpu(cpu) { + gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false); + } + on_each_cpu(gator_timer_online, NULL, 1); + register_scheduler_tracepoints(); + register_hotcpu_notifier(&gator_hotcpu_notifier); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block gator_pm_notifier = { + .notifier_call = gator_pm_notify, +}; + +static int gator_notifier_start(void) +{ + int retval; + retval = register_hotcpu_notifier(&gator_hotcpu_notifier); + if (retval == 0) + retval = register_pm_notifier(&gator_pm_notifier); + return retval; +} + +static void gator_notifier_stop(void) +{ + unregister_pm_notifier(&gator_pm_notifier); + unregister_hotcpu_notifier(&gator_hotcpu_notifier); +} + +/****************************************************************************** + * Main + ******************************************************************************/ +static void gator_summary(void) +{ + u64 timestamp, uptime; + struct timespec ts; + char uname_buf[512]; + void (*m2b)(struct timespec *ts); + unsigned long flags; + + snprintf(uname_buf, sizeof(uname_buf), "%s %s %s %s %s GNU/Linux", utsname()->sysname, utsname()->nodename, utsname()->release, utsname()->version, utsname()->machine); + + getnstimeofday(&ts); + timestamp = timespec_to_ns(&ts); + + do_posix_clock_monotonic_gettime(&ts); + // monotonic_to_bootbased is not defined for some versions of Android + m2b = symbol_get(monotonic_to_bootbased); + if (m2b) { + m2b(&ts); + } + uptime = timespec_to_ns(&ts); + + // Disable interrupts as gator_get_time calls smp_processor_id to verify time is monotonic + local_irq_save(flags); + // Set monotonic_started to zero as gator_get_time is uptime minus monotonic_started + gator_monotonic_started = 0; + gator_monotonic_started = gator_get_time(); + local_irq_restore(flags); + + marshal_summary(timestamp, uptime, gator_monotonic_started, uname_buf); +} + +int gator_events_install(struct gator_interface *interface) +{ + list_add_tail(&interface->list, &gator_events); + + return 0; +} + +int gator_events_get_key(void) +{ + // key 0 is reserved as a timestamp + // key 1 is reserved as the marker for thread specific counters + // Odd keys are assigned by the driver, even keys by the daemon + static int key = 3; + + const int ret = key; + key += 2; + return ret; +} + +static int gator_init(void) +{ + int i; + + calc_first_cluster_size(); + + // events sources + for (i = 0; i < ARRAY_SIZE(gator_events_list); i++) + if (gator_events_list[i]) + gator_events_list[i](); + + gator_trace_sched_init(); + gator_trace_power_init(); + + return 0; +} + +static void gator_exit(void) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) + if (gi->shutdown) + gi->shutdown(); +} + +static int gator_start(void) +{ + unsigned long cpu, i; + struct gator_interface *gi; + + gator_buffer_wake_stop = false; + if (IS_ERR(gator_buffer_wake_thread = kthread_run(gator_buffer_wake_func, NULL, "gator_bwake"))) { + goto bwake_failure; + } + + if (gator_migrate_start()) + goto migrate_failure; + + // Initialize the buffer with the frame type and core + for_each_present_cpu(cpu) { + for (i = 0; i < NUM_GATOR_BUFS; i++) { + marshal_frame(cpu, i); + } + per_cpu(last_timestamp, cpu) = 0; + } + printed_monotonic_warning = false; + + // Capture the start time + gator_summary(); + + // start all events + list_for_each_entry(gi, &gator_events, list) { + if (gi->start && gi->start() != 0) { + struct list_head *ptr = gi->list.prev; + + while (ptr != &gator_events) { + gi = list_entry(ptr, struct gator_interface, list); + + if (gi->stop) + gi->stop(); + + ptr = ptr->prev; + } + goto events_failure; + } + } + + // cookies shall be initialized before trace_sched_start() and gator_timer_start() + if (cookies_initialize()) + goto cookies_failure; + if (gator_annotate_start()) + goto annotate_failure; + if (gator_trace_sched_start()) + goto sched_failure; + if (gator_trace_power_start()) + goto power_failure; + if (gator_trace_gpu_start()) + goto gpu_failure; + if (gator_timer_start(gator_timer_count)) + goto timer_failure; + if (gator_notifier_start()) + goto notifier_failure; + + return 0; + +notifier_failure: + gator_timer_stop(); +timer_failure: + gator_trace_gpu_stop(); +gpu_failure: + gator_trace_power_stop(); +power_failure: + gator_trace_sched_stop(); +sched_failure: + gator_annotate_stop(); +annotate_failure: + cookies_release(); +cookies_failure: + // stop all events + list_for_each_entry(gi, &gator_events, list) + if (gi->stop) + gi->stop(); +events_failure: + gator_migrate_stop(); +migrate_failure: + gator_buffer_wake_stop = true; + wake_up_process(gator_buffer_wake_thread); +bwake_failure: + + return -1; +} + +static void gator_stop(void) +{ + struct gator_interface *gi; + + gator_annotate_stop(); + gator_trace_sched_stop(); + gator_trace_power_stop(); + gator_trace_gpu_stop(); + + // stop all interrupt callback reads before tearing down other interfaces + gator_notifier_stop(); // should be called before gator_timer_stop to avoid re-enabling the hrtimer after it has been offlined + gator_timer_stop(); + + // stop all events + list_for_each_entry(gi, &gator_events, list) + if (gi->stop) + gi->stop(); + + gator_migrate_stop(); + + gator_buffer_wake_stop = true; + wake_up_process(gator_buffer_wake_thread); +} + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +/* fopen("buffer") */ +static int gator_op_setup(void) +{ + int err = 0; + int cpu, i; + + mutex_lock(&start_mutex); + + gator_buffer_size[SUMMARY_BUF] = SUMMARY_BUFFER_SIZE; + gator_buffer_mask[SUMMARY_BUF] = SUMMARY_BUFFER_SIZE - 1; + + gator_buffer_size[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE; + gator_buffer_mask[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE - 1; + + gator_buffer_size[NAME_BUF] = NAME_BUFFER_SIZE; + gator_buffer_mask[NAME_BUF] = NAME_BUFFER_SIZE - 1; + + gator_buffer_size[COUNTER_BUF] = COUNTER_BUFFER_SIZE; + gator_buffer_mask[COUNTER_BUF] = COUNTER_BUFFER_SIZE - 1; + + gator_buffer_size[BLOCK_COUNTER_BUF] = BLOCK_COUNTER_BUFFER_SIZE; + gator_buffer_mask[BLOCK_COUNTER_BUF] = BLOCK_COUNTER_BUFFER_SIZE - 1; + + gator_buffer_size[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE; + gator_buffer_mask[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE - 1; + + gator_buffer_size[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE; + gator_buffer_mask[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE - 1; + + gator_buffer_size[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE; + gator_buffer_mask[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE - 1; + + gator_buffer_size[IDLE_BUF] = IDLE_BUFFER_SIZE; + gator_buffer_mask[IDLE_BUF] = IDLE_BUFFER_SIZE - 1; + + // Initialize percpu per buffer variables + for (i = 0; i < NUM_GATOR_BUFS; i++) { + // Verify buffers are a power of 2 + if (gator_buffer_size[i] & (gator_buffer_size[i] - 1)) { + err = -ENOEXEC; + goto setup_error; + } + + for_each_present_cpu(cpu) { + per_cpu(gator_buffer_read, cpu)[i] = 0; + per_cpu(gator_buffer_write, cpu)[i] = 0; + per_cpu(gator_buffer_commit, cpu)[i] = 0; + per_cpu(buffer_space_available, cpu)[i] = true; + per_cpu(gator_buffer_commit_time, cpu) = gator_live_rate; + + // Annotation is a special case that only uses a single buffer + if (cpu > 0 && i == ANNOTATE_BUF) { + per_cpu(gator_buffer, cpu)[i] = NULL; + continue; + } + + per_cpu(gator_buffer, cpu)[i] = vmalloc(gator_buffer_size[i]); + if (!per_cpu(gator_buffer, cpu)[i]) { + err = -ENOMEM; + goto setup_error; + } + } + } + +setup_error: + mutex_unlock(&start_mutex); + return err; +} + +/* Actually start profiling (echo 1>/dev/gator/enable) */ +static int gator_op_start(void) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (gator_started || gator_start()) + err = -EINVAL; + else + gator_started = 1; + + mutex_unlock(&start_mutex); + + return err; +} + +/* echo 0>/dev/gator/enable */ +static void gator_op_stop(void) +{ + mutex_lock(&start_mutex); + + if (gator_started) { + gator_stop(); + + mutex_lock(&gator_buffer_mutex); + + gator_started = 0; + gator_monotonic_started = 0; + cookies_release(); + wake_up(&gator_buffer_wait); + + mutex_unlock(&gator_buffer_mutex); + } + + mutex_unlock(&start_mutex); +} + +static void gator_shutdown(void) +{ + int cpu, i; + + mutex_lock(&start_mutex); + + for_each_present_cpu(cpu) { + mutex_lock(&gator_buffer_mutex); + for (i = 0; i < NUM_GATOR_BUFS; i++) { + vfree(per_cpu(gator_buffer, cpu)[i]); + per_cpu(gator_buffer, cpu)[i] = NULL; + per_cpu(gator_buffer_read, cpu)[i] = 0; + per_cpu(gator_buffer_write, cpu)[i] = 0; + per_cpu(gator_buffer_commit, cpu)[i] = 0; + per_cpu(buffer_space_available, cpu)[i] = true; + per_cpu(gator_buffer_commit_time, cpu) = 0; + } + mutex_unlock(&gator_buffer_mutex); + } + + memset(&sent_core_name, 0, sizeof(sent_core_name)); + + mutex_unlock(&start_mutex); +} + +static int gator_set_backtrace(unsigned long val) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (gator_started) + err = -EBUSY; + else + gator_backtrace_depth = val; + + mutex_unlock(&start_mutex); + + return err; +} + +static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_ulong_to_user(gator_started, buf, count, offset); +} + +static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + if (val) + retval = gator_op_start(); + else + gator_op_stop(); + + if (retval) + return retval; + return count; +} + +static const struct file_operations enable_fops = { + .read = enable_read, + .write = enable_write, +}; + +static int userspace_buffer_open(struct inode *inode, struct file *file) +{ + int err = -EPERM; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (test_and_set_bit_lock(0, &gator_buffer_opened)) + return -EBUSY; + + if ((err = gator_op_setup())) + goto fail; + + /* NB: the actual start happens from userspace + * echo 1 >/dev/gator/enable + */ + + return 0; + +fail: + __clear_bit_unlock(0, &gator_buffer_opened); + return err; +} + +static int userspace_buffer_release(struct inode *inode, struct file *file) +{ + gator_op_stop(); + gator_shutdown(); + __clear_bit_unlock(0, &gator_buffer_opened); + return 0; +} + +static ssize_t userspace_buffer_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + int commit, length1, length2, read; + char *buffer1; + char *buffer2; + int cpu, buftype; + int written = 0; + + // ensure there is enough space for a whole frame + if (count < userspace_buffer_size || *offset) { + return -EINVAL; + } + + // sleep until the condition is true or a signal is received + // the condition is checked each time gator_buffer_wait is woken up + wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || !gator_started); + + if (signal_pending(current)) { + return -EINTR; + } + + if (buftype == -1 || cpu == -1) { + return 0; + } + + mutex_lock(&gator_buffer_mutex); + + do { + read = per_cpu(gator_buffer_read, cpu)[buftype]; + commit = per_cpu(gator_buffer_commit, cpu)[buftype]; + + // May happen if the buffer is freed during pending reads. + if (!per_cpu(gator_buffer, cpu)[buftype]) { + break; + } + + // determine the size of two halves + length1 = commit - read; + length2 = 0; + buffer1 = &(per_cpu(gator_buffer, cpu)[buftype][read]); + buffer2 = &(per_cpu(gator_buffer, cpu)[buftype][0]); + if (length1 < 0) { + length1 = gator_buffer_size[buftype] - read; + length2 = commit; + } + + if (length1 + length2 > count - written) { + break; + } + + // start, middle or end + if (length1 > 0 && copy_to_user(&buf[written], buffer1, length1)) { + break; + } + + // possible wrap around + if (length2 > 0 && copy_to_user(&buf[written + length1], buffer2, length2)) { + break; + } + + per_cpu(gator_buffer_read, cpu)[buftype] = commit; + written += length1 + length2; + + // Wake up annotate_write if more space is available + if (buftype == ANNOTATE_BUF) { + wake_up(&gator_annotate_wait); + } + } while (buffer_commit_ready(&cpu, &buftype)); + + mutex_unlock(&gator_buffer_mutex); + + // kick just in case we've lost an SMP event + wake_up(&gator_buffer_wait); + + return written > 0 ? written : -EFAULT; +} + +const struct file_operations gator_event_buffer_fops = { + .open = userspace_buffer_open, + .release = userspace_buffer_release, + .read = userspace_buffer_read, +}; + +static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count, offset); +} + +static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + retval = gator_set_backtrace(val); + + if (retval) + return retval; + return count; +} + +static const struct file_operations depth_fops = { + .read = depth_read, + .write = depth_write +}; + +void gator_op_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + struct gator_interface *gi; + int cpu; + + /* reinitialize default values */ + gator_cpu_cores = 0; + for_each_present_cpu(cpu) { + gator_cpu_cores++; + } + userspace_buffer_size = BACKTRACE_BUFFER_SIZE; + gator_response_type = 1; + gator_live_rate = 0; + + gatorfs_create_file(sb, root, "enable", &enable_fops); + gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops); + gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops); + gatorfs_create_ro_ulong(sb, root, "cpu_cores", &gator_cpu_cores); + gatorfs_create_ro_ulong(sb, root, "buffer_size", &userspace_buffer_size); + gatorfs_create_ulong(sb, root, "tick", &gator_timer_count); + gatorfs_create_ulong(sb, root, "response_type", &gator_response_type); + gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version); + gatorfs_create_ro_u64(sb, root, "started", &gator_monotonic_started); + gatorfs_create_u64(sb, root, "live_rate", &gator_live_rate); + + // Annotate interface + gator_annotate_create_files(sb, root); + + // Linux Events + dir = gatorfs_mkdir(sb, root, "events"); + list_for_each_entry(gi, &gator_events, list) + if (gi->create_files) + gi->create_files(sb, dir); + + // Sched Events + sched_trace_create_files(sb, dir); + + // Power interface + gator_trace_power_create_files(sb, dir); +} + +/****************************************************************************** + * Module + ******************************************************************************/ +static int __init gator_module_init(void) +{ + if (gatorfs_register()) { + return -1; + } + + if (gator_init()) { + gatorfs_unregister(); + return -1; + } + + setup_timer(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0); + + return 0; +} + +static void __exit gator_module_exit(void) +{ + del_timer_sync(&gator_buffer_wake_up_timer); + tracepoint_synchronize_unregister(); + gator_exit(); + gatorfs_unregister(); +} + +module_init(gator_module_init); +module_exit(gator_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd"); +MODULE_DESCRIPTION("Gator system profiler"); +#define STRIFY2(ARG) #ARG +#define STRIFY(ARG) STRIFY2(ARG) +MODULE_VERSION(STRIFY(PROTOCOL_VERSION)); diff -Nur linux-3.10.30/drivers/gator/gator_marshaling.c linux-3.10.30-cubox-i/drivers/gator/gator_marshaling.c --- linux-3.10.30/drivers/gator/gator_marshaling.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_marshaling.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,432 @@ +/** + * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * + * 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. + * + */ + +#define NEWLINE_CANARY \ + /* Unix */ \ + "1\n" \ + /* Windows */ \ + "2\r\n" \ + /* Mac OS */ \ + "3\r" \ + /* RISC OS */ \ + "4\n\r" \ + /* Add another character so the length isn't 0x0a bytes */ \ + "5" + +#ifdef MALI_SUPPORT +#include "gator_events_mali_common.h" +#endif + +static void marshal_summary(long long timestamp, long long uptime, long long monotonic_delta, const char * uname) +{ + unsigned long flags; + int cpu = 0; + + local_irq_save(flags); + gator_buffer_write_string(cpu, SUMMARY_BUF, NEWLINE_CANARY); + gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, timestamp); + gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, uptime); + gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, monotonic_delta); + gator_buffer_write_string(cpu, SUMMARY_BUF, "uname"); + gator_buffer_write_string(cpu, SUMMARY_BUF, uname); +#if GATOR_IKS_SUPPORT + gator_buffer_write_string(cpu, SUMMARY_BUF, "iks"); + gator_buffer_write_string(cpu, SUMMARY_BUF, ""); +#endif + // Let Streamline know which GPU is used so that it can label the GPU Activity appropriately. This is a temporary fix, to be improved in a future release. +#ifdef MALI_SUPPORT + gator_buffer_write_string(cpu, SUMMARY_BUF, "mali_type"); +#if (MALI_SUPPORT == MALI_4xx) + gator_buffer_write_string(cpu, SUMMARY_BUF, "4xx"); +#elif (MALI_SUPPORT == MALI_T6xx) + gator_buffer_write_string(cpu, SUMMARY_BUF, "6xx"); +#else + gator_buffer_write_string(cpu, SUMMARY_BUF, "unknown"); +#endif +#endif + gator_buffer_write_string(cpu, SUMMARY_BUF, ""); + // Commit the buffer now so it can be one of the first frames read by Streamline + gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time()); + local_irq_restore(flags); +} + +static bool marshal_cookie_header(const char *text) +{ + int cpu = get_physical_cpu(); + return buffer_check_space(cpu, NAME_BUF, strlen(text) + 3 * MAXSIZE_PACK32); +} + +static void marshal_cookie(int cookie, const char *text) +{ + int cpu = get_physical_cpu(); + // buffer_check_space already called by marshal_cookie_header + gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_COOKIE); + gator_buffer_write_packed_int(cpu, NAME_BUF, cookie); + gator_buffer_write_string(cpu, NAME_BUF, text); + buffer_check(cpu, NAME_BUF, gator_get_time()); +} + +static void marshal_thread_name(int pid, char *name) +{ + unsigned long flags, cpu; + u64 time; + local_irq_save(flags); + cpu = get_physical_cpu(); + time = gator_get_time(); + if (buffer_check_space(cpu, NAME_BUF, TASK_COMM_LEN + 3 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_THREAD_NAME); + gator_buffer_write_packed_int64(cpu, NAME_BUF, time); + gator_buffer_write_packed_int(cpu, NAME_BUF, pid); + gator_buffer_write_string(cpu, NAME_BUF, name); + } + buffer_check(cpu, NAME_BUF, time); + local_irq_restore(flags); +} + +static void marshal_link(int cookie, int tgid, int pid) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_LINK); + gator_buffer_write_packed_int64(cpu, NAME_BUF, time); + gator_buffer_write_packed_int(cpu, NAME_BUF, cookie); + gator_buffer_write_packed_int(cpu, NAME_BUF, tgid); + gator_buffer_write_packed_int(cpu, NAME_BUF, pid); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, NAME_BUF, time); + local_irq_restore(flags); +} + +static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inKernel, u64 time) +{ + int cpu = get_physical_cpu(); + if (!buffer_check_space(cpu, BACKTRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32 + gator_backtrace_depth * 2 * MAXSIZE_PACK32)) { + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, BACKTRACE_BUF, time); + + return false; + } + + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, time); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, exec_cookie); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, inKernel); + + return true; +} + +static void marshal_backtrace(unsigned long address, int cookie) +{ + int cpu = get_physical_cpu(); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, address); +} + +static void marshal_backtrace_footer(u64 time) +{ + int cpu = get_physical_cpu(); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_END_BACKTRACE); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, BACKTRACE_BUF, time); +} + +static bool marshal_event_header(u64 time) +{ + unsigned long flags, cpu = get_physical_cpu(); + bool retval = false; + + local_irq_save(flags); + if (buffer_check_space(cpu, BLOCK_COUNTER_BUF, MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, 0); // key of zero indicates a timestamp + gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, time); + retval = true; + } + local_irq_restore(flags); + + return retval; +} + +static void marshal_event(int len, int *buffer) +{ + unsigned long i, flags, cpu = get_physical_cpu(); + + if (len <= 0) + return; + + // length must be even since all data is a (key, value) pair + if (len & 0x1) { + pr_err("gator: invalid counter data detected and discarded"); + return; + } + + // events must be written in key,value pairs + local_irq_save(flags); + for (i = 0; i < len; i += 2) { + if (!buffer_check_space(cpu, BLOCK_COUNTER_BUF, 2 * MAXSIZE_PACK32)) { + break; + } + gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i]); + gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i + 1]); + } + local_irq_restore(flags); +} + +static void marshal_event64(int len, long long *buffer64) +{ + unsigned long i, flags, cpu = get_physical_cpu(); + + if (len <= 0) + return; + + // length must be even since all data is a (key, value) pair + if (len & 0x1) { + pr_err("gator: invalid counter data detected and discarded"); + return; + } + + // events must be written in key,value pairs + local_irq_save(flags); + for (i = 0; i < len; i += 2) { + if (!buffer_check_space(cpu, BLOCK_COUNTER_BUF, 2 * MAXSIZE_PACK64)) { + break; + } + gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i]); + gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i + 1]); + } + local_irq_restore(flags); +} + +#if GATOR_CPU_FREQ_SUPPORT +static void marshal_event_single(int core, int key, int value) +{ + unsigned long flags, cpu; + u64 time; + + local_irq_save(flags); + cpu = get_physical_cpu(); + time = gator_get_time(); + if (buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, time); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, core); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, key); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, value); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF, time); + local_irq_restore(flags); +} +#endif + +static void marshal_sched_gpu_start(int unit, int core, int tgid, int pid) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, MESSAGE_GPU_START); + gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, time); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, pid); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, GPU_TRACE_BUF, time); + local_irq_restore(flags); +} + +static void marshal_sched_gpu_stop(int unit, int core) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, MESSAGE_GPU_STOP); + gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, time); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, GPU_TRACE_BUF, time); + local_irq_restore(flags); +} + +static void marshal_sched_trace_start(int tgid, int pid, int cookie) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_START); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, SCHED_TRACE_BUF, time); + local_irq_restore(flags); +} + +static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_SWITCH); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, SCHED_TRACE_BUF, time); + local_irq_restore(flags); +} + +static void marshal_sched_trace_exit(int tgid, int pid) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_EXIT); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, SCHED_TRACE_BUF, time); + local_irq_restore(flags); +} + +#if GATOR_CPU_FREQ_SUPPORT +static void marshal_idle(int core, int state) +{ + unsigned long flags, cpu; + u64 time; + + local_irq_save(flags); + cpu = get_physical_cpu(); + time = gator_get_time(); + if (buffer_check_space(cpu, IDLE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, IDLE_BUF, state); + gator_buffer_write_packed_int64(cpu, IDLE_BUF, time); + gator_buffer_write_packed_int(cpu, IDLE_BUF, core); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, IDLE_BUF, time); + local_irq_restore(flags); +} +#endif + +static void marshal_frame(int cpu, int buftype) +{ + int frame; + + if (!per_cpu(gator_buffer, cpu)[buftype]) { + return; + } + + switch (buftype) { + case SUMMARY_BUF: + frame = FRAME_SUMMARY; + break; + case BACKTRACE_BUF: + frame = FRAME_BACKTRACE; + break; + case NAME_BUF: + frame = FRAME_NAME; + break; + case COUNTER_BUF: + frame = FRAME_COUNTER; + break; + case BLOCK_COUNTER_BUF: + frame = FRAME_BLOCK_COUNTER; + break; + case ANNOTATE_BUF: + frame = FRAME_ANNOTATE; + break; + case SCHED_TRACE_BUF: + frame = FRAME_SCHED_TRACE; + break; + case GPU_TRACE_BUF: + frame = FRAME_GPU_TRACE; + break; + case IDLE_BUF: + frame = FRAME_IDLE; + break; + default: + frame = -1; + break; + } + + // add response type + if (gator_response_type > 0) { + gator_buffer_write_packed_int(cpu, buftype, gator_response_type); + } + + // leave space for 4-byte unpacked length + per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + sizeof(s32)) & gator_buffer_mask[buftype]; + + // add frame type and core number + gator_buffer_write_packed_int(cpu, buftype, frame); + gator_buffer_write_packed_int(cpu, buftype, cpu); +} + +#if defined(__arm__) || defined(__aarch64__) +static void marshal_core_name(const int core, const int cpuid, const char *name) +{ + int cpu = get_physical_cpu(); + unsigned long flags; + local_irq_save(flags); + if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) { + gator_buffer_write_packed_int(cpu, NAME_BUF, HRTIMER_CORE_NAME); + gator_buffer_write_packed_int(cpu, NAME_BUF, core); + gator_buffer_write_packed_int(cpu, NAME_BUF, cpuid); + gator_buffer_write_string(cpu, NAME_BUF, name); + } + // Commit core names now so that they can show up in live + gator_commit_buffer(cpu, NAME_BUF, gator_get_time()); + local_irq_restore(flags); +} +#endif diff -Nur linux-3.10.30/drivers/gator/gator_pack.c linux-3.10.30-cubox-i/drivers/gator/gator_pack.c --- linux-3.10.30/drivers/gator/gator_pack.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_pack.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,58 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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. + * + */ + +static void gator_buffer_write_packed_int(int cpu, int buftype, int x) +{ + uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; + uint32_t mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buffer[(write + packedBytes) & mask] = b; + packedBytes++; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; +} + +static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x) +{ + uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; + uint32_t mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buffer[(write + packedBytes) & mask] = b; + packedBytes++; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; +} diff -Nur linux-3.10.30/drivers/gator/gator_trace_gpu.c linux-3.10.30-cubox-i/drivers/gator/gator_trace_gpu.c --- linux-3.10.30/drivers/gator/gator_trace_gpu.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_trace_gpu.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,294 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 "gator.h" + +#include +#include +#include +#include + +#ifdef MALI_SUPPORT +#include "linux/mali_linux_trace.h" +#endif +#include "gator_trace_gpu.h" + +/* + * Taken from MALI_PROFILING_EVENT_TYPE_* items in Mali DDK. + */ +#define EVENT_TYPE_SINGLE 0 +#define EVENT_TYPE_START 1 +#define EVENT_TYPE_STOP 2 +#define EVENT_TYPE_SUSPEND 3 +#define EVENT_TYPE_RESUME 4 + +/* Note whether tracepoints have been registered */ +static int mali_timeline_trace_registered; +static int mali_job_slots_trace_registered; +static int gpu_trace_registered; + +enum { + GPU_UNIT_NONE = 0, + GPU_UNIT_VP, + GPU_UNIT_FP, + GPU_UNIT_CL, + NUMBER_OF_GPU_UNITS +}; + +#define MALI_4xx (0x0b07) +#define MALI_T6xx (0x0056) + +struct mali_gpu_job { + int count; + int last_tgid; + int last_pid; + int last_job_id; +}; + +#define NUMBER_OF_GPU_CORES 16 +static struct mali_gpu_job mali_gpu_jobs[NUMBER_OF_GPU_UNITS][NUMBER_OF_GPU_CORES]; +static DEFINE_SPINLOCK(mali_gpu_jobs_lock); + +/* Only one event should be running on a unit and core at a time (ie, a start + * event can only be followed by a stop and vice versa), but because the kernel + * only knows when a job is enqueued and not started, it is possible for a + * start1, start2, stop1, stop2. Change it back into start1, stop1, start2, + * stop2 by queueing up start2 and releasing it when stop1 is received. + */ +static void mali_gpu_enqueue(int unit, int core, int tgid, int pid, int job_id) +{ + int count; + + spin_lock(&mali_gpu_jobs_lock); + count = mali_gpu_jobs[unit][core].count; + BUG_ON(count < 0); + ++mali_gpu_jobs[unit][core].count; + if (count) { + mali_gpu_jobs[unit][core].last_tgid = tgid; + mali_gpu_jobs[unit][core].last_pid = pid; + mali_gpu_jobs[unit][core].last_job_id = job_id; + } + spin_unlock(&mali_gpu_jobs_lock); + + if (!count) { + marshal_sched_gpu_start(unit, core, tgid, pid/*, job_id*/); + } +} + +static void mali_gpu_stop(int unit, int core) +{ + int count; + int last_tgid = 0; + int last_pid = 0; + //int last_job_id = 0; + + spin_lock(&mali_gpu_jobs_lock); + if (mali_gpu_jobs[unit][core].count == 0) { + spin_unlock(&mali_gpu_jobs_lock); + return; + } + --mali_gpu_jobs[unit][core].count; + count = mali_gpu_jobs[unit][core].count; + if (count) { + last_tgid = mali_gpu_jobs[unit][core].last_tgid; + last_pid = mali_gpu_jobs[unit][core].last_pid; + //last_job_id = mali_gpu_jobs[unit][core].last_job_id; + } + spin_unlock(&mali_gpu_jobs_lock); + + marshal_sched_gpu_stop(unit, core); + if (count) { + marshal_sched_gpu_start(unit, core, last_tgid, last_pid/*, last_job_id*/); + } +} + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) +#include "gator_events_mali_4xx.h" + +/* + * Taken from MALI_PROFILING_EVENT_CHANNEL_* in Mali DDK. + */ +enum { + EVENT_CHANNEL_SOFTWARE = 0, + EVENT_CHANNEL_VP0 = 1, + EVENT_CHANNEL_FP0 = 5, + EVENT_CHANNEL_FP1, + EVENT_CHANNEL_FP2, + EVENT_CHANNEL_FP3, + EVENT_CHANNEL_FP4, + EVENT_CHANNEL_FP5, + EVENT_CHANNEL_FP6, + EVENT_CHANNEL_FP7, + EVENT_CHANNEL_GPU = 21 +}; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel + */ +enum { + EVENT_REASON_SINGLE_GPU_NONE = 0, + EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1, +}; + +GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4)) +{ + unsigned int component, state; + + // do as much work as possible before disabling interrupts + component = (event_id >> 16) & 0xFF; // component is an 8-bit field + state = (event_id >> 24) & 0xF; // state is a 4-bit field + + switch (state) { + case EVENT_TYPE_START: + if (component == EVENT_CHANNEL_VP0) { + /* tgid = d0; pid = d1; */ + mali_gpu_enqueue(GPU_UNIT_VP, 0, d0, d1, 0); + } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { + /* tgid = d0; pid = d1; */ + mali_gpu_enqueue(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0, d0, d1, 0); + } + break; + + case EVENT_TYPE_STOP: + if (component == EVENT_CHANNEL_VP0) { + mali_gpu_stop(GPU_UNIT_VP, 0); + } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { + mali_gpu_stop(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0); + } + break; + + case EVENT_TYPE_SINGLE: + if (component == EVENT_CHANNEL_GPU) { + unsigned int reason = (event_id & 0xffff); + + if (reason == EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE) { + gator_events_mali_log_dvfs_event(d0, d1); + } + } + break; + + default: + break; + } +} +#endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) +#if defined(MALI_JOB_SLOTS_EVENT_CHANGED) +GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid, unsigned char job_id)) +#else +GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid)) +#endif +{ + unsigned int component, state, unit; +#if !defined(MALI_JOB_SLOTS_EVENT_CHANGED) + unsigned char job_id = 0; +#endif + + component = (event_id >> 16) & 0xFF; // component is an 8-bit field + state = (event_id >> 24) & 0xF; // state is a 4-bit field + + switch (component) { + case 0: + unit = GPU_UNIT_FP; + break; + case 1: + unit = GPU_UNIT_VP; + break; + case 2: + unit = GPU_UNIT_CL; + break; + default: + unit = GPU_UNIT_NONE; + } + + if (unit != GPU_UNIT_NONE) { + switch (state) { + case EVENT_TYPE_START: + mali_gpu_enqueue(unit, 0, tgid, (pid != 0 ? pid : tgid), job_id); + break; + case EVENT_TYPE_STOP: + mali_gpu_stop(unit, 0); + break; + default: + /* + * Some jobs can be soft-stopped, so ensure that this terminates the activity trace. + */ + mali_gpu_stop(unit, 0); + } + } +} +#endif + +GATOR_DEFINE_PROBE(gpu_activity_start, TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p)) +{ + mali_gpu_enqueue(gpu_unit, gpu_core, (int)p->tgid, (int)p->pid, 0); +} + +GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core)) +{ + mali_gpu_stop(gpu_unit, gpu_core); +} + +int gator_trace_gpu_start(void) +{ + /* + * Returns nonzero for installation failed + * Absence of gpu trace points is not an error + */ + + memset(&mali_gpu_jobs, 0, sizeof(mali_gpu_jobs)); + gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) + if (!GATOR_REGISTER_TRACE(mali_timeline_event)) { + mali_timeline_trace_registered = 1; + } +#endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + if (!GATOR_REGISTER_TRACE(mali_job_slots_event)) { + mali_job_slots_trace_registered = 1; + } +#endif + + if (!mali_timeline_trace_registered) { + if (GATOR_REGISTER_TRACE(gpu_activity_start)) { + return 0; + } + if (GATOR_REGISTER_TRACE(gpu_activity_stop)) { + GATOR_UNREGISTER_TRACE(gpu_activity_start); + return 0; + } + gpu_trace_registered = 1; + } + + return 0; +} + +void gator_trace_gpu_stop(void) +{ +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) + if (mali_timeline_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_timeline_event); + } +#endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + if (mali_job_slots_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_job_slots_event); + } +#endif + + if (gpu_trace_registered) { + GATOR_UNREGISTER_TRACE(gpu_activity_stop); + GATOR_UNREGISTER_TRACE(gpu_activity_start); + } + + gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; +} diff -Nur linux-3.10.30/drivers/gator/gator_trace_gpu.h linux-3.10.30-cubox-i/drivers/gator/gator_trace_gpu.h --- linux-3.10.30/drivers/gator/gator_trace_gpu.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_trace_gpu.h 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,79 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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. + */ + +#undef TRACE_GPU +#define TRACE_GPU gpu + +#if !defined(_TRACE_GPU_H) +#define _TRACE_GPU_H + +#include + +/* + * UNIT - the GPU processor type + * 1 = Vertex Processor + * 2 = Fragment Processor + * + * CORE - the GPU processor core number + * this is not the CPU core number + */ + +/* + * Tracepoint for calling GPU unit start activity on core + */ +TRACE_EVENT(gpu_activity_start, + + TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p), + + TP_ARGS(gpu_unit, gpu_core, p), + + TP_STRUCT__entry( + __field(int, gpu_unit) + __field(int, gpu_core) + __array(char, comm, TASK_COMM_LEN) + __field(pid_t, pid) + ), + + TP_fast_assign( + __entry->gpu_unit = gpu_unit; + __entry->gpu_core = gpu_core; + memcpy(__entry->comm, p->comm, TASK_COMM_LEN); + __entry->pid = p->pid; + ), + + TP_printk("unit=%d core=%d comm=%s pid=%d", + __entry->gpu_unit, __entry->gpu_core, __entry->comm, + __entry->pid) + ); + +/* + * Tracepoint for calling GPU unit stop activity on core + */ +TRACE_EVENT(gpu_activity_stop, + + TP_PROTO(int gpu_unit, int gpu_core), + + TP_ARGS(gpu_unit, gpu_core), + + TP_STRUCT__entry( + __field(int, gpu_unit) + __field(int, gpu_core) + ), + + TP_fast_assign( + __entry->gpu_unit = gpu_unit; + __entry->gpu_core = gpu_core; + ), + + TP_printk("unit=%d core=%d", __entry->gpu_unit, __entry->gpu_core) + ); + +#endif /* _TRACE_GPU_H */ + +/* This part must be outside protection */ +#include diff -Nur linux-3.10.30/drivers/gator/gator_trace_power.c linux-3.10.30-cubox-i/drivers/gator/gator_trace_power.c --- linux-3.10.30/drivers/gator/gator_trace_power.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_trace_power.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,203 @@ +/** + * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * + * 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 +#include + +#if defined(__arm__) + +#include + +#define implements_wfi() (!machine_is_omap3_beagle()) + +#else + +#define implements_wfi() false + +#endif + +// cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38 +// the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86 +#if GATOR_CPU_FREQ_SUPPORT +enum { + POWER_CPU_FREQ, + POWER_CPU_IDLE, + POWER_TOTAL +}; + +static DEFINE_PER_CPU(ulong, idle_prev_state); +static ulong power_cpu_enabled[POWER_TOTAL]; +static ulong power_cpu_key[POWER_TOTAL]; + +static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int cpu; + bool found_nonzero_freq = false; + + // Even if CONFIG_CPU_FREQ is defined, it still may not be used. Check + // for non-zero values from cpufreq_quick_get + for_each_online_cpu(cpu) { + if (cpufreq_quick_get(cpu) > 0) { + found_nonzero_freq = true; + break; + } + } + + if (found_nonzero_freq) { + // cpu_frequency + dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]); + gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]); + } + + // cpu_idle + dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]); + gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]); + + return 0; +} + +// 'cpu' may not equal smp_processor_id(), i.e. may not be running on the core that is having the freq/idle state change +GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu)) +{ + cpu = lcpu_to_pcpu(cpu); + marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000); +} + +GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) +{ + cpu = lcpu_to_pcpu(cpu); + + if (state == per_cpu(idle_prev_state, cpu)) { + return; + } + + if (implements_wfi()) { + if (state == PWR_EVENT_EXIT) { + // transition from wfi to non-wfi + marshal_idle(cpu, MESSAGE_IDLE_EXIT); + } else { + // transition from non-wfi to wfi + marshal_idle(cpu, MESSAGE_IDLE_ENTER); + } + } + + per_cpu(idle_prev_state, cpu) = state; + + if (power_cpu_enabled[POWER_CPU_IDLE]) { + // Increment state so that no negative numbers are sent + marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state + 1); + } +} + +static void gator_trace_power_online(void) +{ + int pcpu = get_physical_cpu(); + int lcpu = get_logical_cpu(); + if (power_cpu_enabled[POWER_CPU_FREQ]) { + marshal_event_single(pcpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(lcpu) * 1000); + } +} + +static void gator_trace_power_offline(void) +{ + // Set frequency to zero on an offline + int cpu = get_physical_cpu(); + if (power_cpu_enabled[POWER_CPU_FREQ]) { + marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], 0); + } +} + +static int gator_trace_power_start(void) +{ + int cpu; + + // register tracepoints + if (power_cpu_enabled[POWER_CPU_FREQ]) + if (GATOR_REGISTER_TRACE(cpu_frequency)) + goto fail_cpu_frequency_exit; + + // Always register for cpu:idle for detecting WFI, independent of power_cpu_enabled[POWER_CPU_IDLE] + if (GATOR_REGISTER_TRACE(cpu_idle)) + goto fail_cpu_idle_exit; + pr_debug("gator: registered power event tracepoints\n"); + + for_each_present_cpu(cpu) { + per_cpu(idle_prev_state, cpu) = 0; + } + + return 0; + + // unregister tracepoints on error +fail_cpu_idle_exit: + if (power_cpu_enabled[POWER_CPU_FREQ]) + GATOR_UNREGISTER_TRACE(cpu_frequency); +fail_cpu_frequency_exit: + pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_trace_power_stop(void) +{ + int i; + + if (power_cpu_enabled[POWER_CPU_FREQ]) + GATOR_UNREGISTER_TRACE(cpu_frequency); + GATOR_UNREGISTER_TRACE(cpu_idle); + pr_debug("gator: unregistered power event tracepoints\n"); + + for (i = 0; i < POWER_TOTAL; i++) { + power_cpu_enabled[i] = 0; + } +} + +void gator_trace_power_init(void) +{ + int i; + for (i = 0; i < POWER_TOTAL; i++) { + power_cpu_enabled[i] = 0; + power_cpu_key[i] = gator_events_get_key(); + } +} +#else +static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root) +{ + return 0; +} + +static void gator_trace_power_online(void) +{ +} + +static void gator_trace_power_offline(void) +{ +} + +static int gator_trace_power_start(void) +{ + return 0; +} + +static void gator_trace_power_stop(void) +{ +} + +void gator_trace_power_init(void) +{ +} +#endif diff -Nur linux-3.10.30/drivers/gator/gator_trace_sched.c linux-3.10.30-cubox-i/drivers/gator/gator_trace_sched.c --- linux-3.10.30/drivers/gator/gator_trace_sched.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/gator_trace_sched.c 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,270 @@ +/** + * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * + * 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 +#include "gator.h" + +#define TASK_MAP_ENTRIES 1024 /* must be power of 2 */ +#define TASK_MAX_COLLISIONS 2 + +enum { + STATE_WAIT_ON_OTHER = 0, + STATE_CONTENTION, + STATE_WAIT_ON_IO, + CPU_WAIT_TOTAL +}; + +static DEFINE_PER_CPU(uint64_t *, taskname_keys); +static DEFINE_PER_CPU(int, collecting); +static DEFINE_PER_CPU(bool, in_scheduler_context); + +// this array is never read as the cpu wait charts are derived counters +// the files are needed, nonetheless, to show that these counters are available +static ulong cpu_wait_enabled[CPU_WAIT_TOTAL]; +static ulong sched_cpu_key[CPU_WAIT_TOTAL]; + +static int sched_trace_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + // CPU Wait - Contention + dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_contention"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_CONTENTION]); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_CONTENTION]); + + // CPU Wait - I/O + dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_io"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_WAIT_ON_IO]); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_WAIT_ON_IO]); + + return 0; +} + +void emit_pid_name(struct task_struct *task) +{ + bool found = false; + char taskcomm[TASK_COMM_LEN + 3]; + unsigned long x, cpu = get_physical_cpu(); + uint64_t *keys = &(per_cpu(taskname_keys, cpu)[(task->pid & 0xFF) * TASK_MAX_COLLISIONS]); + uint64_t value; + + value = gator_chksum_crc32(task->comm); + value = (value << 32) | (uint32_t)task->pid; + + // determine if the thread name was emitted already + for (x = 0; x < TASK_MAX_COLLISIONS; x++) { + if (keys[x] == value) { + found = true; + break; + } + } + + if (!found) { + // shift values, new value always in front + uint64_t oldv, newv = value; + for (x = 0; x < TASK_MAX_COLLISIONS; x++) { + oldv = keys[x]; + keys[x] = newv; + newv = oldv; + } + + // emit pid names, cannot use get_task_comm, as it's not exported on all kernel versions + if (strlcpy(taskcomm, task->comm, TASK_COMM_LEN) == TASK_COMM_LEN - 1) { + // append ellipses if task->comm has length of TASK_COMM_LEN - 1 + strcat(taskcomm, "..."); + } + + marshal_thread_name(task->pid, taskcomm); + } +} + +static void collect_counters(u64 time, struct task_struct *task) +{ + int *buffer, len, cpu = get_physical_cpu(); + long long *buffer64; + struct gator_interface *gi; + + if (marshal_event_header(time)) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->read) { + len = gi->read(&buffer); + marshal_event(len, buffer); + } else if (gi->read64) { + len = gi->read64(&buffer64); + marshal_event64(len, buffer64); + } + if (gi->read_proc && task != NULL) { + len = gi->read_proc(&buffer64, task); + marshal_event64(len, buffer64); + } + } + // Only check after writing all counters so that time and corresponding counters appear in the same frame + buffer_check(cpu, BLOCK_COUNTER_BUF, time); + + // Commit buffers on timeout + if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) { + static const int buftypes[] = { NAME_BUF, COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF }; + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < ARRAY_SIZE(buftypes); ++i) { + gator_commit_buffer(cpu, buftypes[i], time); + } + local_irq_restore(flags); + + // Try to preemptively flush the annotate buffer to reduce the chance of the buffer being full + if (on_primary_core() && spin_trylock(&annotate_lock)) { + gator_commit_buffer(0, ANNOTATE_BUF, time); + spin_unlock(&annotate_lock); + } + } + } +} + +// special case used during a suspend of the system +static void trace_sched_insert_idle(void) +{ + marshal_sched_trace_switch(0, 0, 0, 0); +} + +GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child)) +{ + int cookie; + int cpu = get_physical_cpu(); + + cookie = get_exec_cookie(cpu, child); + emit_pid_name(child); + + marshal_sched_trace_start(child->tgid, child->pid, cookie); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + int cookie; + int state; + int cpu = get_physical_cpu(); + + per_cpu(in_scheduler_context, cpu) = true; + + // do as much work as possible before disabling interrupts + cookie = get_exec_cookie(cpu, next); + emit_pid_name(next); + if (prev->state == TASK_RUNNING) { + state = STATE_CONTENTION; + } else if (prev->in_iowait) { + state = STATE_WAIT_ON_IO; + } else { + state = STATE_WAIT_ON_OTHER; + } + + per_cpu(collecting, cpu) = 1; + collect_counters(gator_get_time(), prev); + per_cpu(collecting, cpu) = 0; + + marshal_sched_trace_switch(next->tgid, next->pid, cookie, state); + + per_cpu(in_scheduler_context, cpu) = false; +} + +GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p)) +{ + marshal_sched_trace_exit(p->tgid, p->pid); +} + +static void do_nothing(void *info) +{ + // Intentionally do nothing + (void)info; +} + +static int register_scheduler_tracepoints(void) +{ + // register tracepoints + if (GATOR_REGISTER_TRACE(sched_process_fork)) + goto fail_sched_process_fork; + if (GATOR_REGISTER_TRACE(sched_switch)) + goto fail_sched_switch; + if (GATOR_REGISTER_TRACE(sched_process_free)) + goto fail_sched_process_free; + pr_debug("gator: registered tracepoints\n"); + + // Now that the scheduler tracepoint is registered, force a context switch + // on all cpus to capture what is currently running. + on_each_cpu(do_nothing, NULL, 0); + + return 0; + + // unregister tracepoints on error +fail_sched_process_free: + GATOR_UNREGISTER_TRACE(sched_switch); +fail_sched_switch: + GATOR_UNREGISTER_TRACE(sched_process_fork); +fail_sched_process_fork: + pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +int gator_trace_sched_start(void) +{ + int cpu, size; + + for_each_present_cpu(cpu) { + size = TASK_MAP_ENTRIES * TASK_MAX_COLLISIONS * sizeof(uint64_t); + per_cpu(taskname_keys, cpu) = (uint64_t *)kmalloc(size, GFP_KERNEL); + if (!per_cpu(taskname_keys, cpu)) + return -1; + memset(per_cpu(taskname_keys, cpu), 0, size); + } + + return register_scheduler_tracepoints(); +} + +void gator_trace_sched_offline(void) +{ + trace_sched_insert_idle(); +} + +static void unregister_scheduler_tracepoints(void) +{ + GATOR_UNREGISTER_TRACE(sched_process_fork); + GATOR_UNREGISTER_TRACE(sched_switch); + GATOR_UNREGISTER_TRACE(sched_process_free); + pr_debug("gator: unregistered tracepoints\n"); +} + +void gator_trace_sched_stop(void) +{ + int cpu; + unregister_scheduler_tracepoints(); + + for_each_present_cpu(cpu) { + kfree(per_cpu(taskname_keys, cpu)); + } +} + +void gator_trace_sched_init(void) +{ + int i; + for (i = 0; i < CPU_WAIT_TOTAL; i++) { + cpu_wait_enabled[i] = 0; + sched_cpu_key[i] = gator_events_get_key(); + } +} diff -Nur linux-3.10.30/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h linux-3.10.30-cubox-i/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h --- linux-3.10.30/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,163 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * 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 __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ +#define __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* + * The number of processor cores. Update to suit your hardware implementation. + */ +#define MAX_NUM_FP_CORES (4) +#define MAX_NUM_VP_CORES (1) +#define MAX_NUM_L2_CACHE_CORES (1) + +enum counters +{ + /* Timeline activity */ + ACTIVITY_VP_0 = 0, + ACTIVITY_FP_0, + ACTIVITY_FP_1, + ACTIVITY_FP_2, + ACTIVITY_FP_3, + + /* L2 cache counters */ + COUNTER_L2_0_C0, + COUNTER_L2_0_C1, + + /* Vertex processor counters */ + COUNTER_VP_0_C0, + COUNTER_VP_0_C1, + + /* Fragment processor counters */ + COUNTER_FP_0_C0, + COUNTER_FP_0_C1, + COUNTER_FP_1_C0, + COUNTER_FP_1_C1, + COUNTER_FP_2_C0, + COUNTER_FP_2_C1, + COUNTER_FP_3_C0, + COUNTER_FP_3_C1, + + /* EGL Software Counters */ + COUNTER_EGL_BLIT_TIME, + + /* GLES Software Counters */ + COUNTER_GLES_DRAW_ELEMENTS_CALLS, + COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES, + COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_ARRAYS_CALLS, + COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_POINTS, + COUNTER_GLES_DRAW_LINES, + COUNTER_GLES_DRAW_LINE_LOOP, + COUNTER_GLES_DRAW_LINE_STRIP, + COUNTER_GLES_DRAW_TRIANGLES, + COUNTER_GLES_DRAW_TRIANGLE_STRIP, + COUNTER_GLES_DRAW_TRIANGLE_FAN, + COUNTER_GLES_NON_VBO_DATA_COPY_TIME, + COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI, + COUNTER_GLES_UPLOAD_TEXTURE_TIME, + COUNTER_GLES_UPLOAD_VBO_TIME, + COUNTER_GLES_NUM_FLUSHES, + COUNTER_GLES_NUM_VSHADERS_GENERATED, + COUNTER_GLES_NUM_FSHADERS_GENERATED, + COUNTER_GLES_VSHADER_GEN_TIME, + COUNTER_GLES_FSHADER_GEN_TIME, + COUNTER_GLES_INPUT_TRIANGLES, + COUNTER_GLES_VXCACHE_HIT, + COUNTER_GLES_VXCACHE_MISS, + COUNTER_GLES_VXCACHE_COLLISION, + COUNTER_GLES_CULLED_TRIANGLES, + COUNTER_GLES_CULLED_LINES, + COUNTER_GLES_BACKFACE_TRIANGLES, + COUNTER_GLES_GBCLIP_TRIANGLES, + COUNTER_GLES_GBCLIP_LINES, + COUNTER_GLES_TRIANGLES_DRAWN, + COUNTER_GLES_DRAWCALL_TIME, + COUNTER_GLES_TRIANGLES_COUNT, + COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT, + COUNTER_GLES_STRIP_TRIANGLES_COUNT, + COUNTER_GLES_FAN_TRIANGLES_COUNT, + COUNTER_GLES_LINES_COUNT, + COUNTER_GLES_INDEPENDENT_LINES_COUNT, + COUNTER_GLES_STRIP_LINES_COUNT, + COUNTER_GLES_LOOP_LINES_COUNT, + + COUNTER_FILMSTRIP, + COUNTER_FREQUENCY, + COUNTER_VOLTAGE, + + NUMBER_OF_EVENTS +}; + +#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0 +#define LAST_ACTIVITY_EVENT ACTIVITY_FP_3 + +#define FIRST_HW_COUNTER COUNTER_L2_0_C0 +#define LAST_HW_COUNTER COUNTER_FP_3_C1 + +#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME +#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT + +/* Signifies that the system is able to report voltage and frequency numbers. */ +#define DVFS_REPORTED_BY_DDK 1 + +/** + * Structure to pass performance counter data of a Mali core + */ +typedef struct _mali_profiling_core_counters +{ + u32 source0; + u32 value0; + u32 source1; + u32 value1; +} _mali_profiling_core_counters; + +/* + * For compatibility with utgard. + */ +typedef struct _mali_profiling_l2_counter_values +{ + struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES]; +} _mali_profiling_l2_counter_values; + +typedef struct _mali_profiling_mali_version +{ + u32 mali_product_id; + u32 mali_version_major; + u32 mali_version_minor; + u32 num_of_l2_cores; + u32 num_of_fp_cores; + u32 num_of_vp_cores; +} _mali_profiling_mali_version; + +extern void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values); +extern u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values); + +/* + * List of possible actions allowing DDK to be controlled by Streamline. + * The following numbers are used by DDK to control the frame buffer dumping. + */ +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define SW_COUNTER_ENABLE (3) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ */ diff -Nur linux-3.10.30/drivers/gator/mali/mali_utgard_profiling_gator_api.h linux-3.10.30-cubox-i/drivers/gator/mali/mali_utgard_profiling_gator_api.h --- linux-3.10.30/drivers/gator/mali/mali_utgard_profiling_gator_api.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/mali/mali_utgard_profiling_gator_api.h 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,201 @@ +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * 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 __MALI_UTGARD_PROFILING_GATOR_API_H__ +#define __MALI_UTGARD_PROFILING_GATOR_API_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MALI_PROFILING_API_VERSION 4 + +#define MAX_NUM_L2_CACHE_CORES 3 +#define MAX_NUM_FP_CORES 8 +#define MAX_NUM_VP_CORES 1 + +/** The list of events supported by the Mali DDK. */ +typedef enum +{ + /* Vertex processor activity */ + ACTIVITY_VP_0 = 0, + + /* Fragment processor activity */ + ACTIVITY_FP_0, /* 1 */ + ACTIVITY_FP_1, + ACTIVITY_FP_2, + ACTIVITY_FP_3, + ACTIVITY_FP_4, + ACTIVITY_FP_5, + ACTIVITY_FP_6, + ACTIVITY_FP_7, + + /* L2 cache counters */ + COUNTER_L2_0_C0, + COUNTER_L2_0_C1, + COUNTER_L2_1_C0, + COUNTER_L2_1_C1, + COUNTER_L2_2_C0, + COUNTER_L2_2_C1, + + /* Vertex processor counters */ + COUNTER_VP_0_C0, /*15*/ + COUNTER_VP_0_C1, + + /* Fragment processor counters */ + COUNTER_FP_0_C0, + COUNTER_FP_0_C1, + COUNTER_FP_1_C0, + COUNTER_FP_1_C1, + COUNTER_FP_2_C0, + COUNTER_FP_2_C1, + COUNTER_FP_3_C0, + COUNTER_FP_3_C1, + COUNTER_FP_4_C0, + COUNTER_FP_4_C1, + COUNTER_FP_5_C0, + COUNTER_FP_5_C1, + COUNTER_FP_6_C0, + COUNTER_FP_6_C1, + COUNTER_FP_7_C0, + COUNTER_FP_7_C1, /* 32 */ + + /* + * If more hardware counters are added, the _mali_osk_hw_counter_table + * below should also be updated. + */ + + /* EGL software counters */ + COUNTER_EGL_BLIT_TIME, + + /* GLES software counters */ + COUNTER_GLES_DRAW_ELEMENTS_CALLS, + COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES, + COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_ARRAYS_CALLS, + COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_POINTS, + COUNTER_GLES_DRAW_LINES, + COUNTER_GLES_DRAW_LINE_LOOP, + COUNTER_GLES_DRAW_LINE_STRIP, + COUNTER_GLES_DRAW_TRIANGLES, + COUNTER_GLES_DRAW_TRIANGLE_STRIP, + COUNTER_GLES_DRAW_TRIANGLE_FAN, + COUNTER_GLES_NON_VBO_DATA_COPY_TIME, + COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI, + COUNTER_GLES_UPLOAD_TEXTURE_TIME, + COUNTER_GLES_UPLOAD_VBO_TIME, + COUNTER_GLES_NUM_FLUSHES, + COUNTER_GLES_NUM_VSHADERS_GENERATED, + COUNTER_GLES_NUM_FSHADERS_GENERATED, + COUNTER_GLES_VSHADER_GEN_TIME, + COUNTER_GLES_FSHADER_GEN_TIME, + COUNTER_GLES_INPUT_TRIANGLES, + COUNTER_GLES_VXCACHE_HIT, + COUNTER_GLES_VXCACHE_MISS, + COUNTER_GLES_VXCACHE_COLLISION, + COUNTER_GLES_CULLED_TRIANGLES, + COUNTER_GLES_CULLED_LINES, + COUNTER_GLES_BACKFACE_TRIANGLES, + COUNTER_GLES_GBCLIP_TRIANGLES, + COUNTER_GLES_GBCLIP_LINES, + COUNTER_GLES_TRIANGLES_DRAWN, + COUNTER_GLES_DRAWCALL_TIME, + COUNTER_GLES_TRIANGLES_COUNT, + COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT, + COUNTER_GLES_STRIP_TRIANGLES_COUNT, + COUNTER_GLES_FAN_TRIANGLES_COUNT, + COUNTER_GLES_LINES_COUNT, + COUNTER_GLES_INDEPENDENT_LINES_COUNT, + COUNTER_GLES_STRIP_LINES_COUNT, + COUNTER_GLES_LOOP_LINES_COUNT, + + /* Framebuffer capture pseudo-counter */ + COUNTER_FILMSTRIP, + + NUMBER_OF_EVENTS +} _mali_osk_counter_id; + +#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0 +#define LAST_ACTIVITY_EVENT ACTIVITY_FP_7 + +#define FIRST_HW_COUNTER COUNTER_L2_0_C0 +#define LAST_HW_COUNTER COUNTER_FP_7_C1 + +#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME +#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT + +#define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP +#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP + +/** + * Structure to pass performance counter data of a Mali core + */ +typedef struct _mali_profiling_core_counters +{ + u32 source0; + u32 value0; + u32 source1; + u32 value1; +} _mali_profiling_core_counters; + +/** + * Structure to pass performance counter data of Mali L2 cache cores + */ +typedef struct _mali_profiling_l2_counter_values +{ + struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES]; +} _mali_profiling_l2_counter_values; + +/** + * Structure to pass data defining Mali instance in use: + * + * mali_product_id - Mali product id + * mali_version_major - Mali version major number + * mali_version_minor - Mali version minor number + * num_of_l2_cores - number of L2 cache cores + * num_of_fp_cores - number of fragment processor cores + * num_of_vp_cores - number of vertex processor cores + */ +typedef struct _mali_profiling_mali_version +{ + u32 mali_product_id; + u32 mali_version_major; + u32 mali_version_minor; + u32 num_of_l2_cores; + u32 num_of_fp_cores; + u32 num_of_vp_cores; +} _mali_profiling_mali_version; + +/* + * List of possible actions to be controlled by Streamline. + * The following numbers are used by gator to control the frame buffer dumping and s/w counter reporting. + * We cannot use the enums in mali_uk_types.h because they are unknown inside gator. + */ +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define SW_COUNTER_ENABLE (3) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + +void _mali_profiling_control(u32 action, u32 value); + +u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values); + +int _mali_profiling_set_event(u32 counter_id, s32 event_id); + +u32 _mali_profiling_get_api_version(void); + +void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UTGARD_PROFILING_GATOR_API_H__ */ diff -Nur linux-3.10.30/drivers/gator/mali_t6xx.mk linux-3.10.30-cubox-i/drivers/gator/mali_t6xx.mk --- linux-3.10.30/drivers/gator/mali_t6xx.mk 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gator/mali_t6xx.mk 2014-03-08 20:33:31.000000000 +0100 @@ -0,0 +1,26 @@ +# Defines for Mali-T6xx driver +EXTRA_CFLAGS += -DMALI_USE_UMP=1 \ + -DMALI_LICENSE_IS_GPL=1 \ + -DMALI_BASE_TRACK_MEMLEAK=0 \ + -DMALI_DEBUG=0 \ + -DMALI_ERROR_INJECT_ON=0 \ + -DMALI_CUSTOMER_RELEASE=1 \ + -DMALI_UNIT_TEST=0 \ + -DMALI_BACKEND_KERNEL=1 \ + -DMALI_NO_MALI=0 + +DDK_DIR ?= . +KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase +OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase/osk +UMP_DIR = $(DDK_DIR)/include/linux + +# Include directories in the DDK +EXTRA_CFLAGS += -I$(KBASE_DIR)/ \ + -I$(KBASE_DIR)/.. \ + -I$(OSK_DIR)/.. \ + -I$(UMP_DIR)/.. \ + -I$(DDK_DIR)/include \ + -I$(KBASE_DIR)/osk/src/linux/include \ + -I$(KBASE_DIR)/platform_dummy \ + -I$(KBASE_DIR)/src + diff -Nur linux-3.10.30/drivers/gpio/devres.c linux-3.10.30-cubox-i/drivers/gpio/devres.c --- linux-3.10.30/drivers/gpio/devres.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpio/devres.c 2014-03-08 20:33:31.000000000 +0100 @@ -19,6 +19,89 @@ #include #include +static void devm_gpiod_release(struct device *dev, void *res) +{ + struct gpio_desc **desc = res; + + gpiod_put(*desc); +} + +static int devm_gpiod_match(struct device *dev, void *res, void *data) +{ + struct gpio_desc **this = res, **gpio = data; + + return *this == *gpio; +} + +/** + * devm_gpiod_get - Resource-managed gpiod_get() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * + * Managed gpiod_get(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get() for detailed + * information about behavior and return values. + */ +struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, + const char *con_id) +{ + return devm_gpiod_get_index(dev, con_id, 0); +} +EXPORT_SYMBOL(devm_gpiod_get); + +/** + * devm_gpiod_get_index - Resource-managed gpiod_get_index() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * + * Managed gpiod_get_index(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get_index() for detailed + * information about behavior and return values. + */ +struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx) +{ + struct gpio_desc **dr; + struct gpio_desc *desc; + + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpiod_desc *), + GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + desc = gpiod_get_index(dev, con_id, idx); + if (IS_ERR(desc)) { + devres_free(dr); + return desc; + } + + *dr = desc; + devres_add(dev, dr); + + return desc; +} +EXPORT_SYMBOL(devm_gpiod_get_index); + +/** + * devm_gpiod_put - Resource-managed gpiod_put() + * @desc: GPIO descriptor to dispose of + * + * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or + * devm_gpiod_get_index(). Normally this function will not be called as the GPIO + * will be disposed of by the resource management code. + */ +void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) +{ + WARN_ON(devres_release(dev, devm_gpiod_release, devm_gpiod_match, + &desc)); +} +EXPORT_SYMBOL(devm_gpiod_put); + + + + static void devm_gpio_release(struct device *dev, void *res) { unsigned *gpio = res; diff -Nur linux-3.10.30/drivers/gpio/gpio-mxc.c linux-3.10.30-cubox-i/drivers/gpio/gpio-mxc.c --- linux-3.10.30/drivers/gpio/gpio-mxc.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpio/gpio-mxc.c 2014-03-08 20:33:31.000000000 +0100 @@ -405,34 +405,19 @@ mxc_gpio_get_hw(pdev); - port = kzalloc(sizeof(struct mxc_gpio_port), GFP_KERNEL); + port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) { - err = -ENODEV; - goto out_kfree; - } - - if (!request_mem_region(iores->start, resource_size(iores), - pdev->name)) { - err = -EBUSY; - goto out_kfree; - } - - port->base = ioremap(iores->start, resource_size(iores)); - if (!port->base) { - err = -ENOMEM; - goto out_release_mem; - } + port->base = devm_ioremap_resource(&pdev->dev, iores); + if (IS_ERR(port->base)) + return PTR_ERR(port->base); port->irq_high = platform_get_irq(pdev, 1); port->irq = platform_get_irq(pdev, 0); - if (port->irq < 0) { - err = -EINVAL; - goto out_iounmap; - } + if (port->irq < 0) + return -EINVAL; /* disable the interrupt and clear the status */ writel(0, port->base + GPIO_IMR); @@ -462,7 +447,7 @@ port->base + GPIO_DR, NULL, port->base + GPIO_GDIR, NULL, 0); if (err) - goto out_iounmap; + goto out_bgio; port->bgc.gc.to_irq = mxc_gpio_to_irq; port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 : @@ -498,12 +483,7 @@ WARN_ON(gpiochip_remove(&port->bgc.gc) < 0); out_bgpio_remove: bgpio_remove(&port->bgc); -out_iounmap: - iounmap(port->base); -out_release_mem: - release_mem_region(iores->start, resource_size(iores)); -out_kfree: - kfree(port); +out_bgio: dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); return err; } diff -Nur linux-3.10.30/drivers/gpio/gpio-pca953x.c linux-3.10.30-cubox-i/drivers/gpio/gpio-pca953x.c --- linux-3.10.30/drivers/gpio/gpio-pca953x.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpio/gpio-pca953x.c 2014-03-08 20:33:31.000000000 +0100 @@ -19,6 +19,7 @@ #include #include #include +#include #include #ifdef CONFIG_OF_GPIO #include @@ -752,6 +753,10 @@ mutex_init(&chip->i2c_lock); + ret = device_reset(&client->dev); + if (ret == -ENODEV) + return -EPROBE_DEFER; + /* initialize cached registers from their original values. * we can't share this chip with another i2c master. */ diff -Nur linux-3.10.30/drivers/gpio/gpiolib-of.c linux-3.10.30-cubox-i/drivers/gpio/gpiolib-of.c --- linux-3.10.30/drivers/gpio/gpiolib-of.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpio/gpiolib-of.c 2014-03-08 20:33:32.000000000 +0100 @@ -15,19 +15,21 @@ #include #include #include -#include +#include #include #include #include #include #include +struct gpio_desc; + /* Private data structure for of_gpiochip_find_and_xlate */ struct gg_data { enum of_gpio_flags *flags; struct of_phandle_args gpiospec; - int out_gpio; + struct gpio_desc *out_gpio; }; /* Private function for resolving node pointer to gpio_chip */ @@ -45,28 +47,31 @@ if (ret < 0) return false; - gg_data->out_gpio = ret + gc->base; + gg_data->out_gpio = gpio_to_desc(ret + gc->base); return true; } /** - * of_get_named_gpio_flags() - Get a GPIO number and flags to use with GPIO API + * of_get_named_gpiod_flags() - Get a GPIO descriptor and flags for GPIO API * @np: device node to get GPIO from * @propname: property name containing gpio specifier(s) * @index: index of the GPIO * @flags: a flags pointer to fill in * - * Returns GPIO number to use with Linux generic GPIO API, or one of the errno + * Returns GPIO descriptor to use with Linux GPIO API, or one of the errno * value on the error condition. If @flags is not NULL the function also fills * in flags for the GPIO. */ -int of_get_named_gpio_flags(struct device_node *np, const char *propname, - int index, enum of_gpio_flags *flags) +struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, + const char *propname, int index, enum of_gpio_flags *flags) { /* Return -EPROBE_DEFER to support probe() functions to be called * later when the GPIO actually becomes available */ - struct gg_data gg_data = { .flags = flags, .out_gpio = -EPROBE_DEFER }; + struct gg_data gg_data = { + .flags = flags, + .out_gpio = ERR_PTR(-EPROBE_DEFER) + }; int ret; /* .of_xlate might decide to not fill in the flags, so clear it. */ @@ -76,17 +81,19 @@ ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, &gg_data.gpiospec); if (ret) { - pr_debug("%s: can't parse gpios property\n", __func__); - return ret; + pr_debug("%s: can't parse gpios property of node '%s[%d]'\n", + __func__, np->full_name, index); + return ERR_PTR(ret); } gpiochip_find(&gg_data, of_gpiochip_find_and_xlate); of_node_put(gg_data.gpiospec.np); - pr_debug("%s exited with status %d\n", __func__, gg_data.out_gpio); + pr_debug("%s exited with status %d\n", __func__, + PTR_RET(gg_data.out_gpio)); return gg_data.out_gpio; } -EXPORT_SYMBOL(of_get_named_gpio_flags); +EXPORT_SYMBOL(of_get_named_gpiod_flags); /** * of_gpio_simple_xlate - translate gpio_spec to the GPIO number and flags @@ -194,8 +201,8 @@ return; for (;; index++) { - ret = of_parse_phandle_with_args(np, "gpio-ranges", - "#gpio-range-cells", index, &pinspec); + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + index, &pinspec); if (ret) break; diff -Nur linux-3.10.30/drivers/gpio/gpiolib.c linux-3.10.30-cubox-i/drivers/gpio/gpiolib.c --- linux-3.10.30/drivers/gpio/gpiolib.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpio/gpiolib.c 2014-03-08 20:33:32.000000000 +0100 @@ -12,20 +12,16 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include -/* Optional implementation infrastructure for GPIO interfaces. +/* Implementation infrastructure for GPIO interfaces. * - * Platforms may want to use this if they tend to use very many GPIOs - * that aren't part of a System-On-Chip core; or across I2C/SPI/etc. - * - * When kernel footprint or instruction count is an issue, simpler - * implementations may be preferred. The GPIO programming interface - * allows for inlining speed-critical get/set operations for common - * cases, so that access to SOC-integrated GPIOs can sometimes cost - * only an instruction or two per bit. + * The GPIO programming interface allows for inlining speed-critical + * get/set operations for common cases, so that access to SOC-integrated + * GPIOs can sometimes cost only an instruction or two per bit. */ @@ -57,9 +53,10 @@ #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */ #define FLAG_TRIG_FALL 4 /* trigger on falling edge */ #define FLAG_TRIG_RISE 5 /* trigger on rising edge */ -#define FLAG_ACTIVE_LOW 6 /* sysfs value has active low */ +#define FLAG_ACTIVE_LOW 6 /* value has active low */ #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ +#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define ID_SHIFT 16 /* add new flags before this one */ @@ -74,34 +71,50 @@ #define GPIO_OFFSET_VALID(chip, offset) (offset >= 0 && offset < chip->ngpio) +static DEFINE_MUTEX(gpio_lookup_lock); +static LIST_HEAD(gpio_lookup_list); static LIST_HEAD(gpio_chips); #ifdef CONFIG_GPIO_SYSFS static DEFINE_IDR(dirent_idr); #endif -/* - * Internal gpiod_* API using descriptors instead of the integer namespace. - * Most of this should eventually go public. - */ static int gpiod_request(struct gpio_desc *desc, const char *label); static void gpiod_free(struct gpio_desc *desc); -static int gpiod_direction_input(struct gpio_desc *desc); -static int gpiod_direction_output(struct gpio_desc *desc, int value); -static int gpiod_get_direction(const struct gpio_desc *desc); -static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); -static int gpiod_get_value_cansleep(const struct gpio_desc *desc); -static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); -static int gpiod_get_value(const struct gpio_desc *desc); -static void gpiod_set_value(struct gpio_desc *desc, int value); -static int gpiod_cansleep(const struct gpio_desc *desc); -static int gpiod_to_irq(const struct gpio_desc *desc); -static int gpiod_export(struct gpio_desc *desc, bool direction_may_change); -static int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc); -static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); -static void gpiod_unexport(struct gpio_desc *desc); +#ifdef CONFIG_DEBUG_FS +#define gpiod_emerg(desc, fmt, ...) \ + pr_emerg("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_crit(desc, fmt, ...) \ + pr_crit("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_err(desc, fmt, ...) \ + pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_warn(desc, fmt, ...) \ + pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_info(desc, fmt, ...) \ + pr_info("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#define gpiod_dbg(desc, fmt, ...) \ + pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label, \ + ##__VA_ARGS__) +#else +#define gpiod_emerg(desc, fmt, ...) \ + pr_emerg("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_crit(desc, fmt, ...) \ + pr_crit("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_err(desc, fmt, ...) \ + pr_err("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_warn(desc, fmt, ...) \ + pr_warn("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_info(desc, fmt, ...) \ + pr_info("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#define gpiod_dbg(desc, fmt, ...) \ + pr_debug("gpio-%d: " fmt, desc_to_gpio(desc), ##__VA_ARGS__) +#endif static inline void desc_set_label(struct gpio_desc *d, const char *label) { @@ -121,23 +134,36 @@ /** * Convert a GPIO number to its descriptor */ -static struct gpio_desc *gpio_to_desc(unsigned gpio) +struct gpio_desc *gpio_to_desc(unsigned gpio) { if (WARN(!gpio_is_valid(gpio), "invalid GPIO %d\n", gpio)) return NULL; else return &gpio_desc[gpio]; } +EXPORT_SYMBOL_GPL(gpio_to_desc); + +/** + * Convert an offset on a certain chip to a corresponding descriptor + */ +static struct gpio_desc *gpiochip_offset_to_desc(struct gpio_chip *chip, + unsigned int offset) +{ + unsigned int gpio = chip->base + offset; + + return gpio_to_desc(gpio); +} /** * Convert a GPIO descriptor to the integer namespace. * This should disappear in the future but is needed since we still * use GPIO numbers for error messages and sysfs nodes */ -static int desc_to_gpio(const struct gpio_desc *desc) +int desc_to_gpio(const struct gpio_desc *desc) { - return desc->chip->base + gpio_chip_hwgpio(desc); + return desc - &gpio_desc[0]; } +EXPORT_SYMBOL_GPL(desc_to_gpio); /* Warn when drivers omit gpio_request() calls -- legal but ill-advised @@ -172,16 +198,15 @@ return 0; } -static struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) +/** + * gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs + * @desc: descriptor to return the chip of + */ +struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { return desc ? desc->chip : NULL; } - -/* caller holds gpio_lock *OR* gpio is marked as requested */ -struct gpio_chip *gpio_to_chip(unsigned gpio) -{ - return gpiod_to_chip(gpio_to_desc(gpio)); -} +EXPORT_SYMBOL_GPL(gpiod_to_chip); /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ static int gpiochip_find_base(int ngpio) @@ -207,8 +232,15 @@ } } -/* caller ensures gpio is valid and requested, chip->get_direction may sleep */ -static int gpiod_get_direction(const struct gpio_desc *desc) +/** + * gpiod_get_direction - return the current direction of a GPIO + * @desc: GPIO to get the direction of + * + * Return GPIOF_DIR_IN or GPIOF_DIR_OUT, or an error code in case of error. + * + * This function may sleep if gpiod_cansleep() is true. + */ +int gpiod_get_direction(const struct gpio_desc *desc) { struct gpio_chip *chip; unsigned offset; @@ -234,6 +266,7 @@ } return status; } +EXPORT_SYMBOL_GPL(gpiod_get_direction); #ifdef CONFIG_GPIO_SYSFS @@ -318,17 +351,10 @@ mutex_lock(&sysfs_lock); - if (!test_bit(FLAG_EXPORT, &desc->flags)) { + if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; - } else { - int value; - - value = !!gpiod_get_value_cansleep(desc); - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) - value = !value; - - status = sprintf(buf, "%d\n", value); - } + else + status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc)); mutex_unlock(&sysfs_lock); return status; @@ -349,11 +375,9 @@ else { long value; - status = strict_strtol(buf, 0, &value); + status = kstrtol(buf, 0, &value); if (status == 0) { - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) - value = !value; - gpiod_set_value_cansleep(desc, value != 0); + gpiod_set_value_cansleep(desc, value); status = size; } } @@ -395,6 +419,7 @@ desc->flags &= ~GPIO_TRIGGER_MASK; if (!gpio_flags) { + gpiod_unlock_as_irq(desc); ret = 0; goto free_id; } @@ -433,6 +458,12 @@ if (ret < 0) goto free_id; + ret = gpiod_lock_as_irq(desc); + if (ret < 0) { + gpiod_warn(desc, "failed to flag the GPIO for IRQ\n"); + goto free_id; + } + desc->flags |= gpio_flags; return 0; @@ -570,7 +601,7 @@ } else { long value; - status = strict_strtol(buf, 0, &value); + status = kstrtol(buf, 0, &value); if (status == 0) status = sysfs_set_active_low(desc, dev, value != 0); } @@ -652,7 +683,7 @@ struct gpio_desc *desc; int status; - status = strict_strtol(buf, 0, &gpio); + status = kstrtol(buf, 0, &gpio); if (status < 0) goto done; @@ -694,7 +725,7 @@ struct gpio_desc *desc; int status; - status = strict_strtol(buf, 0, &gpio); + status = kstrtol(buf, 0, &gpio); if (status < 0) goto done; @@ -736,7 +767,7 @@ /** - * gpio_export - export a GPIO through sysfs + * gpiod_export - export a GPIO through sysfs * @gpio: gpio to make available, already requested * @direction_may_change: true if userspace may change gpio direction * Context: arch_initcall or later @@ -750,7 +781,7 @@ * * Returns zero on success, else an error. */ -static int gpiod_export(struct gpio_desc *desc, bool direction_may_change) +int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { unsigned long flags; int status; @@ -828,12 +859,7 @@ status); return status; } - -int gpio_export(unsigned gpio, bool direction_may_change) -{ - return gpiod_export(gpio_to_desc(gpio), direction_may_change); -} -EXPORT_SYMBOL_GPL(gpio_export); +EXPORT_SYMBOL_GPL(gpiod_export); static int match_export(struct device *dev, const void *data) { @@ -841,7 +867,7 @@ } /** - * gpio_export_link - create a sysfs link to an exported GPIO node + * gpiod_export_link - create a sysfs link to an exported GPIO node * @dev: device under which to create symlink * @name: name of the symlink * @gpio: gpio to create symlink to, already exported @@ -851,8 +877,8 @@ * * Returns zero on success, else an error. */ -static int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc) +int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc) { int status = -EINVAL; @@ -883,15 +909,10 @@ return status; } - -int gpio_export_link(struct device *dev, const char *name, unsigned gpio) -{ - return gpiod_export_link(dev, name, gpio_to_desc(gpio)); -} -EXPORT_SYMBOL_GPL(gpio_export_link); +EXPORT_SYMBOL_GPL(gpiod_export_link); /** - * gpio_sysfs_set_active_low - set the polarity of gpio sysfs value + * gpiod_sysfs_set_active_low - set the polarity of gpio sysfs value * @gpio: gpio to change * @value: non-zero to use active low, i.e. inverted values * @@ -902,7 +923,7 @@ * * Returns zero on success, else an error. */ -static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) +int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) { struct device *dev = NULL; int status = -EINVAL; @@ -933,20 +954,15 @@ return status; } - -int gpio_sysfs_set_active_low(unsigned gpio, int value) -{ - return gpiod_sysfs_set_active_low(gpio_to_desc(gpio), value); -} -EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low); +EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low); /** - * gpio_unexport - reverse effect of gpio_export() + * gpiod_unexport - reverse effect of gpio_export() * @gpio: gpio to make unavailable * * This is implicit on gpio_free(). */ -static void gpiod_unexport(struct gpio_desc *desc) +void gpiod_unexport(struct gpio_desc *desc) { int status = 0; struct device *dev = NULL; @@ -979,12 +995,7 @@ pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), status); } - -void gpio_unexport(unsigned gpio) -{ - gpiod_unexport(gpio_to_desc(gpio)); -} -EXPORT_SYMBOL_GPL(gpio_unexport); +EXPORT_SYMBOL_GPL(gpiod_unexport); static int gpiochip_export(struct gpio_chip *chip) { @@ -1091,27 +1102,6 @@ { } -static inline int gpiod_export(struct gpio_desc *desc, - bool direction_may_change) -{ - return -ENOSYS; -} - -static inline int gpiod_export_link(struct device *dev, const char *name, - struct gpio_desc *desc) -{ - return -ENOSYS; -} - -static inline int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) -{ - return -ENOSYS; -} - -static inline void gpiod_unexport(struct gpio_desc *desc) -{ -} - #endif /* CONFIG_GPIO_SYSFS */ /* @@ -1214,15 +1204,14 @@ } } + spin_unlock_irqrestore(&gpio_lock, flags); + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&chip->pin_ranges); #endif of_gpiochip_add(chip); -unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - if (status) goto fail; @@ -1235,6 +1224,9 @@ chip->label ? : "generic"); return 0; + +unlock: + spin_unlock_irqrestore(&gpio_lock, flags); fail: /* failures here can mean systems won't boot... */ pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n", @@ -1315,6 +1307,18 @@ } EXPORT_SYMBOL_GPL(gpiochip_find); +static int gpiochip_match_name(struct gpio_chip *chip, void *data) +{ + const char *name = data; + + return !strcmp(chip->label, name); +} + +static struct gpio_chip *find_chip_by_name(const char *name) +{ + return gpiochip_find((void *)name, gpiochip_match_name); +} + #ifdef CONFIG_PINCTRL /** @@ -1621,23 +1625,37 @@ * rely on gpio_request() having been called beforehand. */ -static int gpiod_direction_input(struct gpio_desc *desc) +/** + * gpiod_direction_input - set the GPIO direction to input + * @desc: GPIO to set to input + * + * Set the direction of the passed GPIO to input, such as gpiod_get_value() can + * be called safely on it. + * + * Return 0 in case of success, else an error code. + */ +int gpiod_direction_input(struct gpio_desc *desc) { unsigned long flags; struct gpio_chip *chip; int status = -EINVAL; int offset; - if (!desc) { + if (!desc || !desc->chip) { pr_warn("%s: invalid GPIO\n", __func__); return -EINVAL; } + chip = desc->chip; + if (!chip->get || !chip->direction_input) { + gpiod_warn(desc, + "%s: missing get() or direction_input() operations\n", + __func__); + return -EIO; + } + spin_lock_irqsave(&gpio_lock, flags); - chip = desc->chip; - if (!chip || !chip->get || !chip->direction_input) - goto fail; status = gpio_ensure_requested(desc); if (status < 0) goto fail; @@ -1652,8 +1670,7 @@ if (status) { status = chip->request(chip, offset); if (status < 0) { - pr_debug("GPIO-%d: chip request fail, %d\n", - desc_to_gpio(desc), status); + gpiod_dbg(desc, "chip request fail, %d\n", status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ @@ -1671,29 +1688,41 @@ fail: spin_unlock_irqrestore(&gpio_lock, flags); if (status) - pr_debug("%s: gpio-%d status %d\n", __func__, - desc_to_gpio(desc), status); + gpiod_dbg(desc, "%s status %d\n", __func__, status); return status; } +EXPORT_SYMBOL_GPL(gpiod_direction_input); -int gpio_direction_input(unsigned gpio) -{ - return gpiod_direction_input(gpio_to_desc(gpio)); -} -EXPORT_SYMBOL_GPL(gpio_direction_input); - -static int gpiod_direction_output(struct gpio_desc *desc, int value) +/** + * gpiod_direction_output - set the GPIO direction to input + * @desc: GPIO to set to output + * @value: initial output value of the GPIO + * + * Set the direction of the passed GPIO to output, such as gpiod_set_value() can + * be called safely on it. The initial value of the output must be specified. + * + * Return 0 in case of success, else an error code. + */ +int gpiod_direction_output(struct gpio_desc *desc, int value) { unsigned long flags; struct gpio_chip *chip; int status = -EINVAL; int offset; - if (!desc) { + if (!desc || !desc->chip) { pr_warn("%s: invalid GPIO\n", __func__); return -EINVAL; } + /* GPIOs used for IRQs shall not be set as output */ + if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) { + gpiod_err(desc, + "%s: tried to set a GPIO tied to an IRQ as output\n", + __func__); + return -EIO; + } + /* Open drain pin should not be driven to 1 */ if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags)) return gpiod_direction_input(desc); @@ -1702,11 +1731,16 @@ if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags)) return gpiod_direction_input(desc); + chip = desc->chip; + if (!chip->set || !chip->direction_output) { + gpiod_warn(desc, + "%s: missing set() or direction_output() operations\n", + __func__); + return -EIO; + } + spin_lock_irqsave(&gpio_lock, flags); - chip = desc->chip; - if (!chip || !chip->set || !chip->direction_output) - goto fail; status = gpio_ensure_requested(desc); if (status < 0) goto fail; @@ -1721,8 +1755,7 @@ if (status) { status = chip->request(chip, offset); if (status < 0) { - pr_debug("GPIO-%d: chip request fail, %d\n", - desc_to_gpio(desc), status); + gpiod_dbg(desc, "chip request fail, %d\n", status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ @@ -1740,39 +1773,40 @@ fail: spin_unlock_irqrestore(&gpio_lock, flags); if (status) - pr_debug("%s: gpio-%d status %d\n", __func__, - desc_to_gpio(desc), status); + gpiod_dbg(desc, "%s: gpio status %d\n", __func__, status); return status; } - -int gpio_direction_output(unsigned gpio, int value) -{ - return gpiod_direction_output(gpio_to_desc(gpio), value); -} -EXPORT_SYMBOL_GPL(gpio_direction_output); +EXPORT_SYMBOL_GPL(gpiod_direction_output); /** - * gpio_set_debounce - sets @debounce time for a @gpio + * gpiod_set_debounce - sets @debounce time for a @gpio * @gpio: the gpio to set debounce time * @debounce: debounce time is microseconds + * + * returns -ENOTSUPP if the controller does not support setting + * debounce. */ -static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long flags; struct gpio_chip *chip; int status = -EINVAL; int offset; - if (!desc) { + if (!desc || !desc->chip) { pr_warn("%s: invalid GPIO\n", __func__); return -EINVAL; } - spin_lock_irqsave(&gpio_lock, flags); - chip = desc->chip; - if (!chip || !chip->set || !chip->set_debounce) - goto fail; + if (!chip->set || !chip->set_debounce) { + gpiod_dbg(desc, + "%s: missing set() or set_debounce() operations\n", + __func__); + return -ENOTSUPP; + } + + spin_lock_irqsave(&gpio_lock, flags); status = gpio_ensure_requested(desc); if (status < 0) @@ -1790,17 +1824,23 @@ fail: spin_unlock_irqrestore(&gpio_lock, flags); if (status) - pr_debug("%s: gpio-%d status %d\n", __func__, - desc_to_gpio(desc), status); + gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } +EXPORT_SYMBOL_GPL(gpiod_set_debounce); -int gpio_set_debounce(unsigned gpio, unsigned debounce) +/** + * gpiod_is_active_low - test whether a GPIO is active-low or not + * @desc: the gpio descriptor to test + * + * Returns 1 if the GPIO is active-low, 0 otherwise. + */ +int gpiod_is_active_low(const struct gpio_desc *desc) { - return gpiod_set_debounce(gpio_to_desc(gpio), debounce); + return test_bit(FLAG_ACTIVE_LOW, &desc->flags); } -EXPORT_SYMBOL_GPL(gpio_set_debounce); +EXPORT_SYMBOL_GPL(gpiod_is_active_low); /* I/O calls are only valid after configuration completed; the relevant * "is this a valid GPIO" error checks should already have been done. @@ -1824,42 +1864,68 @@ * that the GPIO was actually requested. */ -/** - * __gpio_get_value() - return a gpio's value - * @gpio: gpio whose value will be returned - * Context: any - * - * This is used directly or indirectly to implement gpio_get_value(). - * It returns the zero or nonzero value provided by the associated - * gpio_chip.get() method; or zero if no such method is provided. - */ -static int gpiod_get_value(const struct gpio_desc *desc) +static int _gpiod_get_raw_value(const struct gpio_desc *desc) { struct gpio_chip *chip; int value; int offset; - if (!desc) - return 0; chip = desc->chip; offset = gpio_chip_hwgpio(desc); - /* Should be using gpio_get_value_cansleep() */ - WARN_ON(chip->can_sleep); value = chip->get ? chip->get(chip, offset) : 0; trace_gpio_value(desc_to_gpio(desc), 1, value); return value; } -int __gpio_get_value(unsigned gpio) +/** + * gpiod_get_raw_value() - return a gpio's raw value + * @desc: gpio whose value will be returned + * + * Return the GPIO's raw value, i.e. the value of the physical line disregarding + * its ACTIVE_LOW status. + * + * This function should be called from contexts where we cannot sleep, and will + * complain if the GPIO chip functions potentially sleep. + */ +int gpiod_get_raw_value(const struct gpio_desc *desc) { - return gpiod_get_value(gpio_to_desc(gpio)); + if (!desc) + return 0; + /* Should be using gpio_get_value_cansleep() */ + WARN_ON(desc->chip->can_sleep); + return _gpiod_get_raw_value(desc); +} +EXPORT_SYMBOL_GPL(gpiod_get_raw_value); + +/** + * gpiod_get_value() - return a gpio's value + * @desc: gpio whose value will be returned + * + * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into + * account. + * + * This function should be called from contexts where we cannot sleep, and will + * complain if the GPIO chip functions potentially sleep. + */ +int gpiod_get_value(const struct gpio_desc *desc) +{ + int value; + if (!desc) + return 0; + /* Should be using gpio_get_value_cansleep() */ + WARN_ON(desc->chip->can_sleep); + + value = _gpiod_get_raw_value(desc); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + + return value; } -EXPORT_SYMBOL_GPL(__gpio_get_value); +EXPORT_SYMBOL_GPL(gpiod_get_value); /* * _gpio_set_open_drain_value() - Set the open drain gpio's value. - * @gpio: Gpio whose state need to be set. - * @chip: Gpio chip. + * @desc: gpio descriptor whose state need to be set. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */ static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value) @@ -1879,14 +1945,14 @@ } trace_gpio_direction(desc_to_gpio(desc), value, err); if (err < 0) - pr_err("%s: Error in set_value for open drain gpio%d err %d\n", - __func__, desc_to_gpio(desc), err); + gpiod_err(desc, + "%s: Error in set_value for open drain err %d\n", + __func__, err); } /* - * _gpio_set_open_source() - Set the open source gpio's value. - * @gpio: Gpio whose state need to be set. - * @chip: Gpio chip. + * _gpio_set_open_source_value() - Set the open source gpio's value. + * @desc: gpio descriptor whose state need to be set. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */ static void _gpio_set_open_source_value(struct gpio_desc *desc, int value) @@ -1906,28 +1972,16 @@ } trace_gpio_direction(desc_to_gpio(desc), !value, err); if (err < 0) - pr_err("%s: Error in set_value for open source gpio%d err %d\n", - __func__, desc_to_gpio(desc), err); + gpiod_err(desc, + "%s: Error in set_value for open source err %d\n", + __func__, err); } -/** - * __gpio_set_value() - assign a gpio's value - * @gpio: gpio whose value will be assigned - * @value: value to assign - * Context: any - * - * This is used directly or indirectly to implement gpio_set_value(). - * It invokes the associated gpio_chip.set() method. - */ -static void gpiod_set_value(struct gpio_desc *desc, int value) +static void _gpiod_set_raw_value(struct gpio_desc *desc, int value) { struct gpio_chip *chip; - if (!desc) - return; chip = desc->chip; - /* Should be using gpio_set_value_cansleep() */ - WARN_ON(chip->can_sleep); trace_gpio_value(desc_to_gpio(desc), 0, value); if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) _gpio_set_open_drain_value(desc, value); @@ -1937,44 +1991,71 @@ chip->set(chip, gpio_chip_hwgpio(desc), value); } -void __gpio_set_value(unsigned gpio, int value) +/** + * gpiod_set_raw_value() - assign a gpio's raw value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the raw value of the GPIO, i.e. the value of its physical line without + * regard for its ACTIVE_LOW status. + * + * This function should be called from contexts where we cannot sleep, and will + * complain if the GPIO chip functions potentially sleep. + */ +void gpiod_set_raw_value(struct gpio_desc *desc, int value) { - return gpiod_set_value(gpio_to_desc(gpio), value); + if (!desc) + return; + /* Should be using gpio_set_value_cansleep() */ + WARN_ON(desc->chip->can_sleep); + _gpiod_set_raw_value(desc, value); } -EXPORT_SYMBOL_GPL(__gpio_set_value); +EXPORT_SYMBOL_GPL(gpiod_set_raw_value); /** - * __gpio_cansleep() - report whether gpio value access will sleep - * @gpio: gpio in question - * Context: any + * gpiod_set_value() - assign a gpio's value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into + * account * - * This is used directly or indirectly to implement gpio_cansleep(). It - * returns nonzero if access reading or writing the GPIO value can sleep. + * This function should be called from contexts where we cannot sleep, and will + * complain if the GPIO chip functions potentially sleep. */ -static int gpiod_cansleep(const struct gpio_desc *desc) +void gpiod_set_value(struct gpio_desc *desc, int value) { if (!desc) - return 0; - /* only call this on GPIOs that are valid! */ - return desc->chip->can_sleep; + return; + /* Should be using gpio_set_value_cansleep() */ + WARN_ON(desc->chip->can_sleep); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + _gpiod_set_raw_value(desc, value); } +EXPORT_SYMBOL_GPL(gpiod_set_value); -int __gpio_cansleep(unsigned gpio) +/** + * gpiod_cansleep() - report whether gpio value access may sleep + * @desc: gpio to check + * + */ +int gpiod_cansleep(const struct gpio_desc *desc) { - return gpiod_cansleep(gpio_to_desc(gpio)); + if (!desc) + return 0; + return desc->chip->can_sleep; } -EXPORT_SYMBOL_GPL(__gpio_cansleep); +EXPORT_SYMBOL_GPL(gpiod_cansleep); /** - * __gpio_to_irq() - return the IRQ corresponding to a GPIO - * @gpio: gpio whose IRQ will be returned (already requested) - * Context: any + * gpiod_to_irq() - return the IRQ corresponding to a GPIO + * @desc: gpio whose IRQ will be returned (already requested) * - * This is used directly or indirectly to implement gpio_to_irq(). - * It returns the number of the IRQ signaled by this (input) GPIO, - * or a negative errno. + * Return the IRQ corresponding to the passed GPIO, or an error code in case of + * error. */ -static int gpiod_to_irq(const struct gpio_desc *desc) +int gpiod_to_irq(const struct gpio_desc *desc) { struct gpio_chip *chip; int offset; @@ -1985,62 +2066,336 @@ offset = gpio_chip_hwgpio(desc); return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO; } +EXPORT_SYMBOL_GPL(gpiod_to_irq); -int __gpio_to_irq(unsigned gpio) +/** + * gpiod_lock_as_irq() - lock a GPIO to be used as IRQ + * @gpio: the GPIO line to lock as used for IRQ + * + * This is used directly by GPIO drivers that want to lock down + * a certain GPIO line to be used as IRQs, for example in the + * .to_irq() callback of their gpio_chip, or in the .irq_enable() + * of its irq_chip implementation if the GPIO is known from that + * code. + */ +int gpiod_lock_as_irq(struct gpio_desc *desc) { - return gpiod_to_irq(gpio_to_desc(gpio)); + if (!desc) + return -EINVAL; + + if (test_bit(FLAG_IS_OUT, &desc->flags)) { + gpiod_err(desc, + "%s: tried to flag a GPIO set as output for IRQ\n", + __func__); + return -EIO; + } + + set_bit(FLAG_USED_AS_IRQ, &desc->flags); + return 0; } -EXPORT_SYMBOL_GPL(__gpio_to_irq); +EXPORT_SYMBOL_GPL(gpiod_lock_as_irq); +int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset) +{ + return gpiod_lock_as_irq(gpiochip_offset_to_desc(chip, offset)); +} +EXPORT_SYMBOL_GPL(gpio_lock_as_irq); -/* There's no value in making it easy to inline GPIO calls that may sleep. - * Common examples include ones connected to I2C or SPI chips. +/** + * gpiod_unlock_as_irq() - unlock a GPIO used as IRQ + * @gpio: the GPIO line to unlock from IRQ usage + * + * This is used directly by GPIO drivers that want to indicate + * that a certain GPIO is no longer used exclusively for IRQ. */ +void gpiod_unlock_as_irq(struct gpio_desc *desc) +{ + if (!desc) + return; -static int gpiod_get_value_cansleep(const struct gpio_desc *desc) + clear_bit(FLAG_USED_AS_IRQ, &desc->flags); +} +EXPORT_SYMBOL_GPL(gpiod_unlock_as_irq); + +void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) +{ + return gpiod_unlock_as_irq(gpiochip_offset_to_desc(chip, offset)); +} +EXPORT_SYMBOL_GPL(gpio_unlock_as_irq); + +/** + * gpiod_get_raw_value_cansleep() - return a gpio's raw value + * @desc: gpio whose value will be returned + * + * Return the GPIO's raw value, i.e. the value of the physical line disregarding + * its ACTIVE_LOW status. + * + * This function is to be called from contexts that can sleep. + */ +int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) +{ + might_sleep_if(extra_checks); + if (!desc) + return 0; + return _gpiod_get_raw_value(desc); +} +EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep); + +/** + * gpiod_get_value_cansleep() - return a gpio's value + * @desc: gpio whose value will be returned + * + * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into + * account. + * + * This function is to be called from contexts that can sleep. + */ +int gpiod_get_value_cansleep(const struct gpio_desc *desc) { - struct gpio_chip *chip; int value; - int offset; might_sleep_if(extra_checks); if (!desc) return 0; - chip = desc->chip; - offset = gpio_chip_hwgpio(desc); - value = chip->get ? chip->get(chip, offset) : 0; - trace_gpio_value(desc_to_gpio(desc), 1, value); + + value = _gpiod_get_raw_value(desc); + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + return value; } +EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep); -int gpio_get_value_cansleep(unsigned gpio) +/** + * gpiod_set_raw_value_cansleep() - assign a gpio's raw value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the raw value of the GPIO, i.e. the value of its physical line without + * regard for its ACTIVE_LOW status. + * + * This function is to be called from contexts that can sleep. + */ +void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { - return gpiod_get_value_cansleep(gpio_to_desc(gpio)); + might_sleep_if(extra_checks); + if (!desc) + return; + _gpiod_set_raw_value(desc, value); } -EXPORT_SYMBOL_GPL(gpio_get_value_cansleep); +EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep); -static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) +/** + * gpiod_set_value_cansleep() - assign a gpio's value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into + * account + * + * This function is to be called from contexts that can sleep. + */ +void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { - struct gpio_chip *chip; - might_sleep_if(extra_checks); if (!desc) return; - chip = desc->chip; - trace_gpio_value(desc_to_gpio(desc), 0, value); - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - _gpio_set_open_drain_value(desc, value); - else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - _gpio_set_open_source_value(desc, value); + + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + _gpiod_set_raw_value(desc, value); +} +EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep); + +/** + * gpiod_add_table() - register GPIO device consumers + * @table: array of consumers to register + * @num: number of consumers in table + */ +void gpiod_add_table(struct gpiod_lookup *table, size_t size) +{ + mutex_lock(&gpio_lookup_lock); + + while (size--) { + list_add_tail(&table->list, &gpio_lookup_list); + table++; + } + + mutex_unlock(&gpio_lookup_lock); +} + +#ifdef CONFIG_OF +static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, + enum gpio_lookup_flags *flags) +{ + char prop_name[32]; /* 32 is max size of property name */ + enum of_gpio_flags of_flags; + struct gpio_desc *desc; + + if (con_id) + snprintf(prop_name, 32, "%s-gpios", con_id); else - chip->set(chip, gpio_chip_hwgpio(desc), value); + snprintf(prop_name, 32, "gpios"); + + desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, + &of_flags); + + if (IS_ERR(desc)) + return desc; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + *flags |= GPIO_ACTIVE_LOW; + + return desc; +} +#else +static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, unsigned long *flags) +{ + return ERR_PTR(-ENODEV); +} +#endif + +static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, + unsigned int idx, + enum gpio_lookup_flags *flags) +{ + const char *dev_id = dev ? dev_name(dev) : NULL; + struct gpio_desc *desc = ERR_PTR(-ENODEV); + unsigned int match, best = 0; + struct gpiod_lookup *p; + + mutex_lock(&gpio_lookup_lock); + + list_for_each_entry(p, &gpio_lookup_list, list) { + match = 0; + + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; + + match += 2; + } + + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; + + match += 1; + } + + if (p->idx != idx) + continue; + + if (match > best) { + struct gpio_chip *chip; + + chip = find_chip_by_name(p->chip_label); + + if (!chip) { + dev_warn(dev, "cannot find GPIO chip %s\n", + p->chip_label); + continue; + } + + if (chip->ngpio >= p->chip_hwnum) { + dev_warn(dev, "GPIO chip %s has %d GPIOs\n", + chip->label, chip->ngpio); + continue; + } + + desc = gpio_to_desc(chip->base + p->chip_hwnum); + *flags = p->flags; + + if (match != 3) + best = match; + else + break; + } + } + + mutex_unlock(&gpio_lookup_lock); + + return desc; +} + +/** + * gpio_get - obtain a GPIO for a given GPIO function + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * + * Return the GPIO descriptor corresponding to the function con_id of device + * dev, or an IS_ERR() condition if an error occured. + */ +struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id) +{ + return gpiod_get_index(dev, con_id, 0); +} +EXPORT_SYMBOL_GPL(gpiod_get); + +/** + * gpiod_get_index - obtain a GPIO from a multi-index GPIO function + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * + * This variant of gpiod_get() allows to access GPIOs other than the first + * defined one for functions that define several GPIOs. + * + * Return a valid GPIO descriptor, or an IS_ERR() condition in case of error. + */ +struct gpio_desc *__must_check gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx) +{ + struct gpio_desc *desc; + int status; + enum gpio_lookup_flags flags = 0; + + dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id); + + /* Using device tree? */ + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) { + dev_dbg(dev, "using device tree for GPIO lookup\n"); + desc = of_find_gpio(dev, con_id, idx, &flags); + } else { + dev_dbg(dev, "using lookup tables for GPIO lookup"); + desc = gpiod_find(dev, con_id, idx, &flags); + } + + if (IS_ERR(desc)) { + dev_warn(dev, "lookup for GPIO %s failed\n", con_id); + return desc; + } + + status = gpiod_request(desc, con_id); + + if (status < 0) + return ERR_PTR(status); + + if (flags & GPIO_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (flags & GPIO_OPEN_DRAIN) + set_bit(FLAG_OPEN_DRAIN, &desc->flags); + if (flags & GPIO_OPEN_SOURCE) + set_bit(FLAG_OPEN_SOURCE, &desc->flags); + + return desc; } +EXPORT_SYMBOL_GPL(gpiod_get_index); -void gpio_set_value_cansleep(unsigned gpio, int value) +/** + * gpiod_put - dispose of a GPIO descriptor + * @desc: GPIO descriptor to dispose of + * + * No descriptor can be used after gpiod_put() has been called on it. + */ +void gpiod_put(struct gpio_desc *desc) { - return gpiod_set_value_cansleep(gpio_to_desc(gpio), value); + gpiod_free(desc); } -EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); +EXPORT_SYMBOL_GPL(gpiod_put); #ifdef CONFIG_DEBUG_FS @@ -2050,6 +2405,7 @@ unsigned gpio = chip->base; struct gpio_desc *gdesc = &chip->desc[0]; int is_out; + int is_irq; for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) { if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) @@ -2057,12 +2413,14 @@ gpiod_get_direction(gdesc); is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); - seq_printf(s, " gpio-%-3d (%-20.20s) %s %s", + is_irq = test_bit(FLAG_USED_AS_IRQ, &gdesc->flags); + seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s", gpio, gdesc->label, is_out ? "out" : "in ", chip->get ? (chip->get(chip, i) ? "hi" : "lo") - : "? "); + : "? ", + is_irq ? "IRQ" : " "); seq_printf(s, "\n"); } } diff -Nur linux-3.10.30/drivers/gpu/drm/Kconfig linux-3.10.30-cubox-i/drivers/gpu/drm/Kconfig --- linux-3.10.30/drivers/gpu/drm/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpu/drm/Kconfig 2014-03-08 20:33:32.000000000 +0100 @@ -199,6 +199,13 @@ Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister chipset. If M is selected the module will be called savage. +config DRM_VIVANTE + tristate "Vivante GCCore" + depends on DRM + help + Choose this option if you have a Vivante graphics card. + If M is selected, the module will be called vivante. + source "drivers/gpu/drm/exynos/Kconfig" source "drivers/gpu/drm/vmwgfx/Kconfig" diff -Nur linux-3.10.30/drivers/gpu/drm/Makefile linux-3.10.30-cubox-i/drivers/gpu/drm/Makefile --- linux-3.10.30/drivers/gpu/drm/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpu/drm/Makefile 2014-03-08 20:33:32.000000000 +0100 @@ -1,3 +1,24 @@ +############################################################################## +# +# Copyright (C) 2005 - 2013 by Vivante Corp. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the license, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +############################################################################## + + # # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. @@ -29,8 +50,8 @@ CFLAGS_drm_trace_points.o := -I$(src) obj-$(CONFIG_DRM) += drm.o -obj-$(CONFIG_DRM_USB) += drm_usb.o obj-$(CONFIG_DRM_TTM) += ttm/ +obj-$(CONFIG_DRM_VIVANTE) += vivante/ obj-$(CONFIG_DRM_TDFX) += tdfx/ obj-$(CONFIG_DRM_R128) += r128/ obj-$(CONFIG_DRM_RADEON)+= radeon/ diff -Nur linux-3.10.30/drivers/gpu/drm/drm_crtc_helper.c linux-3.10.30-cubox-i/drivers/gpu/drm/drm_crtc_helper.c --- linux-3.10.30/drivers/gpu/drm/drm_crtc_helper.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpu/drm/drm_crtc_helper.c 2014-03-08 20:33:32.000000000 +0100 @@ -308,6 +308,29 @@ EXPORT_SYMBOL(drm_helper_disable_unused_functions); /** + * drm_helper_crtc_possible_mask - find the mask of a registered CRTC + * @crtc: crtc to find mask for + * + * Given a registered CRTC, return the mask bit of that CRTC for an + * encoder's possible_crtcs field. + */ +uint32_t drm_helper_crtc_possible_mask(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_crtc *tmp; + uint32_t crtc_mask = 1; + + list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { + if (tmp == crtc) + return crtc_mask; + crtc_mask <<= 1; + } + + return 0; +} +EXPORT_SYMBOL(drm_helper_crtc_possible_mask); + +/** * drm_encoder_crtc_ok - can a given crtc drive a given encoder? * @encoder: encoder to test * @crtc: crtc to test @@ -317,23 +340,13 @@ static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, struct drm_crtc *crtc) { - struct drm_device *dev; - struct drm_crtc *tmp; - int crtc_mask = 1; + uint32_t crtc_mask; WARN(!crtc, "checking null crtc?\n"); - dev = crtc->dev; + crtc_mask = drm_helper_crtc_possible_mask(crtc); - list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { - if (tmp == crtc) - break; - crtc_mask <<= 1; - } - - if (encoder->possible_crtcs & crtc_mask) - return true; - return false; + return !!(encoder->possible_crtcs & crtc_mask); } /* diff -Nur linux-3.10.30/drivers/gpu/drm/vivante/Makefile linux-3.10.30-cubox-i/drivers/gpu/drm/vivante/Makefile --- linux-3.10.30/drivers/gpu/drm/vivante/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpu/drm/vivante/Makefile 2014-03-08 20:33:35.000000000 +0100 @@ -0,0 +1,29 @@ +############################################################################## +# +# Copyright (C) 2005 - 2013 by Vivante Corp. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the license, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +############################################################################## + + +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm +vivante-y := vivante_drv.o + +obj-$(CONFIG_DRM_VIVANTE) += vivante.o diff -Nur linux-3.10.30/drivers/gpu/drm/vivante/vivante_drv.c linux-3.10.30-cubox-i/drivers/gpu/drm/vivante/vivante_drv.c --- linux-3.10.30/drivers/gpu/drm/vivante/vivante_drv.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpu/drm/vivante/vivante_drv.c 2014-03-08 20:33:35.000000000 +0100 @@ -0,0 +1,111 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2013 by Vivante Corp. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the license, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +/* vivante_drv.c -- vivante driver -*- linux-c -*- + * + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * Gareth Hughes + */ + +#include +#include + +#include "drmP.h" +#include "vivante_drv.h" + +#include "drm_pciids.h" + +static char platformdevicename[] = "Vivante GCCore"; +static struct platform_device *pplatformdev; + +static const struct file_operations viv_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .llseek = noop_llseek, +}; + +static struct drm_driver driver = { + .driver_features = DRIVER_USE_MTRR, + .fops = &viv_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +static int __init vivante_init(void) +{ + int retcode; + + pplatformdev = platform_device_register_simple(platformdevicename, + -1, NULL, 0); + if (pplatformdev == NULL) + printk(KERN_ERR"Platform device is null\n"); + + retcode = drm_platform_init(&driver, pplatformdev); + + return retcode; +} + +static void __exit vivante_exit(void) +{ + if (pplatformdev) { + drm_platform_exit(&driver, pplatformdev); + platform_device_unregister(pplatformdev); + pplatformdev = NULL; + } +} + +module_init(vivante_init); +module_exit(vivante_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff -Nur linux-3.10.30/drivers/gpu/drm/vivante/vivante_drv.h linux-3.10.30-cubox-i/drivers/gpu/drm/vivante/vivante_drv.h --- linux-3.10.30/drivers/gpu/drm/vivante/vivante_drv.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/gpu/drm/vivante/vivante_drv.h 2014-03-08 20:33:35.000000000 +0100 @@ -0,0 +1,66 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2013 by Vivante Corp. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the license, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +/* vivante_drv.h -- Vivante DRM template customization -*- linux-c -*- + * Created: Wed Feb 14 12:32:32 2012 by John Zhao + */ +/* + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + */ + +#ifndef __VIVANTE_DRV_H__ +#define __VIVANTE_DRV_H__ + +/* General customization: + */ + +#define DRIVER_AUTHOR "Vivante Inc." + +#define DRIVER_NAME "vivante" +#define DRIVER_DESC "Vivante GCCore" +#define DRIVER_DATE "20120216" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#endif diff -Nur linux-3.10.30/drivers/hwmon/Kconfig linux-3.10.30-cubox-i/drivers/hwmon/Kconfig --- linux-3.10.30/drivers/hwmon/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/hwmon/Kconfig 2014-03-08 20:33:35.000000000 +0100 @@ -867,6 +867,15 @@ This driver can also be built as a module. If so, the module will be called max1668. +config SENSORS_MAX17135 + tristate "Maxim MAX17135 EPD temperature sensor" + depends on I2C + help + If you say yes here you get support for MAX17135 PMIC sensor. + + This driver can also be built as a module. If so, the module + will be called max17135_sensor. + config SENSORS_MAX197 tristate "Maxim MAX197 and compatibles" help @@ -1556,4 +1565,18 @@ endif # ACPI +config SENSORS_MAG3110 + tristate "Freescale MAG3110 e-compass sensor" + depends on I2C && SYSFS + help + If you say yes here you get support for the Freescale MAG3110 + e-compass sensor. + This driver can also be built as a module. If so, the module + will be called mag3110. + +config MXC_MMA8451 + tristate "MMA8451 device driver" + depends on I2C + default y + endif # HWMON diff -Nur linux-3.10.30/drivers/hwmon/Makefile linux-3.10.30-cubox-i/drivers/hwmon/Makefile --- linux-3.10.30/drivers/hwmon/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/hwmon/Makefile 2014-03-08 20:33:35.000000000 +0100 @@ -101,6 +101,7 @@ obj-$(CONFIG_SENSORS_MAX16065) += max16065.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1668) += max1668.o +obj-$(CONFIG_SENSORS_MAX17135) += max17135-hwmon.o obj-$(CONFIG_SENSORS_MAX197) += max197.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o @@ -140,6 +141,8 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o +obj-$(CONFIG_SENSORS_MAG3110) += mag3110.o +obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o obj-$(CONFIG_PMBUS) += pmbus/ diff -Nur linux-3.10.30/drivers/hwmon/mag3110.c linux-3.10.30-cubox-i/drivers/hwmon/mag3110.c --- linux-3.10.30/drivers/hwmon/mag3110.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/hwmon/mag3110.c 2014-03-08 20:33:36.000000000 +0100 @@ -0,0 +1,611 @@ +/* + * + * Copyright (C) 2011-2013 Freescale Semiconductor, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAG3110_DRV_NAME "mag3110" +#define MAG3110_ID 0xC4 +#define MAG3110_XYZ_DATA_LEN 6 +#define MAG3110_STATUS_ZYXDR 0x08 + +#define MAG3110_AC_MASK (0x01) +#define MAG3110_AC_OFFSET 0 +#define MAG3110_DR_MODE_MASK (0x7 << 5) +#define MAG3110_DR_MODE_OFFSET 5 +#define MAG3110_IRQ_USED 0 + +#define POLL_INTERVAL_MAX 500 +#define POLL_INTERVAL 100 +#define INT_TIMEOUT 1000 +#define DEFAULT_POSITION 2 +/* register enum for mag3110 registers */ +enum { + MAG3110_DR_STATUS = 0x00, + MAG3110_OUT_X_MSB, + MAG3110_OUT_X_LSB, + MAG3110_OUT_Y_MSB, + MAG3110_OUT_Y_LSB, + MAG3110_OUT_Z_MSB, + MAG3110_OUT_Z_LSB, + MAG3110_WHO_AM_I, + + MAG3110_OFF_X_MSB, + MAG3110_OFF_X_LSB, + MAG3110_OFF_Y_MSB, + MAG3110_OFF_Y_LSB, + MAG3110_OFF_Z_MSB, + MAG3110_OFF_Z_LSB, + + MAG3110_DIE_TEMP, + + MAG3110_CTRL_REG1 = 0x10, + MAG3110_CTRL_REG2, +}; +enum { + MAG_STANDBY, + MAG_ACTIVED +}; +struct mag3110_data { + struct i2c_client *client; + struct input_polled_dev *poll_dev; + struct device *hwmon_dev; + wait_queue_head_t waitq; + bool data_ready; + u8 ctl_reg1; + int active; + int position; +}; +static short MAGHAL[8][3][3] = { + { {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} }, + { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }, + { {0, -1, 0}, {1, 0, 0}, {0, 0, 1} }, + { {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} }, + + { {0, 1, 0}, {1, 0, 0}, {0, 0, -1} }, + { {1, 0, 0}, {0, -1, 0}, {0, 0, -1} }, + { {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} }, + { {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} }, +}; + +static struct mag3110_data *mag3110_pdata; +/*! + * This function do one mag3110 register read. + */ +static DEFINE_MUTEX(mag3110_lock); +static int mag3110_adjust_position(short *x, short *y, short *z) +{ + short rawdata[3], data[3]; + int i, j; + int position = mag3110_pdata->position; + if (position < 0 || position > 7) + position = 0; + rawdata[0] = *x; + rawdata[1] = *y; + rawdata[2] = *z; + for (i = 0; i < 3; i++) { + data[i] = 0; + for (j = 0; j < 3; j++) + data[i] += rawdata[j] * MAGHAL[position][i][j]; + } + *x = data[0]; + *y = data[1]; + *z = data[2]; + return 0; +} + +static int mag3110_read_reg(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/*! + * This function do one mag3110 register write. + */ +static int mag3110_write_reg(struct i2c_client *client, u8 reg, char value) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, value); + if (ret < 0) + dev_err(&client->dev, "i2c write failed\n"); + return ret; +} + +/*! + * This function do multiple mag3110 registers read. + */ +static int mag3110_read_block_data(struct i2c_client *client, u8 reg, + int count, u8 *addr) +{ + if (i2c_smbus_read_i2c_block_data(client, reg, count, addr) < count) { + dev_err(&client->dev, "i2c block read failed\n"); + return -1; + } + + return count; +} + +/* + * Initialization function + */ +static int mag3110_init_client(struct i2c_client *client) +{ + int val, ret; + + /* enable automatic resets */ + val = 0x80; + ret = mag3110_write_reg(client, MAG3110_CTRL_REG2, val); + + /* set default data rate to 10HZ */ + val = mag3110_read_reg(client, MAG3110_CTRL_REG1); + val |= (0x0 << MAG3110_DR_MODE_OFFSET); + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, val); + + return ret; +} + +/*************************************************************** +* +* read sensor data from mag3110 +* +***************************************************************/ +static int mag3110_read_data(short *x, short *y, short *z) +{ + struct mag3110_data *data; + int retry = 3; + u8 tmp_data[MAG3110_XYZ_DATA_LEN]; + int result; + if (!mag3110_pdata || mag3110_pdata->active == MAG_STANDBY) + return -EINVAL; + + data = mag3110_pdata; +#if MAG3110_IRQ_USED + if (!wait_event_interruptible_timeout + (data->waitq, data->data_ready != 0, + msecs_to_jiffies(INT_TIMEOUT))) { + dev_dbg(&data->client->dev, "interrupt not received\n"); + return -ETIME; + } +#else + do { + msleep(1); + result = i2c_smbus_read_byte_data(data->client, + MAG3110_DR_STATUS); + retry--; + } while (!(result & MAG3110_STATUS_ZYXDR) && retry > 0); + /* Clear data_ready flag after data is read out */ + if (retry == 0) + return -EINVAL; +#endif + + data->data_ready = 0; + + if (mag3110_read_block_data(data->client, + MAG3110_OUT_X_MSB, MAG3110_XYZ_DATA_LEN, + tmp_data) < 0) + return -1; + + *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; + *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; + *z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; + + return 0; +} + +static void report_abs(void) +{ + struct input_dev *idev; + short x, y, z; + + mutex_lock(&mag3110_lock); + if (mag3110_read_data(&x, &y, &z) != 0) + goto out; + mag3110_adjust_position(&x, &y, &z); + idev = mag3110_pdata->poll_dev->input; + input_report_abs(idev, ABS_X, x); + input_report_abs(idev, ABS_Y, y); + input_report_abs(idev, ABS_Z, z); + input_sync(idev); +out: + mutex_unlock(&mag3110_lock); +} + +static void mag3110_dev_poll(struct input_polled_dev *dev) +{ + report_abs(); +} + +#if MAG3110_IRQ_USED +static irqreturn_t mag3110_irq_handler(int irq, void *dev_id) +{ + mag3110_pdata->data_ready = 1; + wake_up_interruptible(&mag3110_pdata->waitq); + + return IRQ_HANDLED; +} +#endif +static ssize_t mag3110_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + int val; + mutex_lock(&mag3110_lock); + client = mag3110_pdata->client; + val = mag3110_read_reg(client, MAG3110_CTRL_REG1) & MAG3110_AC_MASK; + + mutex_unlock(&mag3110_lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t mag3110_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + int reg, ret; + long enable; + u8 tmp_data[MAG3110_XYZ_DATA_LEN]; + + ret = strict_strtol(buf, 10, &enable); + if (ret) { + dev_err(dev, "string to long error\n"); + return ret; + } + + mutex_lock(&mag3110_lock); + client = mag3110_pdata->client; + reg = mag3110_read_reg(client, MAG3110_CTRL_REG1); + if (enable && mag3110_pdata->active == MAG_STANDBY) { + reg |= MAG3110_AC_MASK; + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); + if (!ret) + mag3110_pdata->active = MAG_ACTIVED; + } else if (!enable && mag3110_pdata->active == MAG_ACTIVED) { + reg &= ~MAG3110_AC_MASK; + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); + if (!ret) + mag3110_pdata->active = MAG_STANDBY; + } + + if (mag3110_pdata->active == MAG_ACTIVED) { + msleep(100); + /* Read out MSB data to clear interrupt flag automatically */ + mag3110_read_block_data(client, MAG3110_OUT_X_MSB, + MAG3110_XYZ_DATA_LEN, tmp_data); + } + mutex_unlock(&mag3110_lock); + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + mag3110_enable_show, mag3110_enable_store); + +static ssize_t mag3110_dr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + int val; + + client = mag3110_pdata->client; + val = (mag3110_read_reg(client, MAG3110_CTRL_REG1) + & MAG3110_DR_MODE_MASK) >> MAG3110_DR_MODE_OFFSET; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t mag3110_dr_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + int reg, ret; + unsigned long val; + + /* This must be done when mag3110 is disabled */ + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 7)) + return -EINVAL; + + client = mag3110_pdata->client; + reg = mag3110_read_reg(client, MAG3110_CTRL_REG1) & + ~MAG3110_DR_MODE_MASK; + reg |= (val << MAG3110_DR_MODE_OFFSET); + /* MAG3110_CTRL_REG1 bit 5-7: data rate mode */ + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(dr_mode, S_IWUSR | S_IRUGO, + mag3110_dr_mode_show, mag3110_dr_mode_store); + +static ssize_t mag3110_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + mutex_lock(&mag3110_lock); + val = mag3110_pdata->position; + mutex_unlock(&mag3110_lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t mag3110_position_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + long position; + int ret; + ret = strict_strtol(buf, 10, &position); + if (ret) { + dev_err(dev, "string to long error\n"); + return ret; + } + + mutex_lock(&mag3110_lock); + mag3110_pdata->position = (int)position; + mutex_unlock(&mag3110_lock); + return count; +} + +static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, + mag3110_position_show, mag3110_position_store); + +static struct attribute *mag3110_attributes[] = { + &dev_attr_enable.attr, + &dev_attr_dr_mode.attr, + &dev_attr_position.attr, + NULL +}; + +static const struct attribute_group mag3110_attr_group = { + .attrs = mag3110_attributes, +}; + +static int mag3110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter; + struct input_dev *idev; + struct mag3110_data *data; + int ret = 0; + struct regulator *vdd, *vdd_io; + u32 pos = 0; + struct device_node *of_node = client->dev.of_node; + vdd = NULL; + vdd_io = NULL; + + vdd = devm_regulator_get(&client->dev, "vdd"); + if (!IS_ERR(vdd)) { + ret = regulator_enable(vdd); + if (ret) { + dev_err(&client->dev, "vdd set voltage error\n"); + return ret; + } + } + + vdd_io = devm_regulator_get(&client->dev, "vddio"); + if (!IS_ERR(vdd_io)) { + ret = regulator_enable(vdd_io); + if (ret) { + dev_err(&client->dev, "vddio set voltage error\n"); + return ret; + } + } + + adapter = to_i2c_adapter(client->dev.parent); + if (!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + dev_info(&client->dev, "check mag3110 chip ID\n"); + ret = mag3110_read_reg(client, MAG3110_WHO_AM_I); + + if (MAG3110_ID != ret) { + dev_err(&client->dev, + "read chip ID 0x%x is not equal to 0x%x!\n", ret, + MAG3110_ID); + return -EINVAL; + } + data = kzalloc(sizeof(struct mag3110_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->client = client; + i2c_set_clientdata(client, data); + /* Init queue */ + init_waitqueue_head(&data->waitq); + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + dev_err(&client->dev, "hwmon register failed!\n"); + ret = PTR_ERR(data->hwmon_dev); + goto error_rm_dev_sysfs; + } + + /*input poll device register */ + data->poll_dev = input_allocate_polled_device(); + if (!data->poll_dev) { + dev_err(&client->dev, "alloc poll device failed!\n"); + ret = -ENOMEM; + goto error_rm_hwmon_dev; + } + data->poll_dev->poll = mag3110_dev_poll; + data->poll_dev->poll_interval = POLL_INTERVAL; + data->poll_dev->poll_interval_max = POLL_INTERVAL_MAX; + idev = data->poll_dev->input; + idev->name = MAG3110_DRV_NAME; + idev->id.bustype = BUS_I2C; + idev->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(idev, ABS_X, -15000, 15000, 0, 0); + input_set_abs_params(idev, ABS_Y, -15000, 15000, 0, 0); + input_set_abs_params(idev, ABS_Z, -15000, 15000, 0, 0); + ret = input_register_polled_device(data->poll_dev); + if (ret) { + dev_err(&client->dev, "register poll device failed!\n"); + goto error_free_poll_dev; + } + + /*create device group in sysfs as user interface */ + ret = sysfs_create_group(&idev->dev.kobj, &mag3110_attr_group); + if (ret) { + dev_err(&client->dev, "create device file failed!\n"); + ret = -EINVAL; + goto error_rm_poll_dev; + } + /* set irq type to edge rising */ +#if MAG3110_IRQ_USED + ret = request_irq(client->irq, mag3110_irq_handler, + IRQF_TRIGGER_RISING, client->dev.driver->name, idev); + if (ret < 0) { + dev_err(&client->dev, "failed to register irq %d!\n", + client->irq); + goto error_rm_dev_sysfs; + } +#endif + /* Initialize mag3110 chip */ + mag3110_init_client(client); + mag3110_pdata = data; + mag3110_pdata->active = MAG_STANDBY; + ret = of_property_read_u32(of_node, "position", &pos); + if (ret) + pos = DEFAULT_POSITION; + mag3110_pdata->position = (int)pos; + dev_info(&client->dev, "mag3110 is probed\n"); + return 0; +error_rm_dev_sysfs: + sysfs_remove_group(&client->dev.kobj, &mag3110_attr_group); +error_rm_poll_dev: + input_unregister_polled_device(data->poll_dev); +error_free_poll_dev: + input_free_polled_device(data->poll_dev); +error_rm_hwmon_dev: + hwmon_device_unregister(data->hwmon_dev); + + kfree(data); + mag3110_pdata = NULL; + + return ret; +} + +static int mag3110_remove(struct i2c_client *client) +{ + struct mag3110_data *data; + int ret; + + data = i2c_get_clientdata(client); + + data->ctl_reg1 = mag3110_read_reg(client, MAG3110_CTRL_REG1); + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, + data->ctl_reg1 & ~MAG3110_AC_MASK); + + free_irq(client->irq, data); + input_unregister_polled_device(data->poll_dev); + input_free_polled_device(data->poll_dev); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &mag3110_attr_group); + kfree(data); + mag3110_pdata = NULL; + + return ret; +} + +#ifdef CONFIG_PM +static int mag3110_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret = 0; + struct mag3110_data *data = i2c_get_clientdata(client); + if (data->active == MAG_ACTIVED) { + data->ctl_reg1 = mag3110_read_reg(client, MAG3110_CTRL_REG1); + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, + data->ctl_reg1 & ~MAG3110_AC_MASK); + } + return ret; +} + +static int mag3110_resume(struct i2c_client *client) +{ + int ret = 0; + u8 tmp_data[MAG3110_XYZ_DATA_LEN]; + struct mag3110_data *data = i2c_get_clientdata(client); + if (data->active == MAG_ACTIVED) { + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, + data->ctl_reg1); + + if (data->ctl_reg1 & MAG3110_AC_MASK) { + /* Read out MSB data to clear interrupt + flag automatically */ + mag3110_read_block_data(client, MAG3110_OUT_X_MSB, + MAG3110_XYZ_DATA_LEN, tmp_data); + } + } + return ret; +} + +#else +#define mag3110_suspend NULL +#define mag3110_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id mag3110_id[] = { + {MAG3110_DRV_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mag3110_id); +static struct i2c_driver mag3110_driver = { + .driver = {.name = MAG3110_DRV_NAME, + .owner = THIS_MODULE,}, + .suspend = mag3110_suspend, + .resume = mag3110_resume, + .probe = mag3110_probe, + .remove = mag3110_remove, + .id_table = mag3110_id, +}; + +static int __init mag3110_init(void) +{ + return i2c_add_driver(&mag3110_driver); +} + +static void __exit mag3110_exit(void) +{ + i2c_del_driver(&mag3110_driver); +} + +module_init(mag3110_init); +module_exit(mag3110_exit); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale mag3110 3-axis magnetometer driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/hwmon/max17135-hwmon.c linux-3.10.30-cubox-i/drivers/hwmon/max17135-hwmon.c --- linux-3.10.30/drivers/hwmon/max17135-hwmon.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/hwmon/max17135-hwmon.c 2014-03-08 20:33:36.000000000 +0100 @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010-2013 Freescale Semiconductor, 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 + * + */ +/* + * max17135.c + * + * Based on the MAX1619 driver. + * Copyright (C) 2003-2004 Alexey Fisher + * Jean Delvare + * + * The MAX17135 is a sensor chip made by Maxim. + * It reports up to two temperatures (its own plus up to + * one external one). + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Conversions + */ +static int temp_from_reg(int val) +{ + return val >> 8; +} + +/* + * Functions declaration + */ +static int max17135_sensor_probe(struct platform_device *pdev); +static int max17135_sensor_remove(struct platform_device *pdev); + +static const struct platform_device_id max17135_sns_id[] = { + { "max17135-sns", 0}, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, max17135_sns_id); + +/* + * Driver data (common to all clients) + */ +static struct platform_driver max17135_sensor_driver = { + .probe = max17135_sensor_probe, + .remove = max17135_sensor_remove, + .id_table = max17135_sns_id, + .driver = { + .name = "max17135_sensor", + }, +}; + +/* + * Client data (each client gets its own) + */ +struct max17135_data { + struct device *hwmon_dev; +}; + +/* + * Sysfs stuff + */ +static ssize_t show_temp_input1(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg_val; + max17135_reg_read(REG_MAX17135_INT_TEMP, ®_val); + return snprintf(buf, PAGE_SIZE, "%d\n", temp_from_reg(reg_val)); +} + +static ssize_t show_temp_input2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg_val; + max17135_reg_read(REG_MAX17135_EXT_TEMP, ®_val); + return snprintf(buf, PAGE_SIZE, "%d\n", temp_from_reg(reg_val)); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); +static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL); + +static struct attribute *max17135_attributes[] = { + &dev_attr_temp1_input.attr, + &dev_attr_temp2_input.attr, + NULL +}; + +static const struct attribute_group max17135_group = { + .attrs = max17135_attributes, +}; + +/* + * Real code + */ +static int max17135_sensor_probe(struct platform_device *pdev) +{ + struct max17135_data *data; + int err; + + data = kzalloc(sizeof(struct max17135_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + /* Register sysfs hooks */ + err = sysfs_create_group(&pdev->dev.kobj, &max17135_group); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + platform_set_drvdata(pdev, data); + + return 0; + +exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &max17135_group); +exit_free: + kfree(data); +exit: + return err; +} + +static int max17135_sensor_remove(struct platform_device *pdev) +{ + struct max17135_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &max17135_group); + + kfree(data); + return 0; +} + +static int __init sensors_max17135_init(void) +{ + return platform_driver_register(&max17135_sensor_driver); +} +module_init(sensors_max17135_init); + +static void __exit sensors_max17135_exit(void) +{ + platform_driver_unregister(&max17135_sensor_driver); +} +module_exit(sensors_max17135_exit); + +MODULE_DESCRIPTION("MAX17135 sensor driver"); +MODULE_LICENSE("GPL"); + diff -Nur linux-3.10.30/drivers/hwmon/mxc_mma8451.c linux-3.10.30-cubox-i/drivers/hwmon/mxc_mma8451.c --- linux-3.10.30/drivers/hwmon/mxc_mma8451.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/hwmon/mxc_mma8451.c 2014-03-08 20:33:36.000000000 +0100 @@ -0,0 +1,538 @@ +/* + * mma8451.c - Linux kernel modules for 3-Axis Orientation/Motion + * Detection Sensor + * + * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MMA8451_I2C_ADDR 0x1C +#define MMA8451_ID 0x1A +#define MMA8452_ID 0x2A +#define MMA8453_ID 0x3A + +#define POLL_INTERVAL_MIN 1 +#define POLL_INTERVAL_MAX 500 +#define POLL_INTERVAL 100 /* msecs */ +#define INPUT_FUZZ 32 +#define INPUT_FLAT 32 +#define MODE_CHANGE_DELAY_MS 100 + +#define MMA8451_STATUS_ZYXDR 0x08 +#define MMA8451_BUF_SIZE 7 +#define DEFAULT_POSITION 0 + +/* register enum for mma8451 registers */ +enum { + MMA8451_STATUS = 0x00, + MMA8451_OUT_X_MSB, + MMA8451_OUT_X_LSB, + MMA8451_OUT_Y_MSB, + MMA8451_OUT_Y_LSB, + MMA8451_OUT_Z_MSB, + MMA8451_OUT_Z_LSB, + + MMA8451_F_SETUP = 0x09, + MMA8451_TRIG_CFG, + MMA8451_SYSMOD, + MMA8451_INT_SOURCE, + MMA8451_WHO_AM_I, + MMA8451_XYZ_DATA_CFG, + MMA8451_HP_FILTER_CUTOFF, + + MMA8451_PL_STATUS, + MMA8451_PL_CFG, + MMA8451_PL_COUNT, + MMA8451_PL_BF_ZCOMP, + MMA8451_P_L_THS_REG, + + MMA8451_FF_MT_CFG, + MMA8451_FF_MT_SRC, + MMA8451_FF_MT_THS, + MMA8451_FF_MT_COUNT, + + MMA8451_TRANSIENT_CFG = 0x1D, + MMA8451_TRANSIENT_SRC, + MMA8451_TRANSIENT_THS, + MMA8451_TRANSIENT_COUNT, + + MMA8451_PULSE_CFG, + MMA8451_PULSE_SRC, + MMA8451_PULSE_THSX, + MMA8451_PULSE_THSY, + MMA8451_PULSE_THSZ, + MMA8451_PULSE_TMLT, + MMA8451_PULSE_LTCY, + MMA8451_PULSE_WIND, + + MMA8451_ASLP_COUNT, + MMA8451_CTRL_REG1, + MMA8451_CTRL_REG2, + MMA8451_CTRL_REG3, + MMA8451_CTRL_REG4, + MMA8451_CTRL_REG5, + + MMA8451_OFF_X, + MMA8451_OFF_Y, + MMA8451_OFF_Z, + + MMA8451_REG_END, +}; + +/* The sensitivity is represented in counts/g. In 2g mode the +sensitivity is 1024 counts/g. In 4g mode the sensitivity is 512 +counts/g and in 8g mode the sensitivity is 256 counts/g. + */ +enum { + MODE_2G = 0, + MODE_4G, + MODE_8G, +}; + +enum { + MMA_STANDBY = 0, + MMA_ACTIVED, +}; + +/* mma8451 status */ +struct mma8451_status { + u8 mode; + u8 ctl_reg1; + int active; + int position; +}; + +static struct mma8451_status mma_status; +static struct input_polled_dev *mma8451_idev; +static struct device *hwmon_dev; +static struct i2c_client *mma8451_i2c_client; + +static int senstive_mode = MODE_2G; +static int ACCHAL[8][3][3] = { + { {0, -1, 0}, {1, 0, 0}, {0, 0, 1} }, + { {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} }, + { {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} }, + { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} }, + + { {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} }, + { {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} }, + { {0, 1, 0}, {1, 0, 0}, {0, 0, -1} }, + { {1, 0, 0}, {0, -1, 0}, {0, 0, -1} }, +}; + +static DEFINE_MUTEX(mma8451_lock); +static int mma8451_adjust_position(short *x, short *y, short *z) +{ + short rawdata[3], data[3]; + int i, j; + int position = mma_status.position; + if (position < 0 || position > 7) + position = 0; + rawdata[0] = *x; + rawdata[1] = *y; + rawdata[2] = *z; + for (i = 0; i < 3; i++) { + data[i] = 0; + for (j = 0; j < 3; j++) + data[i] += rawdata[j] * ACCHAL[position][i][j]; + } + *x = data[0]; + *y = data[1]; + *z = data[2]; + return 0; +} + +static int mma8451_change_mode(struct i2c_client *client, int mode) +{ + int result; + + mma_status.ctl_reg1 = 0; + result = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, 0); + if (result < 0) + goto out; + + mma_status.mode = mode; + result = i2c_smbus_write_byte_data(client, MMA8451_XYZ_DATA_CFG, + mma_status.mode); + if (result < 0) + goto out; + mma_status.active = MMA_STANDBY; + mdelay(MODE_CHANGE_DELAY_MS); + + return 0; +out: + dev_err(&client->dev, "error when init mma8451:(%d)", result); + return result; +} + +static int mma8451_read_data(short *x, short *y, short *z) +{ + u8 tmp_data[MMA8451_BUF_SIZE]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(mma8451_i2c_client, + MMA8451_OUT_X_MSB, 7, tmp_data); + if (ret < MMA8451_BUF_SIZE) { + dev_err(&mma8451_i2c_client->dev, "i2c block read failed\n"); + return -EIO; + } + + *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; + *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; + *z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; + return 0; +} + +static void report_abs(void) +{ + short x, y, z; + int result; + int retry = 3; + + mutex_lock(&mma8451_lock); + if (mma_status.active == MMA_STANDBY) + goto out; + /* wait for the data ready */ + do { + result = i2c_smbus_read_byte_data(mma8451_i2c_client, + MMA8451_STATUS); + retry--; + msleep(1); + } while (!(result & MMA8451_STATUS_ZYXDR) && retry > 0); + if (retry == 0) + goto out; + if (mma8451_read_data(&x, &y, &z) != 0) + goto out; + mma8451_adjust_position(&x, &y, &z); + input_report_abs(mma8451_idev->input, ABS_X, x); + input_report_abs(mma8451_idev->input, ABS_Y, y); + input_report_abs(mma8451_idev->input, ABS_Z, z); + input_sync(mma8451_idev->input); +out: + mutex_unlock(&mma8451_lock); +} + +static void mma8451_dev_poll(struct input_polled_dev *dev) +{ + report_abs(); +} + +static ssize_t mma8451_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + u8 val; + int enable; + + mutex_lock(&mma8451_lock); + client = mma8451_i2c_client; + val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); + if ((val & 0x01) && mma_status.active == MMA_ACTIVED) + enable = 1; + else + enable = 0; + mutex_unlock(&mma8451_lock); + return sprintf(buf, "%d\n", enable); +} + +static ssize_t mma8451_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + int ret; + unsigned long enable; + u8 val = 0; + + ret = strict_strtoul(buf, 10, &enable); + if (ret) { + dev_err(dev, "string transform error\n"); + return ret; + } + + mutex_lock(&mma8451_lock); + client = mma8451_i2c_client; + enable = (enable > 0) ? 1 : 0; + if (enable && mma_status.active == MMA_STANDBY) { + val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); + ret = + i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + val | 0x01); + if (!ret) + mma_status.active = MMA_ACTIVED; + + } else if (enable == 0 && mma_status.active == MMA_ACTIVED) { + val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); + ret = + i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + val & 0xFE); + if (!ret) + mma_status.active = MMA_STANDBY; + + } + mutex_unlock(&mma8451_lock); + return count; +} + +static ssize_t mma8451_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int position = 0; + mutex_lock(&mma8451_lock); + position = mma_status.position; + mutex_unlock(&mma8451_lock); + return sprintf(buf, "%d\n", position); +} + +static ssize_t mma8451_position_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long position; + int ret; + ret = strict_strtoul(buf, 10, &position); + if (ret) { + dev_err(dev, "string transform error\n"); + return ret; + } + + mutex_lock(&mma8451_lock); + mma_status.position = (int)position; + mutex_unlock(&mma8451_lock); + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + mma8451_enable_show, mma8451_enable_store); +static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, + mma8451_position_show, mma8451_position_store); + +static struct attribute *mma8451_attributes[] = { + &dev_attr_enable.attr, + &dev_attr_position.attr, + NULL +}; + +static const struct attribute_group mma8451_attr_group = { + .attrs = mma8451_attributes, +}; + +static int mma8451_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int result, client_id; + struct input_dev *idev; + struct i2c_adapter *adapter; + u32 pos; + struct device_node *of_node = client->dev.of_node; + struct regulator *vdd, *vdd_io; + + mma8451_i2c_client = client; + + vdd = devm_regulator_get(&client->dev, "vdd"); + if (!IS_ERR(vdd)) { + result = regulator_enable(vdd); + if (result) { + dev_err(&client->dev, "vdd set voltage error\n"); + return result; + } + } + + vdd_io = devm_regulator_get(&client->dev, "vddio"); + if (!IS_ERR(vdd_io)) { + result = regulator_enable(vdd_io); + if (result) { + dev_err(&client->dev, "vddio set voltage error\n"); + return result; + } + } + + adapter = to_i2c_adapter(client->dev.parent); + result = i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA); + if (!result) + goto err_out; + + client_id = i2c_smbus_read_byte_data(client, MMA8451_WHO_AM_I); + if (client_id != MMA8451_ID && client_id != MMA8452_ID + && client_id != MMA8453_ID) { + dev_err(&client->dev, + "read chip ID 0x%x is not equal to 0x%x or 0x%x!\n", + result, MMA8451_ID, MMA8452_ID); + result = -EINVAL; + goto err_out; + } + + /* Initialize the MMA8451 chip */ + result = mma8451_change_mode(client, senstive_mode); + if (result) { + dev_err(&client->dev, + "error when init mma8451 chip:(%d)\n", result); + goto err_out; + } + + hwmon_dev = hwmon_device_register(&client->dev); + if (!hwmon_dev) { + result = -ENOMEM; + dev_err(&client->dev, "error when register hwmon device\n"); + goto err_out; + } + + mma8451_idev = input_allocate_polled_device(); + if (!mma8451_idev) { + result = -ENOMEM; + dev_err(&client->dev, "alloc poll device failed!\n"); + goto err_alloc_poll_device; + } + mma8451_idev->poll = mma8451_dev_poll; + mma8451_idev->poll_interval = POLL_INTERVAL; + mma8451_idev->poll_interval_min = POLL_INTERVAL_MIN; + mma8451_idev->poll_interval_max = POLL_INTERVAL_MAX; + idev = mma8451_idev->input; + idev->name = "mma845x"; + idev->id.bustype = BUS_I2C; + idev->evbit[0] = BIT_MASK(EV_ABS); + + input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + + result = input_register_polled_device(mma8451_idev); + if (result) { + dev_err(&client->dev, "register poll device failed!\n"); + goto err_register_polled_device; + } + result = sysfs_create_group(&idev->dev.kobj, &mma8451_attr_group); + if (result) { + dev_err(&client->dev, "create device file failed!\n"); + result = -EINVAL; + goto err_register_polled_device; + } + + result = of_property_read_u32(of_node, "position", &pos); + if (result) + pos = DEFAULT_POSITION; + mma_status.position = (int)pos; + + return 0; +err_register_polled_device: + input_free_polled_device(mma8451_idev); +err_alloc_poll_device: + hwmon_device_unregister(&client->dev); +err_out: + return result; +} + +static int mma8451_stop_chip(struct i2c_client *client) +{ + int ret = 0; + if (mma_status.active == MMA_ACTIVED) { + mma_status.ctl_reg1 = i2c_smbus_read_byte_data(client, + MMA8451_CTRL_REG1); + ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + mma_status.ctl_reg1 & 0xFE); + } + return ret; +} + +static int mma8451_remove(struct i2c_client *client) +{ + int ret; + ret = mma8451_stop_chip(client); + hwmon_device_unregister(hwmon_dev); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int mma8451_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + return mma8451_stop_chip(client); +} + +static int mma8451_resume(struct device *dev) +{ + int ret = 0; + struct i2c_client *client = to_i2c_client(dev); + if (mma_status.active == MMA_ACTIVED) + ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, + mma_status.ctl_reg1); + return ret; + +} +#endif + +static const struct i2c_device_id mma8451_id[] = { + {"mma8451", 0}, +}; + +MODULE_DEVICE_TABLE(i2c, mma8451_id); + +static SIMPLE_DEV_PM_OPS(mma8451_pm_ops, mma8451_suspend, mma8451_resume); +static struct i2c_driver mma8451_driver = { + .driver = { + .name = "mma8451", + .owner = THIS_MODULE, + .pm = &mma8451_pm_ops, + }, + .probe = mma8451_probe, + .remove = mma8451_remove, + .id_table = mma8451_id, +}; + +static int __init mma8451_init(void) +{ + /* register driver */ + int res; + + res = i2c_add_driver(&mma8451_driver); + if (res < 0) { + printk(KERN_INFO "add mma8451 i2c driver failed\n"); + return -ENODEV; + } + return res; +} + +static void __exit mma8451_exit(void) +{ + i2c_del_driver(&mma8451_driver); +} + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MMA8451 3-Axis Orientation/Motion Detection Sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(mma8451_init); +module_exit(mma8451_exit); diff -Nur linux-3.10.30/drivers/i2c/busses/i2c-davinci.c linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-davinci.c --- linux-3.10.30/drivers/i2c/busses/i2c-davinci.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-davinci.c 2014-03-08 20:33:36.000000000 +0100 @@ -646,13 +646,6 @@ struct resource *mem, *irq; int r; - /* NOTE: driver uses the static register mapping */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq) { dev_err(&pdev->dev, "no irq resource?\n"); @@ -697,6 +690,7 @@ return -ENODEV; clk_prepare_enable(dev->clk); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(dev->base)) { r = PTR_ERR(dev->base); diff -Nur linux-3.10.30/drivers/i2c/busses/i2c-designware-platdrv.c linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-designware-platdrv.c --- linux-3.10.30/drivers/i2c/busses/i2c-designware-platdrv.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-designware-platdrv.c 2014-03-08 20:33:36.000000000 +0100 @@ -87,13 +87,6 @@ struct resource *mem; int irq, r; - /* NOTE: driver uses the static register mapping */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -EINVAL; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); @@ -104,6 +97,7 @@ if (!dev) return -ENOMEM; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(dev->base)) return PTR_ERR(dev->base); diff -Nur linux-3.10.30/drivers/i2c/busses/i2c-imx.c linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-imx.c --- linux-3.10.30/drivers/i2c/busses/i2c-imx.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-imx.c 2014-03-08 20:33:36.000000000 +0100 @@ -51,7 +51,6 @@ #include #include #include -#include #include /** Defines ******************************************************************** @@ -97,7 +96,7 @@ * Duplicated divider values removed from list */ -static u16 __initdata i2c_clk_div[50][2] = { +static u16 i2c_clk_div[50][2] = { { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, @@ -128,6 +127,9 @@ int stopped; unsigned int ifdr; /* IMX_I2C_IFDR */ enum imx_i2c_type devtype; + + unsigned int cur_clk; + unsigned int bitrate; }; static struct platform_device_id imx_i2c_devtype[] = { @@ -148,6 +150,7 @@ { .compatible = "fsl,imx21-i2c", .data = &imx_i2c_devtype[IMX21_I2C], }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids); static inline int is_imx1_i2c(struct imx_i2c_struct *i2c_imx) { @@ -205,6 +208,49 @@ return 0; } +static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx) +{ + unsigned int i2c_clk_rate; + unsigned int div; + int i; + + /* Divider value calculation */ + i2c_clk_rate = clk_get_rate(i2c_imx->clk); + if (i2c_imx->cur_clk == i2c_clk_rate) + return; + else + i2c_imx->cur_clk = i2c_clk_rate; + + div = (i2c_clk_rate + i2c_imx->bitrate - 1) / i2c_imx->bitrate; + if (div < i2c_clk_div[0][0]) + i = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + i = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (i = 0; i2c_clk_div[i][0] < div; i++) + ; + + /* Store divider value */ + i2c_imx->ifdr = i2c_clk_div[i][1]; + + /* + * There dummy delay is calculated. + * It should be about one I2C clock period long. + * This delay is used in I2C bus disable function + * to fix chip hardware bug. + */ + i2c_imx->disable_delay = (500000U * i2c_clk_div[i][0] + + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2); + + /* dev_dbg() can't be used, because adapter is not yet registered */ +#ifdef CONFIG_I2C_DEBUG_BUS + dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", + __func__, i2c_clk_rate, div); + dev_dbg(&i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", + __func__, i2c_clk_div[i][1], i2c_clk_div[i][0]); +#endif +} + static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) { unsigned int temp = 0; @@ -212,6 +258,7 @@ dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); + i2c_imx_set_clk(i2c_imx); clk_prepare_enable(i2c_imx->clk); writeb(i2c_imx->ifdr, i2c_imx->base + IMX_I2C_IFDR); /* Enable I2C controller */ @@ -264,44 +311,6 @@ clk_disable_unprepare(i2c_imx->clk); } -static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, - unsigned int rate) -{ - unsigned int i2c_clk_rate; - unsigned int div; - int i; - - /* Divider value calculation */ - i2c_clk_rate = clk_get_rate(i2c_imx->clk); - div = (i2c_clk_rate + rate - 1) / rate; - if (div < i2c_clk_div[0][0]) - i = 0; - else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) - i = ARRAY_SIZE(i2c_clk_div) - 1; - else - for (i = 0; i2c_clk_div[i][0] < div; i++); - - /* Store divider value */ - i2c_imx->ifdr = i2c_clk_div[i][1]; - - /* - * There dummy delay is calculated. - * It should be about one I2C clock period long. - * This delay is used in I2C bus disable function - * to fix chip hardware bug. - */ - i2c_imx->disable_delay = (500000U * i2c_clk_div[i][0] - + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2); - - /* dev_dbg() can't be used, because adapter is not yet registered */ -#ifdef CONFIG_I2C_DEBUG_BUS - dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", - __func__, i2c_clk_rate, div); - dev_dbg(&i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", - __func__, i2c_clk_div[i][1], i2c_clk_div[i][0]); -#endif -} - static irqreturn_t i2c_imx_isr(int irq, void *dev_id) { struct imx_i2c_struct *i2c_imx = dev_id; @@ -493,24 +502,18 @@ struct imx_i2c_struct *i2c_imx; struct resource *res; struct imxi2c_platform_data *pdata = pdev->dev.platform_data; - struct pinctrl *pinctrl; void __iomem *base; int irq, ret; - u32 bitrate; dev_dbg(&pdev->dev, "<%s>\n", __func__); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "can't get device resources\n"); - return -ENOENT; - } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "can't get irq number\n"); return -ENOENT; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); @@ -535,12 +538,6 @@ i2c_imx->adapter.dev.of_node = pdev->dev.of_node; i2c_imx->base = base; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - dev_err(&pdev->dev, "can't get/select pinctrl\n"); - return PTR_ERR(pinctrl); - } - /* Get I2C clock */ i2c_imx->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(i2c_imx->clk)) { @@ -548,6 +545,11 @@ return PTR_ERR(i2c_imx->clk); } + ret = clk_prepare_enable(i2c_imx->clk); + if (ret) { + dev_err(&pdev->dev, "can't enable I2C clock\n"); + return ret; + } /* Request IRQ */ ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0, pdev->name, i2c_imx); @@ -563,12 +565,12 @@ i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); /* Set up clock divider */ - bitrate = IMX_I2C_BIT_RATE; + i2c_imx->bitrate = IMX_I2C_BIT_RATE; ret = of_property_read_u32(pdev->dev.of_node, - "clock-frequency", &bitrate); + "clock-frequency", &i2c_imx->bitrate); if (ret < 0 && pdata && pdata->bitrate) - bitrate = pdata->bitrate; - i2c_imx_set_clk(i2c_imx, bitrate); + i2c_imx->bitrate = pdata->bitrate; + i2c_imx_set_clk(i2c_imx); /* Set up chip registers to defaults */ writeb(0, i2c_imx->base + IMX_I2C_I2CR); @@ -585,6 +587,7 @@ /* Set up platform driver data */ platform_set_drvdata(pdev, i2c_imx); + clk_disable_unprepare(i2c_imx->clk); dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq); dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n", diff -Nur linux-3.10.30/drivers/i2c/busses/i2c-omap.c linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-omap.c --- linux-3.10.30/drivers/i2c/busses/i2c-omap.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-omap.c 2014-03-08 20:33:36.000000000 +0100 @@ -1087,13 +1087,6 @@ u32 rev; u16 minor, major, scheme; - /* NOTE: driver uses the static register mapping */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); @@ -1106,6 +1099,7 @@ return -ENOMEM; } + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(dev->base)) return PTR_ERR(dev->base); diff -Nur linux-3.10.30/drivers/i2c/busses/i2c-rcar.c linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-rcar.c --- linux-3.10.30/drivers/i2c/busses/i2c-rcar.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/i2c/busses/i2c-rcar.c 2014-03-08 20:33:37.000000000 +0100 @@ -623,12 +623,6 @@ u32 bus_speed; int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "no mmio resources\n"); - return -ENODEV; - } - priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL); if (!priv) { dev_err(dev, "no mem for private data\n"); @@ -642,6 +636,7 @@ if (ret < 0) return ret; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->io = devm_ioremap_resource(dev, res); if (IS_ERR(priv->io)) return PTR_ERR(priv->io); diff -Nur linux-3.10.30/drivers/input/keyboard/gpio_keys.c linux-3.10.30-cubox-i/drivers/input/keyboard/gpio_keys.c --- linux-3.10.30/drivers/input/keyboard/gpio_keys.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/keyboard/gpio_keys.c 2014-03-08 20:33:39.000000000 +0100 @@ -3,6 +3,7 @@ * * Copyright 2005 Phil Blundell * Copyright 2010, 2011 David Jander + * Copyright (C) 2013 Freescale Semiconductor, 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 @@ -472,6 +473,8 @@ isr = gpio_keys_gpio_isr; irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + if (bdata->button->wakeup) + irqflags |= IRQF_NO_SUSPEND; } else { if (!button->irq) { diff -Nur linux-3.10.30/drivers/input/keyboard/imx_keypad.c linux-3.10.30-cubox-i/drivers/input/keyboard/imx_keypad.c --- linux-3.10.30/drivers/input/keyboard/imx_keypad.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/keyboard/imx_keypad.c 2014-03-08 20:33:39.000000000 +0100 @@ -1,6 +1,7 @@ /* * Driver for the IMX keypad port. * Copyright (C) 2009 Alberto Panizzo + * Copyright (C) 2013 Freescale Semiconductor, 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 @@ -553,6 +554,8 @@ if (device_may_wakeup(&pdev->dev)) enable_irq_wake(kbd->irq); + else + pinctrl_pm_select_sleep_state(dev); return 0; } @@ -566,6 +569,8 @@ if (device_may_wakeup(&pdev->dev)) disable_irq_wake(kbd->irq); + else + pinctrl_pm_select_default_state(dev); mutex_lock(&input_dev->mutex); diff -Nur linux-3.10.30/drivers/input/misc/Kconfig linux-3.10.30-cubox-i/drivers/input/misc/Kconfig --- linux-3.10.30/drivers/input/misc/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/misc/Kconfig 2014-03-08 20:33:39.000000000 +0100 @@ -637,4 +637,14 @@ To compile this driver as a module, choose M here: the module will be called xen-kbdfront. +config INPUT_ISL29023 + tristate "Intersil ISL29023 ambient light sensor" + depends on I2C && SYSFS + help + If you say yes here you get support for the Intersil ISL29023 + ambient light sensor. + + This driver can also be built as a module. If so, the module + will be called isl29023. + endif diff -Nur linux-3.10.30/drivers/input/misc/Makefile linux-3.10.30-cubox-i/drivers/input/misc/Makefile --- linux-3.10.30/drivers/input/misc/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/misc/Makefile 2014-03-08 20:33:39.000000000 +0100 @@ -60,3 +60,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_ISL29023) += isl29023.o diff -Nur linux-3.10.30/drivers/input/misc/isl29023.c linux-3.10.30-cubox-i/drivers/input/misc/isl29023.c --- linux-3.10.30/drivers/input/misc/isl29023.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/misc/isl29023.c 2014-03-08 20:33:39.000000000 +0100 @@ -0,0 +1,1029 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ISL29023_DRV_NAME "isl29023" +#define DRIVER_VERSION "1.0" + +#define ISL29023_COMMAND1 0x00 +#define ISL29023_MODE_SHIFT (5) +#define ISL29023_MODE_MASK (0x7 << ISL29023_MODE_SHIFT) +#define ISL29023_INT_FLAG_SHIFT (2) +#define ISL29023_INT_FLAG_MASK (0x1 << ISL29023_INT_FLAG_SHIFT) +#define ISL29023_INT_PERSISTS_SHIFT (0) +#define ISL29023_INT_PERSISTS_MASK (0x3 << ISL29023_INT_PERSISTS_SHIFT) + +#define ISL29023_COMMAND2 0x01 +#define ISL29023_RES_SHIFT (2) +#define ISL29023_RES_MASK (0x3 << ISL29023_RES_SHIFT) +#define ISL29023_RANGE_SHIFT (0) +#define ISL29023_RANGE_MASK (0x3 << ISL29023_RANGE_SHIFT) + +#define ISL29023_REG_LSB_SENSOR 0x02 +#define ISL29023_REG_MSB_SENSOR 0x03 +#define ISL29023_REG_IRQ_TH_LO_LSB 0x04 +#define ISL29023_REG_IRQ_TH_LO_MSB 0x05 +#define ISL29023_REG_IRQ_TH_HI_LSB 0x06 +#define ISL29023_REG_IRQ_TH_HI_MSB 0x07 + +#define ISL29023_NUM_CACHABLE_REGS 8 +#define DEF_RANGE 2 +#define DEFAULT_REGISTOR_VAL 499 + +struct isl29023_data { + struct i2c_client *client; + struct mutex lock; + struct input_dev *input; + struct work_struct work; + struct workqueue_struct *workqueue; + char phys[32]; + u8 reg_cache[ISL29023_NUM_CACHABLE_REGS]; + u8 mode_before_suspend; + u8 mode_before_interrupt; + u16 rext; +}; + +static int gain_range[] = { + 1000, 4000, 16000, 64000 +}; + +/* + * register access helpers + */ +static int __isl29023_read_reg(struct i2c_client *client, + u32 reg, u8 mask, u8 shift) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + return (data->reg_cache[reg] & mask) >> shift; +} + +static int __isl29023_write_reg(struct i2c_client *client, + u32 reg, u8 mask, u8 shift, u8 val) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + int ret = 0; + u8 tmp; + + if (reg >= ISL29023_NUM_CACHABLE_REGS) + return -EINVAL; + + mutex_lock(&data->lock); + + tmp = data->reg_cache[reg]; + tmp &= ~mask; + tmp |= val << shift; + + ret = i2c_smbus_write_byte_data(client, reg, tmp); + if (!ret) + data->reg_cache[reg] = tmp; + + mutex_unlock(&data->lock); + return ret; +} + +/* + * internally used functions + */ + +/* interrupt persists */ +static int isl29023_get_int_persists(struct i2c_client *client) +{ + return __isl29023_read_reg(client, ISL29023_COMMAND1, + ISL29023_INT_PERSISTS_MASK, ISL29023_INT_PERSISTS_SHIFT); +} + +static int isl29023_set_int_persists(struct i2c_client *client, + int int_persists) +{ + return __isl29023_write_reg(client, ISL29023_COMMAND1, + ISL29023_INT_PERSISTS_MASK, ISL29023_INT_PERSISTS_SHIFT, + int_persists); +} + +/* interrupt flag */ +static int isl29023_get_int_flag(struct i2c_client *client) +{ + return __isl29023_read_reg(client, ISL29023_COMMAND1, + ISL29023_INT_FLAG_MASK, ISL29023_INT_FLAG_SHIFT); +} + +static int isl29023_set_int_flag(struct i2c_client *client, int flag) +{ + return __isl29023_write_reg(client, ISL29023_COMMAND1, + ISL29023_INT_FLAG_MASK, ISL29023_INT_FLAG_SHIFT, flag); +} + +/* interrupt lt */ +static int isl29023_get_int_lt(struct i2c_client *client) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + int lsb, msb, lt; + + mutex_lock(&data->lock); + lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB); + + if (lsb < 0) { + mutex_unlock(&data->lock); + return lsb; + } + + msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB); + mutex_unlock(&data->lock); + + if (msb < 0) + return msb; + + lt = ((msb << 8) | lsb); + + return lt; +} + +static int isl29023_set_int_lt(struct i2c_client *client, int lt) +{ + int ret = 0; + struct isl29023_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB, + lt & 0xff); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB, + (lt >> 8) & 0xff); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + data->reg_cache[ISL29023_REG_IRQ_TH_LO_MSB] = (lt >> 8) & 0xff; + data->reg_cache[ISL29023_REG_IRQ_TH_LO_LSB] = lt & 0xff; + mutex_unlock(&data->lock); + + return ret; +} + +/* interrupt ht */ +static int isl29023_get_int_ht(struct i2c_client *client) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + int lsb, msb, ht; + + mutex_lock(&data->lock); + lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB); + + if (lsb < 0) { + mutex_unlock(&data->lock); + return lsb; + } + + msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB); + mutex_unlock(&data->lock); + + if (msb < 0) + return msb; + + ht = ((msb << 8) | lsb); + + return ht; +} + +static int isl29023_set_int_ht(struct i2c_client *client, int ht) +{ + int ret = 0; + struct isl29023_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB, + ht & 0xff); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB, + (ht >> 8) & 0xff); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + data->reg_cache[ISL29023_REG_IRQ_TH_HI_MSB] = (ht >> 8) & 0xff; + data->reg_cache[ISL29023_REG_IRQ_TH_HI_LSB] = ht & 0xff; + mutex_unlock(&data->lock); + + return ret; +} + +/* range */ +static int isl29023_get_range(struct i2c_client *client) +{ + return __isl29023_read_reg(client, ISL29023_COMMAND2, + ISL29023_RANGE_MASK, ISL29023_RANGE_SHIFT); +} + +static int isl29023_set_range(struct i2c_client *client, int range) +{ + return __isl29023_write_reg(client, ISL29023_COMMAND2, + ISL29023_RANGE_MASK, ISL29023_RANGE_SHIFT, range); +} + +/* resolution */ +static int isl29023_get_resolution(struct i2c_client *client) +{ + return __isl29023_read_reg(client, ISL29023_COMMAND2, + ISL29023_RES_MASK, ISL29023_RES_SHIFT); +} + +static int isl29023_set_resolution(struct i2c_client *client, int res) +{ + return __isl29023_write_reg(client, ISL29023_COMMAND2, + ISL29023_RES_MASK, ISL29023_RES_SHIFT, res); +} + +/* mode */ +static int isl29023_get_mode(struct i2c_client *client) +{ + return __isl29023_read_reg(client, ISL29023_COMMAND1, + ISL29023_MODE_MASK, ISL29023_MODE_SHIFT); +} + +static int isl29023_set_mode(struct i2c_client *client, int mode) +{ + return __isl29023_write_reg(client, ISL29023_COMMAND1, + ISL29023_MODE_MASK, ISL29023_MODE_SHIFT, mode); +} + +/* power_state */ +static int isl29023_set_power_state(struct i2c_client *client, int state) +{ + return __isl29023_write_reg(client, ISL29023_COMMAND1, + ISL29023_MODE_MASK, ISL29023_MODE_SHIFT, + state ? + ISL29023_ALS_ONCE_MODE : ISL29023_PD_MODE); +} + +static int isl29023_get_power_state(struct i2c_client *client) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + u8 cmdreg = data->reg_cache[ISL29023_COMMAND1]; + + if (cmdreg & ISL29023_MODE_MASK) + return 1; + else + return 0; +} + +static int isl29023_get_adc_value(struct i2c_client *client) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + int lsb, msb, range, bitdepth; + + mutex_lock(&data->lock); + lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_LSB_SENSOR); + + if (lsb < 0) { + mutex_unlock(&data->lock); + return lsb; + } + + msb = i2c_smbus_read_byte_data(client, ISL29023_REG_MSB_SENSOR); + mutex_unlock(&data->lock); + + if (msb < 0) + return msb; + + range = isl29023_get_range(client); + bitdepth = (4 - isl29023_get_resolution(client)) * 4; + return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext)) + >> bitdepth; +} + +static int isl29023_get_int_lt_value(struct i2c_client *client) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + int lsb, msb, range, bitdepth; + + mutex_lock(&data->lock); + lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB); + + if (lsb < 0) { + mutex_unlock(&data->lock); + return lsb; + } + + msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB); + mutex_unlock(&data->lock); + + if (msb < 0) + return msb; + + range = isl29023_get_range(client); + bitdepth = (4 - isl29023_get_resolution(client)) * 4; + return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext)) + >> bitdepth; +} + +static int isl29023_get_int_ht_value(struct i2c_client *client) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + int lsb, msb, range, bitdepth; + + mutex_lock(&data->lock); + lsb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB); + + if (lsb < 0) { + mutex_unlock(&data->lock); + return lsb; + } + + msb = i2c_smbus_read_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB); + mutex_unlock(&data->lock); + + if (msb < 0) + return msb; + + range = isl29023_get_range(client); + bitdepth = (4 - isl29023_get_resolution(client)) * 4; + return (((msb << 8) | lsb) * ((gain_range[range] * 499) / data->rext)) + >> bitdepth; +} + +/* + * sysfs layer + */ + +/* interrupt persists */ +static ssize_t isl29023_show_int_persists(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%i\n", isl29023_get_int_persists(client)); +} + +static ssize_t isl29023_store_int_persists(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || + (val > ISL29023_INT_PERSISTS_16)) + return -EINVAL; + + ret = isl29023_set_int_persists(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(int_persists, S_IWUSR | S_IRUGO, + isl29023_show_int_persists, isl29023_store_int_persists); + +/* interrupt flag */ +static ssize_t isl29023_show_int_flag(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%i\n", isl29023_get_int_flag(client)); +} + +static ssize_t isl29023_store_int_flag(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 1)) + return -EINVAL; + + ret = isl29023_set_int_flag(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(int_flag, S_IWUSR | S_IRUGO, + isl29023_show_int_flag, isl29023_store_int_flag); + +/* interrupt lt */ +static ssize_t isl29023_show_int_lt(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%i\n", isl29023_get_int_lt(client)); +} + +static ssize_t isl29023_store_int_lt(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 16, &val) < 0) || (val > 0xffff)) + return -EINVAL; + + ret = isl29023_set_int_lt(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(int_lt, S_IWUSR | S_IRUGO, + isl29023_show_int_lt, isl29023_store_int_lt); + +/* interrupt ht */ +static ssize_t isl29023_show_int_ht(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%i\n", isl29023_get_int_ht(client)); +} + +static ssize_t isl29023_store_int_ht(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 16, &val) < 0) || (val > 0xffff)) + return -EINVAL; + + ret = isl29023_set_int_ht(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(int_ht, S_IWUSR | S_IRUGO, + isl29023_show_int_ht, isl29023_store_int_ht); + +/* range */ +static ssize_t isl29023_show_range(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%i\n", isl29023_get_range(client)); +} + +static ssize_t isl29023_store_range(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > ISL29023_RANGE_64K)) + return -EINVAL; + + ret = isl29023_set_range(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, + isl29023_show_range, isl29023_store_range); + + +/* resolution */ +static ssize_t isl29023_show_resolution(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29023_get_resolution(client)); +} + +static ssize_t isl29023_store_resolution(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > ISL29023_RES_4)) + return -EINVAL; + + ret = isl29023_set_resolution(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO, + isl29023_show_resolution, isl29023_store_resolution); + +/* mode */ +static ssize_t isl29023_show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29023_get_mode(client)); +} + +static ssize_t isl29023_store_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || + (val > ISL29023_IR_CONT_MODE)) + return -EINVAL; + + ret = isl29023_set_mode(client, val); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, + isl29023_show_mode, isl29023_store_mode); + + +/* power state */ +static ssize_t isl29023_show_power_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29023_get_power_state(client)); +} + +static ssize_t isl29023_store_power_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val; + int ret; + + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 1)) + return -EINVAL; + + ret = isl29023_set_power_state(client, val); + return ret ? ret : count; +} + +static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, + isl29023_show_power_state, isl29023_store_power_state); + +/* lux */ +static ssize_t isl29023_show_lux(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + /* No LUX data if not operational */ + if (!isl29023_get_power_state(client)) + return -EBUSY; + + return sprintf(buf, "%d\n", isl29023_get_adc_value(client)); +} + +static DEVICE_ATTR(lux, S_IRUGO, isl29023_show_lux, NULL); + +/* lux interrupt low threshold */ +static ssize_t isl29023_show_int_lt_lux(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + /* No LUX data if not operational */ + if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && + isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE) + return -EIO; + + return sprintf(buf, "%d\n", isl29023_get_int_lt_value(client)); +} + +static ssize_t isl29023_store_int_lt_lux(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct isl29023_data *data = i2c_get_clientdata(client); + unsigned long val, lux_data; + int range, bitdepth, ret; + u8 lsb, msb; + + if ((strict_strtoul(buf, 10, &val) < 0)) + return -EINVAL; + + /* No LUX data if not operational */ + if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && + isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE) + return -EIO; + + if (val > (gain_range[isl29023_get_range(client)]*499/data->rext)) + return -EINVAL; + + range = isl29023_get_range(client); + bitdepth = (4 - isl29023_get_resolution(client)) * 4; + lux_data = ((unsigned long)(val << bitdepth)) / + ((gain_range[range] * 499) / data->rext); + lux_data &= 0xffff; + + msb = lux_data >> 8; + lsb = lux_data & 0xff; + + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_LSB, + lsb); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_LO_MSB, + msb); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + data->reg_cache[ISL29023_REG_IRQ_TH_LO_MSB] = msb; + data->reg_cache[ISL29023_REG_IRQ_TH_LO_LSB] = lsb; + mutex_unlock(&data->lock); + + return count; +} + +static DEVICE_ATTR(int_lt_lux, S_IWUSR | S_IRUGO, + isl29023_show_int_lt_lux, isl29023_store_int_lt_lux); + +/* lux interrupt high threshold */ +static ssize_t isl29023_show_int_ht_lux(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + /* No LUX data if not operational */ + if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && + isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE) + return -EIO; + + return sprintf(buf, "%d\n", isl29023_get_int_ht_value(client)); +} + +static ssize_t isl29023_store_int_ht_lux(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct isl29023_data *data = i2c_get_clientdata(client); + unsigned long val, lux_data; + int range, bitdepth, ret; + u8 lsb, msb; + + if ((strict_strtoul(buf, 10, &val) < 0)) + return -EINVAL; + + /* No LUX data if not operational */ + if (isl29023_get_mode(client) != ISL29023_ALS_ONCE_MODE && + isl29023_get_mode(client) != ISL29023_ALS_CONT_MODE) + return -EIO; + + if (val > (gain_range[isl29023_get_range(client)]*499/data->rext)) + return -EINVAL; + + range = isl29023_get_range(client); + bitdepth = (4 - isl29023_get_resolution(client)) * 4; + lux_data = ((unsigned long)(val << bitdepth)) / + ((gain_range[range] * 499) / data->rext); + lux_data &= 0xffff; + + msb = lux_data >> 8; + lsb = lux_data & 0xff; + + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_LSB, + lsb); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, ISL29023_REG_IRQ_TH_HI_MSB, + msb); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + data->reg_cache[ISL29023_REG_IRQ_TH_HI_MSB] = msb; + data->reg_cache[ISL29023_REG_IRQ_TH_HI_LSB] = lsb; + mutex_unlock(&data->lock); + + return count; +} + +static DEVICE_ATTR(int_ht_lux, S_IWUSR | S_IRUGO, + isl29023_show_int_ht_lux, isl29023_store_int_ht_lux); + +static struct attribute *isl29023_attributes[] = { + &dev_attr_int_persists.attr, + &dev_attr_range.attr, + &dev_attr_resolution.attr, + &dev_attr_mode.attr, + &dev_attr_power_state.attr, + &dev_attr_lux.attr, + &dev_attr_int_lt_lux.attr, + &dev_attr_int_ht_lux.attr, + &dev_attr_int_lt.attr, + &dev_attr_int_ht.attr, + &dev_attr_int_flag.attr, + NULL +}; + +static const struct attribute_group isl29023_attr_group = { + .attrs = isl29023_attributes, +}; + +static int isl29023_init_client(struct i2c_client *client) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + int i; + + /* read all the registers once to fill the cache. + * if one of the reads fails, we consider the init failed */ + for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) { + int v = i2c_smbus_read_byte_data(client, i); + if (v < 0) + return -ENODEV; + + data->reg_cache[i] = v; + } + + /* set defaults */ + isl29023_set_int_persists(client, ISL29023_INT_PERSISTS_8); + isl29023_set_int_ht(client, 0xffff); + isl29023_set_int_lt(client, 0x0); + isl29023_set_range(client, ISL29023_RANGE_16K); + isl29023_set_resolution(client, ISL29023_RES_16); + isl29023_set_mode(client, ISL29023_ALS_ONCE_MODE); + isl29023_set_int_flag(client, 0); + isl29023_set_power_state(client, 0); + + return 0; +} + +static void isl29023_work(struct work_struct *work) +{ + struct isl29023_data *data = + container_of(work, struct isl29023_data, work); + struct i2c_client *client = data->client; + int lux; + + /* Clear interrupt flag */ + isl29023_set_int_flag(client, 0); + + data->mode_before_interrupt = isl29023_get_mode(client); + lux = isl29023_get_adc_value(client); + + /* To clear the interrpt status */ + isl29023_set_power_state(client, ISL29023_PD_MODE); + isl29023_set_mode(client, data->mode_before_interrupt); + + msleep(100); + + input_report_abs(data->input, ABS_MISC, lux); + input_sync(data->input); +} + +static irqreturn_t isl29023_irq_handler(int irq, void *handle) +{ + struct isl29023_data *data = handle; + queue_work(data->workqueue, &data->work); + return IRQ_HANDLED; +} + +/* + * I2C layer + */ + +static int isl29023_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct isl29023_data *data; + struct input_dev *input_dev; + int err = 0; + struct regulator *vdd = NULL; + u32 rext = 0; + struct device_node *of_node = client->dev.of_node; + + vdd = devm_regulator_get(&client->dev, "vdd"); + if (!IS_ERR(vdd)) { + err = regulator_enable(vdd); + if (err) { + dev_err(&client->dev, "vdd set voltage error\n"); + return err; + } + } + + err = of_property_read_u32(of_node, "rext", &rext); + if (err) + rext = DEFAULT_REGISTOR_VAL; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + data = kzalloc(sizeof(struct isl29023_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->rext = (u16)rext; + snprintf(data->phys, sizeof(data->phys), + "%s", dev_name(&client->dev)); + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + /* initialize the ISL29023 chip */ + err = isl29023_init_client(client); + if (err) + goto exit_kfree; + + /* register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &isl29023_attr_group); + if (err) + goto exit_kfree; + + input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto exit_kfree; + } + + data->input = input_dev; + input_dev->name = "isl29023 light sensor"; + input_dev->id.bustype = BUS_I2C; + input_dev->phys = data->phys; + + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_MISC, 0, + gain_range[DEF_RANGE]*499/data->rext, 0, 0); + + err = input_register_device(input_dev); + if (err) + goto exit_free_input; + + /* set irq type to edge falling */ + irq_set_irq_type(client->irq, IRQF_TRIGGER_FALLING); + err = request_irq(client->irq, isl29023_irq_handler, 0, + client->dev.driver->name, data); + if (err < 0) { + dev_err(&client->dev, "failed to register irq %d!\n", + client->irq); + goto exit_free_input; + } + + data->workqueue = create_singlethread_workqueue("isl29023"); + INIT_WORK(&data->work, isl29023_work); + if (data->workqueue == NULL) { + dev_err(&client->dev, "couldn't create workqueue\n"); + err = -ENOMEM; + goto exit_free_interrupt; + } + + dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION); + return 0; + +exit_free_interrupt: + free_irq(client->irq, data); +exit_free_input: + input_free_device(input_dev); +exit_kfree: + kfree(data); + return err; +} + +static int isl29023_remove(struct i2c_client *client) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + + cancel_work_sync(&data->work); + destroy_workqueue(data->workqueue); + free_irq(client->irq, data); + input_unregister_device(data->input); + input_free_device(data->input); + sysfs_remove_group(&client->dev.kobj, &isl29023_attr_group); + isl29023_set_power_state(client, 0); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +#ifdef CONFIG_PM +static int isl29023_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct isl29023_data *data = i2c_get_clientdata(client); + + data->mode_before_suspend = isl29023_get_mode(client); + return isl29023_set_power_state(client, ISL29023_PD_MODE); +} + +static int isl29023_resume(struct i2c_client *client) +{ + int i; + struct isl29023_data *data = i2c_get_clientdata(client); + + /* restore registers from cache */ + for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) + if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i])) + return -EIO; + + return isl29023_set_mode(client, data->mode_before_suspend); +} + +#else +#define isl29023_suspend NULL +#define isl29023_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id isl29023_id[] = { + { ISL29023_DRV_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, isl29023_id); + +static struct i2c_driver isl29023_driver = { + .driver = { + .name = ISL29023_DRV_NAME, + .owner = THIS_MODULE, + }, + .suspend = isl29023_suspend, + .resume = isl29023_resume, + .probe = isl29023_probe, + .remove = isl29023_remove, + .id_table = isl29023_id, +}; + +static int __init isl29023_init(void) +{ + return i2c_add_driver(&isl29023_driver); +} + +static void __exit isl29023_exit(void) +{ + i2c_del_driver(&isl29023_driver); +} + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("ISL29023 ambient light sensor driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +module_init(isl29023_init); +module_exit(isl29023_exit); diff -Nur linux-3.10.30/drivers/input/misc/mma8450.c linux-3.10.30-cubox-i/drivers/input/misc/mma8450.c --- linux-3.10.30/drivers/input/misc/mma8450.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/misc/mma8450.c 2014-03-08 20:33:39.000000000 +0100 @@ -1,7 +1,7 @@ /* * Driver for Freescale's 3-Axis Accelerometer MMA8450 * - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. * * 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 @@ -51,6 +51,8 @@ #define MMA8450_CTRL_REG1 0x38 #define MMA8450_CTRL_REG2 0x39 +#define MMA8450_ID 0xC6 +#define MMA8450_WHO_AM_I 0x0F /* mma8450 status */ struct mma8450 { @@ -172,7 +174,25 @@ { struct input_polled_dev *idev; struct mma8450 *m; - int err; + int err, client_id; + struct i2c_adapter *adapter = NULL; + + adapter = to_i2c_adapter(c->dev.parent); + err = i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA); + if (!err) + goto err_out; + + client_id = i2c_smbus_read_byte_data(c, MMA8450_WHO_AM_I); + + if (MMA8450_ID != client_id) { + dev_err(&c->dev, + "read chip ID 0x%x is not equal to 0x%x!\n", client_id, + MMA8450_ID); + err = -EINVAL; + goto err_out; + } m = kzalloc(sizeof(struct mma8450), GFP_KERNEL); idev = input_allocate_polled_device(); @@ -209,6 +229,7 @@ err_free_mem: input_free_polled_device(idev); kfree(m); +err_out: return err; } diff -Nur linux-3.10.30/drivers/input/touchscreen/Kconfig linux-3.10.30-cubox-i/drivers/input/touchscreen/Kconfig --- linux-3.10.30/drivers/input/touchscreen/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/touchscreen/Kconfig 2014-03-08 20:33:40.000000000 +0100 @@ -235,6 +235,27 @@ To compile this driver as a module, choose M here: the module will be called egalax_ts. +config TOUCHSCREEN_EGALAX_SINGLE_TOUCH + bool "EETI eGalax touchscreen as single-touch" + default N + depends on TOUCHSCREEN_EGALAX + help + If you say yes here you get single-touch touchscreen support + on the eGalax I2C controller. + If you say "no", you'll get the normal multi-touch + +config TOUCHSCREEN_ELAN + tristate "ELAN touchscreen input driver" + depends on I2C + help + Say Y here if you have an I2C ELAN touchscreen + attached. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called elan-touch. + config TOUCHSCREEN_FUJITSU tristate "Fujitsu serial touchscreen" select SERIO diff -Nur linux-3.10.30/drivers/input/touchscreen/Makefile linux-3.10.30-cubox-i/drivers/input/touchscreen/Makefile --- linux-3.10.30/drivers/input/touchscreen/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/touchscreen/Makefile 2014-03-08 20:33:40.000000000 +0100 @@ -27,6 +27,7 @@ obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o +obj-$(CONFIG_TOUCHSCREEN_ELAN) += elan_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o diff -Nur linux-3.10.30/drivers/input/touchscreen/egalax_ts.c linux-3.10.30-cubox-i/drivers/input/touchscreen/egalax_ts.c --- linux-3.10.30/drivers/input/touchscreen/egalax_ts.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/touchscreen/egalax_ts.c 2014-03-08 20:33:40.000000000 +0100 @@ -1,7 +1,7 @@ /* * Driver for EETI eGalax Multiple Touch Controller * - * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. * * based on max11801_ts.c * @@ -35,7 +35,7 @@ * which can only report one point at a given time. * This driver will ignore events in this mode. */ -#define REPORT_MODE_MOUSE 0x1 +#define REPORT_MODE_SINGLE 0x1 /* * Vendor Mode: this mode is used to transfer some vendor specific * messages. @@ -47,6 +47,8 @@ #define MAX_SUPPORT_POINTS 5 +#define EVENT_MODE 0 +#define EVENT_STATUS 1 #define EVENT_VALID_OFFSET 7 #define EVENT_VALID_MASK (0x1 << EVENT_VALID_OFFSET) #define EVENT_ID_OFFSET 2 @@ -56,13 +58,21 @@ #define MAX_I2C_DATA_LEN 10 -#define EGALAX_MAX_X 32760 -#define EGALAX_MAX_Y 32760 +#define EGALAX_MAX_X 32767 +#define EGALAX_MAX_Y 32767 #define EGALAX_MAX_TRIES 100 +struct egalax_pointer { + bool valid; + bool status; + u16 x; + u16 y; +}; + struct egalax_ts { struct i2c_client *client; struct input_dev *input_dev; + struct egalax_pointer events[MAX_SUPPORT_POINTS]; }; static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) @@ -70,8 +80,9 @@ struct egalax_ts *ts = dev_id; struct input_dev *input_dev = ts->input_dev; struct i2c_client *client = ts->client; + struct egalax_pointer *events = ts->events; u8 buf[MAX_I2C_DATA_LEN]; - int id, ret, x, y, z; + int i, id, ret, x, y; int tries = 0; bool down, valid; u8 state; @@ -83,15 +94,38 @@ if (ret < 0) return IRQ_HANDLED; - if (buf[0] != REPORT_MODE_MTTOUCH) { - /* ignore mouse events and vendor events */ + dev_dbg(&client->dev, "recv ret:%d", ret); + for (i = 0; i < MAX_I2C_DATA_LEN; i++) + dev_dbg(&client->dev, " %x ", buf[i]); + + if (buf[0] != REPORT_MODE_VENDOR + && buf[0] != REPORT_MODE_SINGLE + && buf[0] != REPORT_MODE_MTTOUCH) { + /* invalid point */ + return IRQ_HANDLED; + } + + if (buf[0] == REPORT_MODE_VENDOR) { + dev_dbg(&client->dev, "vendor message, ignored\n"); return IRQ_HANDLED; } state = buf[1]; x = (buf[3] << 8) | buf[2]; y = (buf[5] << 8) | buf[4]; - z = (buf[7] << 8) | buf[6]; + + /* Currently, the panel Freescale using on SMD board _NOT_ + * support single pointer mode. All event are going to + * multiple pointer mode. Add single pointer mode according + * to EETI eGalax I2C programming manual. + */ + if (buf[0] == REPORT_MODE_SINGLE) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_key(input_dev, BTN_TOUCH, !!state); + input_sync(input_dev); + return IRQ_HANDLED; + } valid = state & EVENT_VALID_MASK; id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET; @@ -102,19 +136,50 @@ return IRQ_HANDLED; } - input_mt_slot(input_dev, id); - input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down); - - dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d", - down ? "down" : "up", id, x, y, z); - if (down) { - 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, z); + events[id].valid = valid; + events[id].status = down; + events[id].x = x; + events[id].y = y; + +#ifdef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_event(ts->input_dev, EV_KEY, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_PRESSURE, 1); +#endif + } else { + dev_dbg(&client->dev, "release id:%d\n", id); + events[id].valid = 0; + events[id].status = 0; +#ifdef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); +#else + input_report_abs(input_dev, ABS_MT_TRACKING_ID, id); + input_event(input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 0); + input_mt_sync(input_dev); +#endif } - input_mt_report_pointer_emulation(input_dev, true); +#ifndef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH + /* report all pointers */ + for (i = 0; i < MAX_SUPPORT_POINTS; i++) { + if (!events[i].valid) + continue; + dev_dbg(&client->dev, "report id:%d valid:%d x:%d y:%d", + i, valid, x, y); + input_report_abs(input_dev, + ABS_MT_TRACKING_ID, i); + input_report_abs(input_dev, + ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(input_dev, + ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, + ABS_MT_POSITION_Y, events[i].y); + input_mt_sync(input_dev); + } +#endif input_sync(input_dev); return IRQ_HANDLED; @@ -203,22 +268,34 @@ goto err_free_dev; } - input_dev->name = "EETI eGalax Touch Screen"; + input_dev->name = "eGalax Touch Screen"; + input_dev->phys = "I2C", input_dev->id.bustype = BUS_I2C; + input_dev->id.vendor = 0x0EEF; + input_dev->id.product = 0x0020; + input_dev->id.version = 0x0001; input_dev->dev.parent = &client->dev; __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); - + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_PRESSURE, input_dev->absbit); input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0); - input_set_abs_params(input_dev, - ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0); - input_set_abs_params(input_dev, - ABS_MT_POSITION_Y, 0, EGALAX_MAX_Y, 0, 0); - input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1, 0, 0); +#ifndef CONFIG_TOUCHSCREEN_EGALAX_SINGLE_TOUCH + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, EGALAX_MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, EGALAX_MAX_Y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + MAX_SUPPORT_POINTS, 0, 0); +#endif input_set_drvdata(input_dev, ts); error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt, diff -Nur linux-3.10.30/drivers/input/touchscreen/elan_ts.c linux-3.10.30-cubox-i/drivers/input/touchscreen/elan_ts.c --- linux-3.10.30/drivers/input/touchscreen/elan_ts.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/touchscreen/elan_ts.c 2014-03-08 20:33:40.000000000 +0100 @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2007-2008 HTC Corporation. + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * This driver is adapted from elan8232_i2c.c written by Shan-Fu Chiou + * and Jay Tu . + * This driver is also adapted from the ELAN Touch Screen driver + * written by Stanley Zeng + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char ELAN_TS_NAME[] = "elan-touch"; + +#define ELAN_TS_X_MAX 1088 +#define ELAN_TS_Y_MAX 768 +#define ELAN_USER_X_MAX 800 +#define ELAN_USER_Y_MAX 600 +#define IDX_PACKET_SIZE 8 + +enum { + hello_packet = 0x55, + idx_coordinate_packet = 0x5a, +}; + +enum { + idx_finger_state = 7, +}; + +static struct workqueue_struct *elan_wq; + +static struct elan_data { + int intr_gpio; + int use_irq; + struct hrtimer timer; + struct work_struct work; + struct i2c_client *client; + struct input_dev *input; + wait_queue_head_t wait; +} elan_touch_data; + +/*--------------------------------------------------------------*/ +static int elan_touch_detect_int_level(void) +{ + int v; + v = gpio_get_value(elan_touch_data.intr_gpio); + + return v; +} + +static int __elan_touch_poll(struct i2c_client *client) +{ + int status = 0, retry = 20; + + do { + status = elan_touch_detect_int_level(); + retry--; + mdelay(20); + } while (status == 1 && retry > 0); + + return (status == 0 ? 0 : -ETIMEDOUT); +} + +static int elan_touch_poll(struct i2c_client *client) +{ + return __elan_touch_poll(client); +} + +static int __hello_packet_handler(struct i2c_client *client) +{ + int rc; + uint8_t buf_recv[4] = { 0 }; + + rc = elan_touch_poll(client); + + if (rc < 0) + return -EINVAL; + + rc = i2c_master_recv(client, buf_recv, 4); + + if (rc != 4) { + return rc; + } else { + int i; + pr_info("hello packet: [0x%02x 0x%02x 0x%02x 0x%02x]\n", + buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]); + + for (i = 0; i < 4; i++) + if (buf_recv[i] != hello_packet) + return -EINVAL; + } + + return 0; +} + +static inline int elan_touch_parse_xy(uint8_t *data, uint16_t *x, + uint16_t *y) +{ + *x = (data[0] & 0xf0); + *x <<= 4; + *x |= data[1]; + if (*x >= ELAN_TS_X_MAX) + *x = ELAN_TS_X_MAX; + *x = ((((ELAN_TS_X_MAX - + *x) * 1000) / ELAN_TS_X_MAX) * ELAN_USER_X_MAX) / 1000; + + *y = (data[0] & 0x0f); + *y <<= 8; + *y |= data[2]; + if (*y >= ELAN_TS_Y_MAX) + *y = ELAN_TS_Y_MAX; + *y = ((((ELAN_TS_Y_MAX - + *y) * 1000) / ELAN_TS_Y_MAX) * ELAN_USER_Y_MAX) / 1000; + + return 0; +} + +/* __elan_touch_init -- hand shaking with touch panel + * + * 1.recv hello packet + */ +static int __elan_touch_init(struct i2c_client *client) +{ + int rc; + rc = __hello_packet_handler(client); + if (rc < 0) + goto hand_shake_failed; + +hand_shake_failed: + return rc; +} + +static int elan_touch_recv_data(struct i2c_client *client, uint8_t * buf) +{ + int rc, bytes_to_recv = IDX_PACKET_SIZE; + + if (buf == NULL) + return -EINVAL; + + memset(buf, 0, bytes_to_recv); + rc = i2c_master_recv(client, buf, bytes_to_recv); + if (rc != bytes_to_recv) + return -EINVAL; + + return rc; +} + +static void elan_touch_report_data(struct i2c_client *client, uint8_t * buf) +{ + switch (buf[0]) { + case idx_coordinate_packet: + { + uint16_t x1, x2, y1, y2; + uint8_t finger_stat; + + finger_stat = (buf[idx_finger_state] & 0x06) >> 1; + + if (finger_stat == 0) { + input_report_key(elan_touch_data.input, BTN_TOUCH, 0); + input_report_key(elan_touch_data.input, BTN_2, 0); + } else if (finger_stat == 1) { + elan_touch_parse_xy(&buf[1], &x1, &y1); + input_report_abs(elan_touch_data.input, ABS_X, x1); + input_report_abs(elan_touch_data.input, ABS_Y, y1); + input_report_key(elan_touch_data.input, BTN_TOUCH, 1); + input_report_key(elan_touch_data.input, BTN_2, 0); + } else if (finger_stat == 2) { + elan_touch_parse_xy(&buf[1], &x1, &y1); + input_report_abs(elan_touch_data.input, ABS_X, x1); + input_report_abs(elan_touch_data.input, ABS_Y, y1); + input_report_key(elan_touch_data.input, BTN_TOUCH, 1); + elan_touch_parse_xy(&buf[4], &x2, &y2); + input_report_abs(elan_touch_data.input, ABS_HAT0X, x2); + input_report_abs(elan_touch_data.input, ABS_HAT0Y, y2); + input_report_key(elan_touch_data.input, BTN_2, 1); + } + input_sync(elan_touch_data.input); + break; + } + + default: + break; + } +} + +static void elan_touch_work_func(struct work_struct *work) +{ + int rc; + uint8_t buf[IDX_PACKET_SIZE] = { 0 }; + struct i2c_client *client = elan_touch_data.client; + + if (elan_touch_detect_int_level()) + return; + + rc = elan_touch_recv_data(client, buf); + if (rc < 0) + return; + + elan_touch_report_data(client, buf); +} + +static irqreturn_t elan_touch_ts_interrupt(int irq, void *dev_id) +{ + queue_work(elan_wq, &elan_touch_data.work); + + return IRQ_HANDLED; +} + +static enum hrtimer_restart elan_touch_timer_func(struct hrtimer *timer) +{ + queue_work(elan_wq, &elan_touch_data.work); + hrtimer_start(&elan_touch_data.timer, ktime_set(0, 12500000), + HRTIMER_MODE_REL); + + return HRTIMER_NORESTART; +} + +static int elan_touch_register_interrupt(struct i2c_client *client) +{ + int err = 0; + + if (client->irq) { + elan_touch_data.use_irq = 1; + err = + request_irq(client->irq, elan_touch_ts_interrupt, + IRQF_TRIGGER_FALLING, ELAN_TS_NAME, + &elan_touch_data); + + if (err < 0) { + pr_info("%s(%s): Can't allocate irq %d\n", __FILE__, + __func__, client->irq); + elan_touch_data.use_irq = 0; + } + } + + if (!elan_touch_data.use_irq) { + hrtimer_init(&elan_touch_data.timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + elan_touch_data.timer.function = elan_touch_timer_func; + hrtimer_start(&elan_touch_data.timer, ktime_set(1, 0), + HRTIMER_MODE_REL); + } + + pr_info("elan ts starts in %s mode.\n", + elan_touch_data.use_irq == 1 ? "interrupt" : "polling"); + + return 0; +} + +static int elan_touch_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device_node *np = client->dev.of_node; + int gpio_elan_cs, gpio_elan_rst, err = 0; + + if (!np) + return -ENODEV; + + elan_touch_data.intr_gpio = of_get_named_gpio(np, "gpio_intr", 0); + if (!gpio_is_valid(elan_touch_data.intr_gpio)) + return -ENODEV; + + err = devm_gpio_request_one(&client->dev, elan_touch_data.intr_gpio, + GPIOF_IN, "gpio_elan_intr"); + if (err < 0) { + dev_err(&client->dev, + "request gpio failed: %d\n", err); + return err; + } + + /* elan touch init */ + gpio_elan_cs = of_get_named_gpio(np, "gpio_elan_cs", 0); + if (!gpio_is_valid(gpio_elan_cs)) + return -ENODEV; + + err = devm_gpio_request_one(&client->dev, gpio_elan_cs, + GPIOF_OUT_INIT_HIGH, "gpio_elan_cs"); + if (err < 0) { + dev_err(&client->dev, + "request gpio failed: %d\n", err); + return err; + } + gpio_set_value(gpio_elan_cs, 0); + + gpio_elan_rst = of_get_named_gpio(np, "gpio_elan_rst", 0); + if (!gpio_is_valid(gpio_elan_rst)) + return -ENODEV; + + err = devm_gpio_request_one(&client->dev, gpio_elan_rst, + GPIOF_OUT_INIT_HIGH, "gpio_elan_rst"); + if (err < 0) { + dev_err(&client->dev, + "request gpio failed: %d\n", err); + return err; + } + gpio_set_value(gpio_elan_rst, 0); + msleep(10); + gpio_set_value(gpio_elan_rst, 1); + + gpio_set_value(gpio_elan_cs, 1); + msleep(100); + + elan_wq = create_singlethread_workqueue("elan_wq"); + if (!elan_wq) { + err = -ENOMEM; + goto fail; + } + + elan_touch_data.client = client; + strlcpy(client->name, ELAN_TS_NAME, I2C_NAME_SIZE); + + INIT_WORK(&elan_touch_data.work, elan_touch_work_func); + + elan_touch_data.input = input_allocate_device(); + if (elan_touch_data.input == NULL) { + err = -ENOMEM; + goto fail; + } + + err = __elan_touch_init(client); + if (err < 0) { + dev_err(&client->dev, "elan - Read Hello Packet Failed\n"); + goto fail; + } + + elan_touch_data.input->name = ELAN_TS_NAME; + elan_touch_data.input->id.bustype = BUS_I2C; + + set_bit(EV_SYN, elan_touch_data.input->evbit); + + set_bit(EV_KEY, elan_touch_data.input->evbit); + set_bit(BTN_TOUCH, elan_touch_data.input->keybit); + set_bit(BTN_2, elan_touch_data.input->keybit); + + set_bit(EV_ABS, elan_touch_data.input->evbit); + set_bit(ABS_X, elan_touch_data.input->absbit); + set_bit(ABS_Y, elan_touch_data.input->absbit); + set_bit(ABS_HAT0X, elan_touch_data.input->absbit); + set_bit(ABS_HAT0Y, elan_touch_data.input->absbit); + + input_set_abs_params(elan_touch_data.input, ABS_X, 0, ELAN_USER_X_MAX, + 0, 0); + input_set_abs_params(elan_touch_data.input, ABS_Y, 0, ELAN_USER_Y_MAX, + 0, 0); + input_set_abs_params(elan_touch_data.input, ABS_HAT0X, 0, + ELAN_USER_X_MAX, 0, 0); + input_set_abs_params(elan_touch_data.input, ABS_HAT0Y, 0, + ELAN_USER_Y_MAX, 0, 0); + + err = input_register_device(elan_touch_data.input); + if (err < 0) + goto fail; + + elan_touch_register_interrupt(elan_touch_data.client); + + return 0; + +fail: + input_free_device(elan_touch_data.input); + if (elan_wq) + destroy_workqueue(elan_wq); + return err; +} + +static int elan_touch_remove(struct i2c_client *client) +{ + if (elan_wq) + destroy_workqueue(elan_wq); + + input_unregister_device(elan_touch_data.input); + + if (elan_touch_data.use_irq) + free_irq(client->irq, client); + else + hrtimer_cancel(&elan_touch_data.timer); + return 0; +} + +/* -------------------------------------------------------------------- */ +static const struct i2c_device_id elan_touch_id[] = { + {"elan-touch", 0}, + {} +}; + +static const struct of_device_id elan_dt_ids[] = { + { + .compatible = "elan,elan-touch", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, elan_dt_ids); + +static int elan_suspend(struct device *dev) +{ + return 0; +} + +static int elan_resume(struct device *dev) +{ + uint8_t buf[IDX_PACKET_SIZE] = { 0 }; + + if (0 == elan_touch_detect_int_level()) { + dev_dbg(dev, "Got touch during suspend period.\n"); + /* + * if touch screen during suspend, recv and drop the + * data, then touch interrupt pin will return high after + * receving data. + */ + elan_touch_recv_data(elan_touch_data.client, buf); + } + + return 0; +} + +static const struct dev_pm_ops elan_dev_pm_ops = { + .suspend = elan_suspend, + .resume = elan_resume, +}; + +static struct i2c_driver elan_touch_driver = { + .probe = elan_touch_probe, + .remove = elan_touch_remove, + .id_table = elan_touch_id, + .driver = { + .name = "elan-touch", + .owner = THIS_MODULE, + .of_match_table = elan_dt_ids, +#ifdef CONFIG_PM + .pm = &elan_dev_pm_ops, +#endif + }, +}; + +static int __init elan_touch_init(void) +{ + return i2c_add_driver(&elan_touch_driver); +} + +static void __exit elan_touch_exit(void) +{ + i2c_del_driver(&elan_touch_driver); +} + +module_init(elan_touch_init); +module_exit(elan_touch_exit); + +MODULE_AUTHOR("Stanley Zeng "); +MODULE_DESCRIPTION("ELAN Touch Screen driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/input/touchscreen/max11801_ts.c linux-3.10.30-cubox-i/drivers/input/touchscreen/max11801_ts.c --- linux-3.10.30/drivers/input/touchscreen/max11801_ts.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/input/touchscreen/max11801_ts.c 2014-03-08 20:33:40.000000000 +0100 @@ -2,7 +2,7 @@ * Driver for MAXI MAX11801 - A Resistive touch screen controller with * i2c interface * - * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. * Author: Zhang Jiejing * * Based on mcs5000_ts.c @@ -39,6 +39,10 @@ #include #include #include +#include +#include +#include +#include /* Register Address define */ #define GENERNAL_STATUS_REG 0x00 @@ -54,13 +58,30 @@ #define AUX_MESURE_CONF_REG 0x0a #define OP_MODE_CONF_REG 0x0b +#define Panel_Setup_X (0x69 << 1) +#define Panel_Setup_Y (0x6b << 1) + +#define XY_combined_measurement (0x70 << 1) +#define X_measurement (0x78 << 1) +#define Y_measurement (0x7a << 1) +#define AUX_measurement (0x76 << 1) + /* FIFO is found only in max11800 and max11801 */ #define FIFO_RD_CMD (0x50 << 1) #define MAX11801_FIFO_INT (1 << 2) #define MAX11801_FIFO_OVERFLOW (1 << 3) +#define MAX11801_EDGE_INT (1 << 1) + +#define FIFO_RD_X_MSB (0x52 << 1) +#define FIFO_RD_X_LSB (0x53 << 1) +#define FIFO_RD_Y_MSB (0x54 << 1) +#define FIFO_RD_Y_LSB (0x55 << 1) +#define FIFO_RD_AUX_MSB (0x5a << 1) +#define FIFO_RD_AUX_LSB (0x5b << 1) #define XY_BUFSIZE 4 #define XY_BUF_OFFSET 4 +#define AUX_BUFSIZE 2 #define MAX11801_MAX_X 0xfff #define MAX11801_MAX_Y 0xfff @@ -85,6 +106,64 @@ struct input_dev *input_dev; }; +static struct i2c_client *max11801_client; +static unsigned int max11801_workmode; +static u8 aux_buf[AUX_BUFSIZE]; + +static int max11801_dcm_write_command(struct i2c_client *client, int command) +{ + return i2c_smbus_write_byte(client, command); +} + +static u32 max11801_dcm_sample_aux(struct i2c_client *client) +{ + int ret; + int aux = 0; + u32 sample_data; + + /* AUX_measurement */ + max11801_dcm_write_command(client, AUX_measurement); + mdelay(5); + ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_MSB, + 1, &aux_buf[0]); + if (ret < 1) { + dev_err(&client->dev, "FIFO_RD_AUX_MSB read fails\n"); + return ret; + } + mdelay(5); + ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_LSB, + 1, &aux_buf[1]); + if (ret < 1) { + dev_err(&client->dev, "FIFO_RD_AUX_LSB read fails\n"); + return ret; + } + + aux = (aux_buf[0] << 4) + (aux_buf[1] >> 4); + /* + * voltage = (9170*aux)/7371; + * voltage is (26.2*3150*aux)/(16.2*0xFFF) + * V(aux)=3150*sample/0xFFF,V(battery)=212*V(aux)/81 + * sample_data = (14840*aux)/7371-1541; + */ + sample_data = (14840 * aux) / 7371; + + return sample_data; +} + +u32 max11801_read_adc(void) +{ + u32 adc_data; + + if (!max11801_client) { + pr_err("FAIL max11801_client not initialize\n"); + return -1; + } + adc_data = max11801_dcm_sample_aux(max11801_client); + + return adc_data; +} +EXPORT_SYMBOL_GPL(max11801_read_adc); + static u8 read_register(struct i2c_client *client, int addr) { /* XXX: The chip ignores LSB of register address */ @@ -105,29 +184,62 @@ u8 buf[XY_BUFSIZE]; int x = -1; int y = -1; + u8 command = FIFO_RD_X_MSB; status = read_register(data->client, GENERNAL_STATUS_REG); - - if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) { + if ((!max11801_workmode && (status & (MAX11801_FIFO_INT | + MAX11801_FIFO_OVERFLOW))) || (max11801_workmode && (status & + MAX11801_EDGE_INT))) { status = read_register(data->client, GENERNAL_STATUS_REG); + if (!max11801_workmode) { + /* ACM mode */ + ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD, + XY_BUFSIZE, buf); + /* + * We should get 4 bytes buffer that contains X,Y + * and event tag + */ + if (ret < XY_BUFSIZE) + goto out; + } else { + /* DCM mode */ + /* X = panel setup */ + max11801_dcm_write_command(client, Panel_Setup_X); + /* X_measurement */ + max11801_dcm_write_command(client, X_measurement); + for (i = 0; i < 2; i++) { + ret = i2c_smbus_read_i2c_block_data(client, + command, 1, &buf[i]); + if (ret < 1) + goto out; + + command = FIFO_RD_X_LSB; + } + + /* Y = panel setup */ + max11801_dcm_write_command(client, Panel_Setup_Y); + /* Y_measurement */ + max11801_dcm_write_command(client, Y_measurement); + command = FIFO_RD_Y_MSB; + for (i = 2; i < XY_BUFSIZE; i++) { + ret = i2c_smbus_read_i2c_block_data(client, + command, 1, &buf[i]); + if (ret < 1) + goto out; - ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD, - XY_BUFSIZE, buf); - - /* - * We should get 4 bytes buffer that contains X,Y - * and event tag - */ - if (ret < XY_BUFSIZE) - goto out; + command = FIFO_RD_Y_LSB; + } + } for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) { - if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG) + if ((buf[i + 1] & MEASURE_TAG_MASK) == + MEASURE_X_TAG) x = (buf[i] << XY_BUF_OFFSET) + - (buf[i + 1] >> XY_BUF_OFFSET); - else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG) + (buf[i + 1] >> XY_BUF_OFFSET); + else if ((buf[i + 1] & MEASURE_TAG_MASK) == + MEASURE_Y_TAG) y = (buf[i] << XY_BUF_OFFSET) + - (buf[i + 1] >> XY_BUF_OFFSET); + (buf[i + 1] >> XY_BUF_OFFSET); } if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK)) @@ -138,18 +250,17 @@ /* fall through */ case EVENT_MIDDLE: input_report_abs(data->input_dev, ABS_X, x); + y = MAX11801_MAX_Y - y; /* Calibration */ input_report_abs(data->input_dev, ABS_Y, y); input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1); input_sync(data->input_dev); break; - case EVENT_RELEASE: input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0); input_sync(data->input_dev); break; - case EVENT_FIFO_END: - break; + break; } } out: @@ -160,18 +271,37 @@ { struct i2c_client *client = data->client; - /* Average X,Y, take 16 samples, average eight media sample */ + max11801_client = client; + /* Average X,Y, take 16 samples average eight media sample */ max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff); /* X,Y panel setup time set to 20us */ max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11); - /* Rough pullup time (2uS), Fine pullup time (10us) */ + /* Rough pullup time (2uS), Fine pullup time (10us) */ max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10); - /* Auto mode init period = 5ms , scan period = 5ms*/ + /* Auto mode init period = 5ms, scan period = 5ms */ max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa); /* Aperture X,Y set to +- 4LSB */ max11801_write_reg(client, APERTURE_CONF_REG, 0x33); - /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */ - max11801_write_reg(client, OP_MODE_CONF_REG, 0x36); + /* + * Enable Power, enable Automode, enable Aperture, + * enable Average X,Y + */ + if (!max11801_workmode) + max11801_write_reg(client, OP_MODE_CONF_REG, 0x36); + else { + max11801_write_reg(client, OP_MODE_CONF_REG, 0x16); + /* + * Delay initial=1ms, Sampling time 2us + * Averaging sample depth 2 + * samples, Resolution 12bit + */ + max11801_write_reg(client, AUX_MESURE_CONF_REG, 0x76); + /* + * Use edge interrupt with + * direct conversion mode + */ + max11801_write_reg(client, GENERNAL_CONF_REG, 0xf3); + } } static int max11801_ts_probe(struct i2c_client *client, @@ -180,6 +310,7 @@ struct max11801_data *data; struct input_dev *input_dev; int error; + struct device_node *of_node = client->dev.of_node; data = kzalloc(sizeof(struct max11801_data), GFP_KERNEL); input_dev = input_allocate_device(); @@ -203,11 +334,14 @@ input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0); input_set_drvdata(input_dev, data); + if (of_property_read_u32(of_node, "work-mode", &max11801_workmode)) + max11801_workmode = *(int *)(client->dev).platform_data; + max11801_ts_phy_init(data); error = request_threaded_irq(client->irq, NULL, max11801_ts_interrupt, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "max11801_ts", data); + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "max11801_ts", data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); goto err_free_mem; @@ -245,10 +379,17 @@ }; MODULE_DEVICE_TABLE(i2c, max11801_ts_id); +static const struct of_device_id max11801_ts_dt_ids[] = { + { .compatible = "maxim,max11801", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, max11801_ts_dt_ids); + static struct i2c_driver max11801_ts_driver = { .driver = { .name = "max11801_ts", .owner = THIS_MODULE, + .of_match_table = max11801_ts_dt_ids, }, .id_table = max11801_ts_id, .probe = max11801_ts_probe, diff -Nur linux-3.10.30/drivers/irqchip/irq-gic.c linux-3.10.30-cubox-i/drivers/irqchip/irq-gic.c --- linux-3.10.30/drivers/irqchip/irq-gic.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/irqchip/irq-gic.c 2014-03-08 20:33:41.000000000 +0100 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -253,10 +254,9 @@ if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids) return -EINVAL; + raw_spin_lock(&irq_controller_lock); mask = 0xff << shift; bit = gic_cpu_map[cpu] << shift; - - raw_spin_lock(&irq_controller_lock); val = readl_relaxed(reg) & ~mask; writel_relaxed(val | bit, reg); raw_spin_unlock(&irq_controller_lock); @@ -453,6 +453,12 @@ writel_relaxed(1, base + GIC_CPU_CTRL); } +void gic_cpu_if_down(void) +{ + void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]); + writel_relaxed(0, cpu_base + GIC_CPU_CTRL); +} + #ifdef CONFIG_CPU_PM /* * Saves the GIC distributor registers during suspend or idle. Must be called @@ -646,11 +652,15 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) { int cpu; - unsigned long map = 0; + unsigned long flags, map = 0; + + raw_spin_lock_irqsave(&irq_controller_lock, flags); /* Convert our logical CPU mask into a physical one. */ - for_each_cpu(cpu, mask) + for_each_cpu(cpu, mask) { + trace_arm_ipi_send(irq, cpu); map |= gic_cpu_map[cpu]; + } /* * Ensure that stores to Normal memory are visible to the @@ -660,7 +670,143 @@ /* this always happens on GIC0 */ writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); + + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); +} +#endif + +#ifdef CONFIG_BL_SWITCHER +/* + * gic_send_sgi - send a SGI directly to given CPU interface number + * + * cpu_id: the ID for the destination CPU interface + * irq: the IPI number to send a SGI for + */ +void gic_send_sgi(unsigned int cpu_id, unsigned int irq) +{ + BUG_ON(cpu_id >= NR_GIC_CPU_IF); + cpu_id = 1 << cpu_id; + /* this always happens on GIC0 */ + writel_relaxed((cpu_id << 16) | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); +} + +/* + * gic_get_cpu_id - get the CPU interface ID for the specified CPU + * + * @cpu: the logical CPU number to get the GIC ID for. + * + * Return the CPU interface ID for the given logical CPU number, + * or -1 if the CPU number is too large or the interface ID is + * unknown (more than one bit set). + */ +int gic_get_cpu_id(unsigned int cpu) +{ + unsigned int cpu_bit; + + if (cpu >= NR_GIC_CPU_IF) + return -1; + cpu_bit = gic_cpu_map[cpu]; + if (cpu_bit & (cpu_bit - 1)) + return -1; + return __ffs(cpu_bit); +} + +/* + * gic_migrate_target - migrate IRQs to another PU interface + * + * @new_cpu_id: the CPU target ID to migrate IRQs to + * + * Migrate all peripheral interrupts with a target matching the current CPU + * to the interface corresponding to @new_cpu_id. The CPU interface mapping + * is also updated. Targets to other CPU interfaces are unchanged. + * This must be called with IRQs locally disabled. + */ +void gic_migrate_target(unsigned int new_cpu_id) +{ + unsigned int old_cpu_id, gic_irqs, gic_nr = 0; + void __iomem *dist_base; + int i, ror_val, cpu = smp_processor_id(); + u32 val, old_mask, active_mask; + + if (gic_nr >= MAX_GIC_NR) + BUG(); + + dist_base = gic_data_dist_base(&gic_data[gic_nr]); + if (!dist_base) + return; + gic_irqs = gic_data[gic_nr].gic_irqs; + + old_cpu_id = __ffs(gic_cpu_map[cpu]); + old_mask = 0x01010101 << old_cpu_id; + ror_val = (old_cpu_id - new_cpu_id) & 31; + + raw_spin_lock(&irq_controller_lock); + + gic_cpu_map[cpu] = 1 << new_cpu_id; + + for (i = 8; i < DIV_ROUND_UP(gic_irqs, 4); i++) { + val = readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); + active_mask = val & old_mask; + if (active_mask) { + val &= ~active_mask; + val |= ror32(active_mask, ror_val); + writel_relaxed(val, dist_base + GIC_DIST_TARGET + i * 4); + } + } + + raw_spin_unlock(&irq_controller_lock); + + /* + * Now let's migrate and clear any potential SGIs that might be + * pending for us (old_cpu_id). Since GIC_DIST_SGI_PENDING_SET + * is a banked register, we can only forward the SGI using + * GIC_DIST_SOFTINT. The original SGI source is lost but Linux + * doesn't use that information anyway. + * + * For the same reason we do not adjust SGI source information + * for previously sent SGIs by us to other CPUs either. + */ + for (i = 0; i < 16; i += 4) { + int j; + val = readl_relaxed(dist_base + GIC_DIST_SGI_PENDING_SET + i); + if (!val) + continue; + writel_relaxed(val, dist_base + GIC_DIST_SGI_PENDING_CLEAR + i); + for (j = i; j < i + 4; j++) { + if (val & 0xff) + writel_relaxed((1 << (new_cpu_id + 16)) | j, + dist_base + GIC_DIST_SOFTINT); + val >>= 8; + } + } } + +/* + * gic_get_sgir_physaddr - get the physical address for the SGI register + * + * REturn the physical address of the SGI register to be used + * by some early assembly code when the kernel is not yet available. + */ +static unsigned long gic_dist_physaddr; + +unsigned long gic_get_sgir_physaddr(void) +{ + if (!gic_dist_physaddr) + return 0; + return gic_dist_physaddr + GIC_DIST_SOFTINT; +} + +void __init gic_init_physaddr(struct device_node *node) +{ + struct resource res; + if (of_address_to_resource(node, 0, &res) == 0) { + gic_dist_physaddr = res.start; + pr_info("GIC physical location is %#lx\n", gic_dist_physaddr); + } +} + +#else +#define gic_init_physaddr(node) do { } while(0) #endif static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, @@ -844,6 +990,8 @@ percpu_offset = 0; gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node); + if (!gic_cnt) + gic_init_physaddr(node); if (parent) { irq = irq_of_parse_and_map(node, 0); diff -Nur linux-3.10.30/drivers/leds/leds-pwm.c linux-3.10.30-cubox-i/drivers/leds/leds-pwm.c --- linux-3.10.30/drivers/leds/leds-pwm.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/leds/leds-pwm.c 2014-03-08 20:33:42.000000000 +0100 @@ -67,8 +67,13 @@ container_of(led_cdev, struct led_pwm_data, cdev); unsigned int max = led_dat->cdev.max_brightness; unsigned int period = led_dat->period; + int duty; - led_dat->duty = brightness * period / max; + duty = brightness * period / max; + if (led_dat->active_low) + duty = period - duty; + + led_dat->duty = duty; if (led_dat->can_sleep) schedule_work(&led_dat->work); @@ -102,6 +107,10 @@ for_each_child_of_node(node, child) { struct led_pwm_data *led_dat = &priv->leds[priv->num_leds]; + led_dat->cdev.brightness_set = led_pwm_set; + led_dat->cdev.brightness = LED_OFF; + led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + led_dat->cdev.name = of_get_property(child, "label", NULL) ? : child->name; @@ -118,10 +127,9 @@ "linux,default-trigger", NULL); of_property_read_u32(child, "max-brightness", &led_dat->cdev.max_brightness); - - led_dat->cdev.brightness_set = led_pwm_set; - led_dat->cdev.brightness = LED_OFF; - led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; + of_property_read_u32(child, "default-brightness", + &led_dat->cdev.brightness); + led_dat->active_low = of_property_read_bool(child, "active-low"); led_dat->can_sleep = pwm_can_sleep(led_dat->pwm); if (led_dat->can_sleep) diff -Nur linux-3.10.30/drivers/media/platform/Kconfig linux-3.10.30-cubox-i/drivers/media/platform/Kconfig --- linux-3.10.30/drivers/media/platform/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/Kconfig 2014-03-08 20:33:46.000000000 +0100 @@ -121,6 +121,21 @@ To compile this driver as a module, choose M here: the module will be called s3c-camif. +config VIDEO_MXC_OUTPUT + tristate "MXC Video For Linux Video Output" + depends on VIDEO_DEV && ARCH_MXC && FB_MXC + select VIDEOBUF_DMA_CONTIG + ---help--- + This is the video4linux2 output driver based on MXC module. + +config VIDEO_MXC_CAPTURE + tristate "MXC Video For Linux Video Capture" + depends on VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE + ---help--- + This is the video4linux2 capture driver based on i.MX video-in module. + +source "drivers/media/platform/mxc/capture/Kconfig" +source "drivers/media/platform/mxc/output/Kconfig" source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" diff -Nur linux-3.10.30/drivers/media/platform/Makefile linux-3.10.30-cubox-i/drivers/media/platform/Makefile --- linux-3.10.30/drivers/media/platform/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/Makefile 2014-03-08 20:33:46.000000000 +0100 @@ -50,4 +50,7 @@ obj-$(CONFIG_ARCH_OMAP) += omap/ +obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/capture/ +obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/ + ccflags-y += -I$(srctree)/drivers/media/i2c diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/Kconfig linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/Kconfig --- linux-3.10.30/drivers/media/platform/mxc/capture/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/Kconfig 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,86 @@ +if VIDEO_MXC_CAPTURE + +menu "MXC Camera/V4L2 PRP Features support" +config VIDEO_MXC_IPU_CAMERA + bool + depends on VIDEO_MXC_CAPTURE && MXC_IPU + default y + +config VIDEO_MXC_CSI_CAMERA + tristate "CSI camera support" + depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2 + ---help--- + This is the video4linux2 capture driver based on CSI module. + +config MXC_CAMERA_OV5640 + tristate "OmniVision ov5640 camera support" + depends on !VIDEO_MXC_EMMA_CAMERA && I2C + ---help--- + If you plan to use the ov5640 Camera with your MXC system, say Y here. + +config MXC_CAMERA_OV5642 + tristate "OmniVision ov5642 camera support" + depends on !VIDEO_MXC_EMMA_CAMERA && I2C + ---help--- + If you plan to use the ov5642 Camera with your MXC system, say Y here. + +config MXC_CAMERA_OV5640_MIPI + tristate "OmniVision ov5640 camera support using mipi" + depends on !VIDEO_MXC_EMMA_CAMERA && I2C + ---help--- + If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here. + +config MXC_TVIN_ADV7180 + tristate "Analog Device adv7180 TV Decoder Input support" + depends on !VIDEO_MXC_EMMA_CAMERA && I2C + ---help--- + If you plan to use the adv7180 video decoder with your MXC system, say Y here. + +choice + prompt "Select Overlay Rounting" + default MXC_IPU_DEVICE_QUEUE_SDC + depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_SYNC_PANEL + +config MXC_IPU_DEVICE_QUEUE_SDC + tristate "Queue ipu device for overlay library" + depends on VIDEO_MXC_IPU_CAMERA + ---help--- + Use case CSI->MEM->IPU DEVICE->SDC: + Images from sensor will be frist recieved in memory,then + queue to ipu device for processing if needed, and displaying + it on synchronous display with SDC use case. + +config MXC_IPU_PRP_VF_SDC + bool "Pre-Processor VF SDC library" + depends on VIDEO_MXC_IPU_CAMERA + ---help--- + Use case PRP_VF_SDC: + Preprocessing image from smart sensor for viewfinder and + displaying it on synchronous display with SDC use case. + If SDC BG is selected, Rotation will not be supported. + CSI -> IC (PRP VF) -> MEM + MEM -> IC (ROT) -> MEM + MEM -> SDC (FG/BG) + +endchoice + +config MXC_IPU_PRP_ENC + tristate "Pre-processor Encoder library" + depends on VIDEO_MXC_IPU_CAMERA + default y + ---help--- + Use case PRP_ENC: + Preprocessing image from smart sensor for encoder. + CSI -> IC (PRP ENC) -> MEM + +config MXC_IPU_CSI_ENC + tristate "IPU CSI Encoder library" + depends on VIDEO_MXC_IPU_CAMERA + default y + ---help--- + Use case IPU_CSI_ENC: + Get raw image with CSI from smart sensor for encoder. + CSI -> MEM +endmenu + +endif diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/Makefile linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/Makefile --- linux-3.10.30/drivers/media/platform/mxc/capture/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/Makefile 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,21 @@ +obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += fsl_csi.o csi_v4l2_capture.o + +ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y) + obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc_v4l2_capture.o + obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o + obj-$(CONFIG_MXC_IPU_DEVICE_QUEUE_SDC) += ipu_fg_overlay_sdc.o ipu_bg_overlay_sdc.o + obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o + obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o +endif + +ov5640_camera-objs := ov5640.o +obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera.o + +ov5642_camera-objs := ov5642.o +obj-$(CONFIG_MXC_CAMERA_OV5642) += ov5642_camera.o + +ov5640_camera_mipi-objs := ov5640_mipi.o +obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi.o + +adv7180_tvin-objs := adv7180.o +obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/adv7180.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/adv7180.c --- linux-3.10.30/drivers/media/platform/mxc/capture/adv7180.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/adv7180.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,1344 @@ +/* + * Copyright 2005-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file adv7180.c + * + * @brief Analog Device ADV7180 video decoder functions + * + * @ingroup Camera + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" + +#define ADV7180_VOLTAGE_ANALOG 1800000 +#define ADV7180_VOLTAGE_DIGITAL_CORE 1800000 +#define ADV7180_VOLTAGE_DIGITAL_IO 3300000 +#define ADV7180_VOLTAGE_PLL 1800000 + +static struct regulator *dvddio_regulator; +static struct regulator *dvdd_regulator; +static struct regulator *avdd_regulator; +static struct regulator *pvdd_regulator; +static int pwn_gpio; + +static int adv7180_probe(struct i2c_client *adapter, + const struct i2c_device_id *id); +static int adv7180_detach(struct i2c_client *client); + +static const struct i2c_device_id adv7180_id[] = { + {"adv7180", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7180_id); + +static struct i2c_driver adv7180_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7180", + }, + .probe = adv7180_probe, + .remove = adv7180_detach, + .id_table = adv7180_id, +}; + +/*! + * Maintains the information on the current state of the sensor. + */ +struct sensor { + struct sensor_data sen; + v4l2_std_id std_id; +} adv7180_data; + + +/*! List of input video formats supported. The video formats is corresponding + * with v4l2 id in video_fmt_t + */ +typedef enum { + ADV7180_NTSC = 0, /*!< Locked on (M) NTSC video signal. */ + ADV7180_PAL, /*!< (B, G, H, I, N)PAL video signal. */ + ADV7180_NOT_LOCKED, /*!< Not locked on a signal. */ +} video_fmt_idx; + +/*! Number of video standards supported (including 'not locked' signal). */ +#define ADV7180_STD_MAX (ADV7180_PAL + 1) + +/*! Video format structure. */ +typedef struct { + int v4l2_id; /*!< Video for linux ID. */ + char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */ + u16 raw_width; /*!< Raw width. */ + u16 raw_height; /*!< Raw height. */ + u16 active_width; /*!< Active width. */ + u16 active_height; /*!< Active height. */ +} video_fmt_t; + +/*! Description of video formats supported. + * + * PAL: raw=720x625, active=720x576. + * NTSC: raw=720x525, active=720x480. + */ +static video_fmt_t video_fmts[] = { + { /*! NTSC */ + .v4l2_id = V4L2_STD_NTSC, + .name = "NTSC", + .raw_width = 720, /* SENS_FRM_WIDTH */ + .raw_height = 525, /* SENS_FRM_HEIGHT */ + .active_width = 720, /* ACT_FRM_WIDTH plus 1 */ + .active_height = 480, /* ACT_FRM_WIDTH plus 1 */ + }, + { /*! (B, G, H, I, N) PAL */ + .v4l2_id = V4L2_STD_PAL, + .name = "PAL", + .raw_width = 720, + .raw_height = 625, + .active_width = 720, + .active_height = 576, + }, + { /*! Unlocked standard */ + .v4l2_id = V4L2_STD_ALL, + .name = "Autodetect", + .raw_width = 720, + .raw_height = 625, + .active_width = 720, + .active_height = 576, + }, +}; + +/*!* Standard index of ADV7180. */ +static video_fmt_idx video_idx = ADV7180_PAL; + +/*! @brief This mutex is used to provide mutual exclusion. + * + * Create a mutex that can be used to provide mutually exclusive + * read/write access to the globally accessible data structures + * and variables that were defined above. + */ +static DEFINE_MUTEX(mutex); + +#define IF_NAME "adv7180" +#define ADV7180_INPUT_CTL 0x00 /* Input Control */ +#define ADV7180_STATUS_1 0x10 /* Status #1 */ +#define ADV7180_BRIGHTNESS 0x0a /* Brightness */ +#define ADV7180_IDENT 0x11 /* IDENT */ +#define ADV7180_VSYNC_FIELD_CTL_1 0x31 /* VSYNC Field Control #1 */ +#define ADV7180_MANUAL_WIN_CTL 0x3d /* Manual Window Control */ +#define ADV7180_SD_SATURATION_CB 0xe3 /* SD Saturation Cb */ +#define ADV7180_SD_SATURATION_CR 0xe4 /* SD Saturation Cr */ +#define ADV7180_PWR_MNG 0x0f /* Power Management */ + +/* supported controls */ +/* This hasn't been fully implemented yet. + * This is how it should work, though. */ +static struct v4l2_queryctrl adv7180_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, /* check this value */ + .maximum = 255, /* check this value */ + .step = 1, /* check this value */ + .default_value = 127, /* check this value */ + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, /* check this value */ + .maximum = 255, /* check this value */ + .step = 0x1, /* check this value */ + .default_value = 127, /* check this value */ + .flags = 0, + } +}; + +static inline void adv7180_power_down(int enable) +{ + gpio_set_value_cansleep(pwn_gpio, !enable); + msleep(2); +} + +static int adv7180_regulator_enable(struct device *dev) +{ + int ret = 0; + + dvddio_regulator = devm_regulator_get(dev, "DOVDD"); + + if (!IS_ERR(dvddio_regulator)) { + regulator_set_voltage(dvddio_regulator, + ADV7180_VOLTAGE_DIGITAL_IO, + ADV7180_VOLTAGE_DIGITAL_IO); + ret = regulator_enable(dvddio_regulator); + if (ret) { + dev_err(dev, "set io voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set io voltage ok\n"); + } + } else { + dev_warn(dev, "cannot get io voltage\n"); + } + + dvdd_regulator = devm_regulator_get(dev, "DVDD"); + if (!IS_ERR(dvdd_regulator)) { + regulator_set_voltage(dvdd_regulator, + ADV7180_VOLTAGE_DIGITAL_CORE, + ADV7180_VOLTAGE_DIGITAL_CORE); + ret = regulator_enable(dvdd_regulator); + if (ret) { + dev_err(dev, "set core voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set core voltage ok\n"); + } + } else { + dev_warn(dev, "cannot get core voltage\n"); + } + + avdd_regulator = devm_regulator_get(dev, "AVDD"); + if (!IS_ERR(avdd_regulator)) { + regulator_set_voltage(avdd_regulator, + ADV7180_VOLTAGE_ANALOG, + ADV7180_VOLTAGE_ANALOG); + ret = regulator_enable(avdd_regulator); + if (ret) { + dev_err(dev, "set analog voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set analog voltage ok\n"); + } + } else { + dev_warn(dev, "cannot get analog voltage\n"); + } + + pvdd_regulator = devm_regulator_get(dev, "PVDD"); + if (!IS_ERR(pvdd_regulator)) { + regulator_set_voltage(pvdd_regulator, + ADV7180_VOLTAGE_PLL, + ADV7180_VOLTAGE_PLL); + ret = regulator_enable(pvdd_regulator); + if (ret) { + dev_err(dev, "set pll voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set pll voltage ok\n"); + } + } else { + dev_warn(dev, "cannot get pll voltage\n"); + } + + return ret; +} + + +/*********************************************************************** + * I2C transfert. + ***********************************************************************/ + +/*! Read one register from a ADV7180 i2c slave device. + * + * @param *reg register in the device we wish to access. + * + * @return 0 if success, an error code otherwise. + */ +static inline int adv7180_read(u8 reg) +{ + int val; + + val = i2c_smbus_read_byte_data(adv7180_data.sen.i2c_client, reg); + if (val < 0) { + dev_dbg(&adv7180_data.sen.i2c_client->dev, + "%s:read reg error: reg=%2x\n", __func__, reg); + return -1; + } + return val; +} + +/*! Write one register of a ADV7180 i2c slave device. + * + * @param *reg register in the device we wish to access. + * + * @return 0 if success, an error code otherwise. + */ +static int adv7180_write_reg(u8 reg, u8 val) +{ + s32 ret; + + ret = i2c_smbus_write_byte_data(adv7180_data.sen.i2c_client, reg, val); + if (ret < 0) { + dev_dbg(&adv7180_data.sen.i2c_client->dev, + "%s:write reg error:reg=%2x,val=%2x\n", __func__, + reg, val); + return -1; + } + return 0; +} + +/*********************************************************************** + * mxc_v4l2_capture interface. + ***********************************************************************/ + +/*! + * Return attributes of current video standard. + * Since this device autodetects the current standard, this function also + * sets the values that need to be changed if the standard changes. + * There is no set std equivalent function. + * + * @return None. + */ +static void adv7180_get_std(v4l2_std_id *std) +{ + int tmp; + int idx; + + dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180_get_std\n"); + + /* Read the AD_RESULT to get the detect output video standard */ + tmp = adv7180_read(ADV7180_STATUS_1) & 0x70; + + mutex_lock(&mutex); + if (tmp == 0x40) { + /* PAL */ + *std = V4L2_STD_PAL; + idx = ADV7180_PAL; + } else if (tmp == 0) { + /*NTSC*/ + *std = V4L2_STD_NTSC; + idx = ADV7180_NTSC; + } else { + *std = V4L2_STD_ALL; + idx = ADV7180_NOT_LOCKED; + dev_dbg(&adv7180_data.sen.i2c_client->dev, + "Got invalid video standard!\n"); + } + mutex_unlock(&mutex); + + /* This assumes autodetect which this device uses. */ + if (*std != adv7180_data.std_id) { + video_idx = idx; + adv7180_data.std_id = *std; + adv7180_data.sen.pix.width = video_fmts[video_idx].raw_width; + adv7180_data.sen.pix.height = video_fmts[video_idx].raw_height; + } +} + +/*********************************************************************** + * IOCTL Functions from v4l2_int_ioctl_desc. + ***********************************************************************/ + +/*! + * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num + * s: pointer to standard V4L2 device structure + * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure + * + * Gets slave interface parameters. + * Calculates the required xclk value to support the requested + * clock parameters in p. This value is returned in the p + * parameter. + * + * vidioc_int_g_ifparm returns platform-specific information about the + * interface settings used by the sensor. + * + * Called on open. + */ +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_g_ifparm\n"); + + if (s == NULL) { + pr_err(" ERROR!! no slave device set!\n"); + return -1; + } + + /* Initialize structure to 0s then set any non-0 values. */ + memset(p, 0, sizeof(*p)); + p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */ + p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; + p->u.bt656.nobt_hs_inv = 1; + p->u.bt656.bt_sync_correct = 1; + + /* ADV7180 has a dedicated clock so no clock settings needed. */ + + return 0; +} + +/*! + * Sets the camera power. + * + * s pointer to the camera device + * on if 1, power is to be turned on. 0 means power is to be turned off + * + * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + * This is called on open, close, suspend and resume. + */ +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct sensor *sensor = s->priv; + + dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_s_power\n"); + + if (on && !sensor->sen.on) { + if (adv7180_write_reg(ADV7180_PWR_MNG, 0x04) != 0) + return -EIO; + + /* + * FIXME:Additional 400ms to wait the chip to be stable? + * This is a workaround for preview scrolling issue. + */ + msleep(400); + } else if (!on && sensor->sen.on) { + if (adv7180_write_reg(ADV7180_PWR_MNG, 0x24) != 0) + return -EIO; + } + + sensor->sen.on = on; + + return 0; +} + +/*! + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_g_parm\n"); + + switch (a->type) { + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cparm->capability = sensor->sen.streamcap.capability; + cparm->timeperframe = sensor->sen.streamcap.timeperframe; + cparm->capturemode = sensor->sen.streamcap.capturemode; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + break; + + default: + pr_debug("ioctl_g_parm:type is unknown %d\n", a->type); + break; + } + + return 0; +} + +/*! + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + * + * This driver cannot change these settings. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_s_parm\n"); + + switch (a->type) { + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + break; + } + + return 0; +} + +/*! + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct sensor *sensor = s->priv; + + dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_g_fmt_cap\n"); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pr_debug(" Returning size of %dx%d\n", + sensor->sen.pix.width, sensor->sen.pix.height); + f->fmt.pix = sensor->sen.pix; + break; + + case V4L2_BUF_TYPE_PRIVATE: { + v4l2_std_id std; + adv7180_get_std(&std); + f->fmt.pix.pixelformat = (u32)std; + } + break; + + default: + f->fmt.pix = sensor->sen.pix; + break; + } + + return 0; +} + +/*! + * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure + * + * If the requested control is supported, returns the control information + * from the video_control[] array. Otherwise, returns -EINVAL if the + * control is not supported. + */ +static int ioctl_queryctrl(struct v4l2_int_device *s, + struct v4l2_queryctrl *qc) +{ + int i; + + dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_queryctrl\n"); + + for (i = 0; i < ARRAY_SIZE(adv7180_qctrl); i++) + if (qc->id && qc->id == adv7180_qctrl[i].id) { + memcpy(qc, &(adv7180_qctrl[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + +/*! + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int ret = 0; + int sat = 0; + + dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_g_ctrl\n"); + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_BRIGHTNESS\n"); + adv7180_data.sen.brightness = adv7180_read(ADV7180_BRIGHTNESS); + vc->value = adv7180_data.sen.brightness; + break; + case V4L2_CID_CONTRAST: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_CONTRAST\n"); + vc->value = adv7180_data.sen.contrast; + break; + case V4L2_CID_SATURATION: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_SATURATION\n"); + sat = adv7180_read(ADV7180_SD_SATURATION_CB); + adv7180_data.sen.saturation = sat; + vc->value = adv7180_data.sen.saturation; + break; + case V4L2_CID_HUE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_HUE\n"); + vc->value = adv7180_data.sen.hue; + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_AUTO_WHITE_BALANCE\n"); + break; + case V4L2_CID_DO_WHITE_BALANCE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_DO_WHITE_BALANCE\n"); + break; + case V4L2_CID_RED_BALANCE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_RED_BALANCE\n"); + vc->value = adv7180_data.sen.red; + break; + case V4L2_CID_BLUE_BALANCE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_BLUE_BALANCE\n"); + vc->value = adv7180_data.sen.blue; + break; + case V4L2_CID_GAMMA: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_GAMMA\n"); + break; + case V4L2_CID_EXPOSURE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_EXPOSURE\n"); + vc->value = adv7180_data.sen.ae_mode; + break; + case V4L2_CID_AUTOGAIN: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_AUTOGAIN\n"); + break; + case V4L2_CID_GAIN: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_GAIN\n"); + break; + case V4L2_CID_HFLIP: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_HFLIP\n"); + break; + case V4L2_CID_VFLIP: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_VFLIP\n"); + break; + default: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " Default case\n"); + vc->value = 0; + ret = -EPERM; + break; + } + + return ret; +} + +/*! + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = 0; + u8 tmp; + + dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_s_ctrl\n"); + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_BRIGHTNESS\n"); + tmp = vc->value; + adv7180_write_reg(ADV7180_BRIGHTNESS, tmp); + adv7180_data.sen.brightness = vc->value; + break; + case V4L2_CID_CONTRAST: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_CONTRAST\n"); + break; + case V4L2_CID_SATURATION: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_SATURATION\n"); + tmp = vc->value; + adv7180_write_reg(ADV7180_SD_SATURATION_CB, tmp); + adv7180_write_reg(ADV7180_SD_SATURATION_CR, tmp); + adv7180_data.sen.saturation = vc->value; + break; + case V4L2_CID_HUE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_HUE\n"); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_AUTO_WHITE_BALANCE\n"); + break; + case V4L2_CID_DO_WHITE_BALANCE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_DO_WHITE_BALANCE\n"); + break; + case V4L2_CID_RED_BALANCE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_RED_BALANCE\n"); + break; + case V4L2_CID_BLUE_BALANCE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_BLUE_BALANCE\n"); + break; + case V4L2_CID_GAMMA: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_GAMMA\n"); + break; + case V4L2_CID_EXPOSURE: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_EXPOSURE\n"); + break; + case V4L2_CID_AUTOGAIN: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_AUTOGAIN\n"); + break; + case V4L2_CID_GAIN: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_GAIN\n"); + break; + case V4L2_CID_HFLIP: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_HFLIP\n"); + break; + case V4L2_CID_VFLIP: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " V4L2_CID_VFLIP\n"); + break; + default: + dev_dbg(&adv7180_data.sen.i2c_client->dev, + " Default case\n"); + retval = -EPERM; + break; + } + + return retval; +} + +/*! + * ioctl_enum_framesizes - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMESIZES ioctl + * @s: pointer to standard V4L2 device structure + * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index >= 1) + return -EINVAL; + + fsize->discrete.width = video_fmts[video_idx].active_width; + fsize->discrete.height = video_fmts[video_idx].active_height; + + return 0; +} + +/*! + * ioctl_g_chip_ident - V4L2 sensor interface handler for + * VIDIOC_DBG_G_CHIP_IDENT ioctl + * @s: pointer to standard V4L2 device structure + * @id: pointer to int + * + * Return 0. + */ +static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) +{ + ((struct v4l2_dbg_chip_ident *)id)->match.type = + V4L2_CHIP_MATCH_I2C_DRIVER; + strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, + "adv7180_decoder"); + ((struct v4l2_dbg_chip_ident *)id)->ident = V4L2_IDENT_ADV7180; + + return 0; +} + +/*! + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_init\n"); + return 0; +} + +/*! + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_dev_init\n"); + return 0; +} + +/*! + * This structure defines all the ioctls for this module. + */ +static struct v4l2_int_ioctl_desc adv7180_ioctl_desc[] = { + + {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*)ioctl_dev_init}, + + /*! + * Delinitialise the dev. at slave detach. + * The complement of ioctl_dev_init. + */ +/* {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit}, */ + + {vidioc_int_s_power_num, (v4l2_int_ioctl_func*)ioctl_s_power}, + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*)ioctl_g_ifparm}, +/* {vidioc_int_g_needs_reset_num, + (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */ +/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */ + {vidioc_int_init_num, (v4l2_int_ioctl_func*)ioctl_init}, + + /*! + * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. + */ +/* {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */ + + /*! + * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. + * This ioctl is used to negotiate the image capture size and + * pixel format without actually making it take effect. + */ +/* {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */ + + {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_g_fmt_cap}, + + /*! + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */ + + {vidioc_int_g_parm_num, (v4l2_int_ioctl_func*)ioctl_g_parm}, + {vidioc_int_s_parm_num, (v4l2_int_ioctl_func*)ioctl_s_parm}, + {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func*)ioctl_queryctrl}, + {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func*)ioctl_g_ctrl}, + {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func*)ioctl_s_ctrl}, + {vidioc_int_enum_framesizes_num, + (v4l2_int_ioctl_func *) ioctl_enum_framesizes}, + {vidioc_int_g_chip_ident_num, + (v4l2_int_ioctl_func *)ioctl_g_chip_ident}, +}; + +static struct v4l2_int_slave adv7180_slave = { + .ioctls = adv7180_ioctl_desc, + .num_ioctls = ARRAY_SIZE(adv7180_ioctl_desc), +}; + +static struct v4l2_int_device adv7180_int_device = { + .module = THIS_MODULE, + .name = "adv7180", + .type = v4l2_int_type_slave, + .u = { + .slave = &adv7180_slave, + }, +}; + + +/*********************************************************************** + * I2C client and driver. + ***********************************************************************/ + +/*! ADV7180 Reset function. + * + * @return None. + */ +static void adv7180_hard_reset(bool cvbs) +{ + dev_dbg(&adv7180_data.sen.i2c_client->dev, + "In adv7180:adv7180_hard_reset\n"); + + if (cvbs) { + /* Set CVBS input on AIN1 */ + adv7180_write_reg(ADV7180_INPUT_CTL, 0x00); + } else { + /* + * Set YPbPr input on AIN1,4,5 and normal + * operations(autodection of all stds). + */ + adv7180_write_reg(ADV7180_INPUT_CTL, 0x09); + } + + /* Datasheet recommends */ + adv7180_write_reg(0x01, 0xc8); + adv7180_write_reg(0x02, 0x04); + adv7180_write_reg(0x03, 0x00); + adv7180_write_reg(0x04, 0x45); + adv7180_write_reg(0x05, 0x00); + adv7180_write_reg(0x06, 0x02); + adv7180_write_reg(0x07, 0x7F); + adv7180_write_reg(0x08, 0x80); + adv7180_write_reg(0x0A, 0x00); + adv7180_write_reg(0x0B, 0x00); + adv7180_write_reg(0x0C, 0x36); + adv7180_write_reg(0x0D, 0x7C); + adv7180_write_reg(0x0E, 0x00); + adv7180_write_reg(0x0F, 0x00); + adv7180_write_reg(0x13, 0x00); + adv7180_write_reg(0x14, 0x12); + adv7180_write_reg(0x15, 0x00); + adv7180_write_reg(0x16, 0x00); + adv7180_write_reg(0x17, 0x01); + adv7180_write_reg(0x18, 0x93); + adv7180_write_reg(0xF1, 0x19); + adv7180_write_reg(0x1A, 0x00); + adv7180_write_reg(0x1B, 0x00); + adv7180_write_reg(0x1C, 0x00); + adv7180_write_reg(0x1D, 0x40); + adv7180_write_reg(0x1E, 0x00); + adv7180_write_reg(0x1F, 0x00); + adv7180_write_reg(0x20, 0x00); + adv7180_write_reg(0x21, 0x00); + adv7180_write_reg(0x22, 0x00); + adv7180_write_reg(0x23, 0xC0); + adv7180_write_reg(0x24, 0x00); + adv7180_write_reg(0x25, 0x00); + adv7180_write_reg(0x26, 0x00); + adv7180_write_reg(0x27, 0x58); + adv7180_write_reg(0x28, 0x00); + adv7180_write_reg(0x29, 0x00); + adv7180_write_reg(0x2A, 0x00); + adv7180_write_reg(0x2B, 0xE1); + adv7180_write_reg(0x2C, 0xAE); + adv7180_write_reg(0x2D, 0xF4); + adv7180_write_reg(0x2E, 0x00); + adv7180_write_reg(0x2F, 0xF0); + adv7180_write_reg(0x30, 0x00); + adv7180_write_reg(0x31, 0x12); + adv7180_write_reg(0x32, 0x41); + adv7180_write_reg(0x33, 0x84); + adv7180_write_reg(0x34, 0x00); + adv7180_write_reg(0x35, 0x02); + adv7180_write_reg(0x36, 0x00); + adv7180_write_reg(0x37, 0x01); + adv7180_write_reg(0x38, 0x80); + adv7180_write_reg(0x39, 0xC0); + adv7180_write_reg(0x3A, 0x10); + adv7180_write_reg(0x3B, 0x05); + adv7180_write_reg(0x3C, 0x58); + adv7180_write_reg(0x3D, 0xB2); + adv7180_write_reg(0x3E, 0x64); + adv7180_write_reg(0x3F, 0xE4); + adv7180_write_reg(0x40, 0x90); + adv7180_write_reg(0x41, 0x01); + adv7180_write_reg(0x42, 0x7E); + adv7180_write_reg(0x43, 0xA4); + adv7180_write_reg(0x44, 0xFF); + adv7180_write_reg(0x45, 0xB6); + adv7180_write_reg(0x46, 0x12); + adv7180_write_reg(0x48, 0x00); + adv7180_write_reg(0x49, 0x00); + adv7180_write_reg(0x4A, 0x00); + adv7180_write_reg(0x4B, 0x00); + adv7180_write_reg(0x4C, 0x00); + adv7180_write_reg(0x4D, 0xEF); + adv7180_write_reg(0x4E, 0x08); + adv7180_write_reg(0x4F, 0x08); + adv7180_write_reg(0x50, 0x08); + adv7180_write_reg(0x51, 0x24); + adv7180_write_reg(0x52, 0x0B); + adv7180_write_reg(0x53, 0x4E); + adv7180_write_reg(0x54, 0x80); + adv7180_write_reg(0x55, 0x00); + adv7180_write_reg(0x56, 0x10); + adv7180_write_reg(0x57, 0x00); + adv7180_write_reg(0x58, 0x00); + adv7180_write_reg(0x59, 0x00); + adv7180_write_reg(0x5A, 0x00); + adv7180_write_reg(0x5B, 0x00); + adv7180_write_reg(0x5C, 0x00); + adv7180_write_reg(0x5D, 0x00); + adv7180_write_reg(0x5E, 0x00); + adv7180_write_reg(0x5F, 0x00); + adv7180_write_reg(0x60, 0x00); + adv7180_write_reg(0x61, 0x00); + adv7180_write_reg(0x62, 0x20); + adv7180_write_reg(0x63, 0x00); + adv7180_write_reg(0x64, 0x00); + adv7180_write_reg(0x65, 0x00); + adv7180_write_reg(0x66, 0x00); + adv7180_write_reg(0x67, 0x03); + adv7180_write_reg(0x68, 0x01); + adv7180_write_reg(0x69, 0x00); + adv7180_write_reg(0x6A, 0x00); + adv7180_write_reg(0x6B, 0xC0); + adv7180_write_reg(0x6C, 0x00); + adv7180_write_reg(0x6D, 0x00); + adv7180_write_reg(0x6E, 0x00); + adv7180_write_reg(0x6F, 0x00); + adv7180_write_reg(0x70, 0x00); + adv7180_write_reg(0x71, 0x00); + adv7180_write_reg(0x72, 0x00); + adv7180_write_reg(0x73, 0x10); + adv7180_write_reg(0x74, 0x04); + adv7180_write_reg(0x75, 0x01); + adv7180_write_reg(0x76, 0x00); + adv7180_write_reg(0x77, 0x3F); + adv7180_write_reg(0x78, 0xFF); + adv7180_write_reg(0x79, 0xFF); + adv7180_write_reg(0x7A, 0xFF); + adv7180_write_reg(0x7B, 0x1E); + adv7180_write_reg(0x7C, 0xC0); + adv7180_write_reg(0x7D, 0x00); + adv7180_write_reg(0x7E, 0x00); + adv7180_write_reg(0x7F, 0x00); + adv7180_write_reg(0x80, 0x00); + adv7180_write_reg(0x81, 0xC0); + adv7180_write_reg(0x82, 0x04); + adv7180_write_reg(0x83, 0x00); + adv7180_write_reg(0x84, 0x0C); + adv7180_write_reg(0x85, 0x02); + adv7180_write_reg(0x86, 0x03); + adv7180_write_reg(0x87, 0x63); + adv7180_write_reg(0x88, 0x5A); + adv7180_write_reg(0x89, 0x08); + adv7180_write_reg(0x8A, 0x10); + adv7180_write_reg(0x8B, 0x00); + adv7180_write_reg(0x8C, 0x40); + adv7180_write_reg(0x8D, 0x00); + adv7180_write_reg(0x8E, 0x40); + adv7180_write_reg(0x8F, 0x00); + adv7180_write_reg(0x90, 0x00); + adv7180_write_reg(0x91, 0x50); + adv7180_write_reg(0x92, 0x00); + adv7180_write_reg(0x93, 0x00); + adv7180_write_reg(0x94, 0x00); + adv7180_write_reg(0x95, 0x00); + adv7180_write_reg(0x96, 0x00); + adv7180_write_reg(0x97, 0xF0); + adv7180_write_reg(0x98, 0x00); + adv7180_write_reg(0x99, 0x00); + adv7180_write_reg(0x9A, 0x00); + adv7180_write_reg(0x9B, 0x00); + adv7180_write_reg(0x9C, 0x00); + adv7180_write_reg(0x9D, 0x00); + adv7180_write_reg(0x9E, 0x00); + adv7180_write_reg(0x9F, 0x00); + adv7180_write_reg(0xA0, 0x00); + adv7180_write_reg(0xA1, 0x00); + adv7180_write_reg(0xA2, 0x00); + adv7180_write_reg(0xA3, 0x00); + adv7180_write_reg(0xA4, 0x00); + adv7180_write_reg(0xA5, 0x00); + adv7180_write_reg(0xA6, 0x00); + adv7180_write_reg(0xA7, 0x00); + adv7180_write_reg(0xA8, 0x00); + adv7180_write_reg(0xA9, 0x00); + adv7180_write_reg(0xAA, 0x00); + adv7180_write_reg(0xAB, 0x00); + adv7180_write_reg(0xAC, 0x00); + adv7180_write_reg(0xAD, 0x00); + adv7180_write_reg(0xAE, 0x60); + adv7180_write_reg(0xAF, 0x00); + adv7180_write_reg(0xB0, 0x00); + adv7180_write_reg(0xB1, 0x60); + adv7180_write_reg(0xB2, 0x1C); + adv7180_write_reg(0xB3, 0x54); + adv7180_write_reg(0xB4, 0x00); + adv7180_write_reg(0xB5, 0x00); + adv7180_write_reg(0xB6, 0x00); + adv7180_write_reg(0xB7, 0x13); + adv7180_write_reg(0xB8, 0x03); + adv7180_write_reg(0xB9, 0x33); + adv7180_write_reg(0xBF, 0x02); + adv7180_write_reg(0xC0, 0x00); + adv7180_write_reg(0xC1, 0x00); + adv7180_write_reg(0xC2, 0x00); + adv7180_write_reg(0xC3, 0x00); + adv7180_write_reg(0xC4, 0x00); + adv7180_write_reg(0xC5, 0x81); + adv7180_write_reg(0xC6, 0x00); + adv7180_write_reg(0xC7, 0x00); + adv7180_write_reg(0xC8, 0x00); + adv7180_write_reg(0xC9, 0x04); + adv7180_write_reg(0xCC, 0x69); + adv7180_write_reg(0xCD, 0x00); + adv7180_write_reg(0xCE, 0x01); + adv7180_write_reg(0xCF, 0xB4); + adv7180_write_reg(0xD0, 0x00); + adv7180_write_reg(0xD1, 0x10); + adv7180_write_reg(0xD2, 0xFF); + adv7180_write_reg(0xD3, 0xFF); + adv7180_write_reg(0xD4, 0x7F); + adv7180_write_reg(0xD5, 0x7F); + adv7180_write_reg(0xD6, 0x3E); + adv7180_write_reg(0xD7, 0x08); + adv7180_write_reg(0xD8, 0x3C); + adv7180_write_reg(0xD9, 0x08); + adv7180_write_reg(0xDA, 0x3C); + adv7180_write_reg(0xDB, 0x9B); + adv7180_write_reg(0xDC, 0xAC); + adv7180_write_reg(0xDD, 0x4C); + adv7180_write_reg(0xDE, 0x00); + adv7180_write_reg(0xDF, 0x00); + adv7180_write_reg(0xE0, 0x14); + adv7180_write_reg(0xE1, 0x80); + adv7180_write_reg(0xE2, 0x80); + adv7180_write_reg(0xE3, 0x80); + adv7180_write_reg(0xE4, 0x80); + adv7180_write_reg(0xE5, 0x25); + adv7180_write_reg(0xE6, 0x44); + adv7180_write_reg(0xE7, 0x63); + adv7180_write_reg(0xE8, 0x65); + adv7180_write_reg(0xE9, 0x14); + adv7180_write_reg(0xEA, 0x63); + adv7180_write_reg(0xEB, 0x55); + adv7180_write_reg(0xEC, 0x55); + adv7180_write_reg(0xEE, 0x00); + adv7180_write_reg(0xEF, 0x4A); + adv7180_write_reg(0xF0, 0x44); + adv7180_write_reg(0xF1, 0x0C); + adv7180_write_reg(0xF2, 0x32); + adv7180_write_reg(0xF3, 0x00); + adv7180_write_reg(0xF4, 0x3F); + adv7180_write_reg(0xF5, 0xE0); + adv7180_write_reg(0xF6, 0x69); + adv7180_write_reg(0xF7, 0x10); + adv7180_write_reg(0xF8, 0x00); + adv7180_write_reg(0xF9, 0x03); + adv7180_write_reg(0xFA, 0xFA); + adv7180_write_reg(0xFB, 0x40); +} + +/*! ADV7180 I2C attach function. + * + * @param *adapter struct i2c_adapter *. + * + * @return Error code indicating success or failure. + */ + +/*! + * ADV7180 I2C probe function. + * Function set in i2c_driver struct. + * Called by insmod. + * + * @param *adapter I2C adapter descriptor. + * + * @return Error code indicating success or failure. + */ +static int adv7180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rev_id; + int ret = 0; + u32 cvbs = true; + struct pinctrl *pinctrl; + struct device *dev = &client->dev; + + printk(KERN_ERR"DBG sensor data is at %p\n", &adv7180_data); + + /* ov5640 pinctrl */ + pinctrl = devm_pinctrl_get_select_default(dev); + if (IS_ERR(pinctrl)) { + dev_err(dev, "setup pinctrl failed\n"); + return PTR_ERR(pinctrl); + } + + /* request power down pin */ + pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); + if (!gpio_is_valid(pwn_gpio)) { + dev_err(dev, "no sensor pwdn pin available\n"); + return -ENODEV; + } + ret = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, + "adv7180_pwdn"); + if (ret < 0) { + dev_err(dev, "no power pin available!\n"); + return ret; + } + + adv7180_regulator_enable(dev); + + adv7180_power_down(0); + + msleep(1); + + /* Set initial values for the sensor struct. */ + memset(&adv7180_data, 0, sizeof(adv7180_data)); + adv7180_data.sen.i2c_client = client; + adv7180_data.sen.streamcap.timeperframe.denominator = 30; + adv7180_data.sen.streamcap.timeperframe.numerator = 1; + adv7180_data.std_id = V4L2_STD_ALL; + video_idx = ADV7180_NOT_LOCKED; + adv7180_data.sen.pix.width = video_fmts[video_idx].raw_width; + adv7180_data.sen.pix.height = video_fmts[video_idx].raw_height; + adv7180_data.sen.pix.pixelformat = V4L2_PIX_FMT_UYVY; /* YUV422 */ + adv7180_data.sen.pix.priv = 1; /* 1 is used to indicate TV in */ + adv7180_data.sen.on = true; + + adv7180_data.sen.sensor_clk = devm_clk_get(dev, "csi_mclk"); + if (IS_ERR(adv7180_data.sen.sensor_clk)) { + dev_err(dev, "get mclk failed\n"); + return PTR_ERR(adv7180_data.sen.sensor_clk); + } + + ret = of_property_read_u32(dev->of_node, "mclk", + &adv7180_data.sen.mclk); + if (ret) { + dev_err(dev, "mclk frequency is invalid\n"); + return ret; + } + + ret = of_property_read_u32( + dev->of_node, "mclk_source", + (u32 *) &(adv7180_data.sen.mclk_source)); + if (ret) { + dev_err(dev, "mclk_source invalid\n"); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "csi_id", + &(adv7180_data.sen.csi)); + if (ret) { + dev_err(dev, "csi_id invalid\n"); + return ret; + } + + clk_prepare_enable(adv7180_data.sen.sensor_clk); + + dev_dbg(&adv7180_data.sen.i2c_client->dev, + "%s:adv7180 probe i2c address is 0x%02X\n", + __func__, adv7180_data.sen.i2c_client->addr); + + /*! Read the revision ID of the tvin chip */ + rev_id = adv7180_read(ADV7180_IDENT); + dev_dbg(dev, + "%s:Analog Device adv7%2X0 detected!\n", __func__, + rev_id); + + ret = of_property_read_u32(dev->of_node, "cvbs", &(cvbs)); + if (ret) { + dev_err(dev, "cvbs setting is not found\n"); + cvbs = true; + } + + /*! ADV7180 initialization. */ + adv7180_hard_reset(cvbs); + + pr_debug(" type is %d (expect %d)\n", + adv7180_int_device.type, v4l2_int_type_slave); + pr_debug(" num ioctls is %d\n", + adv7180_int_device.u.slave->num_ioctls); + + /* This function attaches this structure to the /dev/video0 device. + * The pointer in priv points to the adv7180_data structure here.*/ + adv7180_int_device.priv = &adv7180_data; + ret = v4l2_int_device_register(&adv7180_int_device); + + clk_disable_unprepare(adv7180_data.sen.sensor_clk); + + return ret; +} + +/*! + * ADV7180 I2C detach function. + * Called on rmmod. + * + * @param *client struct i2c_client*. + * + * @return Error code indicating success or failure. + */ +static int adv7180_detach(struct i2c_client *client) +{ + dev_dbg(&adv7180_data.sen.i2c_client->dev, + "%s:Removing %s video decoder @ 0x%02X from adapter %s\n", + __func__, IF_NAME, client->addr << 1, client->adapter->name); + + /* Power down via i2c */ + adv7180_write_reg(ADV7180_PWR_MNG, 0x24); + + if (dvddio_regulator) + regulator_disable(dvddio_regulator); + + if (dvdd_regulator) + regulator_disable(dvdd_regulator); + + if (avdd_regulator) + regulator_disable(avdd_regulator); + + if (pvdd_regulator) + regulator_disable(pvdd_regulator); + + v4l2_int_device_unregister(&adv7180_int_device); + + return 0; +} + +/*! + * ADV7180 init function. + * Called on insmod. + * + * @return Error code indicating success or failure. + */ +static __init int adv7180_init(void) +{ + u8 err = 0; + + pr_debug("In adv7180_init\n"); + + /* Tells the i2c driver what functions to call for this driver. */ + err = i2c_add_driver(&adv7180_i2c_driver); + if (err != 0) + pr_err("%s:driver registration failed, error=%d\n", + __func__, err); + + return err; +} + +/*! + * ADV7180 cleanup function. + * Called on rmmod. + * + * @return Error code indicating success or failure. + */ +static void __exit adv7180_clean(void) +{ + dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180_clean\n"); + i2c_del_driver(&adv7180_i2c_driver); +} + +module_init(adv7180_init); +module_exit(adv7180_clean); + +MODULE_AUTHOR("Freescale Semiconductor"); +MODULE_DESCRIPTION("Anolog Device ADV7180 video decoder driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/csi_v4l2_capture.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/csi_v4l2_capture.c --- linux-3.10.30/drivers/media/platform/mxc/capture/csi_v4l2_capture.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/csi_v4l2_capture.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,2047 @@ +/* + * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file drivers/media/video/mxc/capture/csi_v4l2_capture.c + * This file is derived from mxc_v4l2_capture.c + * + * @brief Video For Linux 2 capture driver + * + * @ingroup MXC_V4L2_CAPTURE + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "fsl_csi.h" + +static int video_nr = -1; +static cam_data *g_cam; +static int req_buf_number; + +static int csi_v4l2_master_attach(struct v4l2_int_device *slave); +static void csi_v4l2_master_detach(struct v4l2_int_device *slave); +static u8 camera_power(cam_data *cam, bool cameraOn); +struct v4l2_crop crop_current; +struct v4l2_window win_current; + +/*! Information about this driver. */ +static struct v4l2_int_master csi_v4l2_master = { + .attach = csi_v4l2_master_attach, + .detach = csi_v4l2_master_detach, +}; + +static struct v4l2_int_device csi_v4l2_int_device = { + .module = THIS_MODULE, + .name = "csi_v4l2_cap", + .type = v4l2_int_type_master, + .u = { + .master = &csi_v4l2_master, + }, +}; + +static struct v4l2_queryctrl pxp_controls[] = { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Rotation", + .minimum = 0, + .maximum = 270, + .step = 90, + .default_value = 0, + .flags = 0, + }, +}; + +/* Callback function triggered after PxP receives an EOF interrupt */ +static void pxp_dma_done(void *arg) +{ + struct pxp_tx_desc *tx_desc = to_tx_desc(arg); + struct dma_chan *chan = tx_desc->txd.chan; + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + cam_data *cam = pxp_chan->client; + + /* This call will signal wait_for_completion_timeout() */ + complete(&cam->pxp_tx_cmpl); +} + +static bool chan_filter(struct dma_chan *chan, void *arg) +{ + if (imx_dma_is_pxp(chan)) + return true; + else + return false; +} + +/* Function to request PXP DMA channel */ +static int pxp_chan_init(cam_data *cam) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + + /* Request a free channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + chan = dma_request_channel(mask, chan_filter, NULL); + if (!chan) { + pr_err("Unsuccessfully request channel!\n"); + return -EBUSY; + } + + cam->pxp_chan = to_pxp_channel(chan); + cam->pxp_chan->client = cam; + + init_completion(&cam->pxp_tx_cmpl); + + return 0; +} + +/* + * Function to call PxP DMA driver and send our new V4L2 buffer + * through the PxP. + * Note: This is a blocking call, so upon return the PxP tx should be complete. + */ +static int pxp_process_update(cam_data *cam) +{ + dma_cookie_t cookie; + struct scatterlist *sg = cam->sg; + struct dma_chan *dma_chan; + struct pxp_tx_desc *desc; + struct dma_async_tx_descriptor *txd; + struct pxp_config_data *pxp_conf = &cam->pxp_conf; + struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; + int i, ret; + int length; + + pr_debug("Starting PxP Send Buffer\n"); + + /* First, check to see that we have acquired a PxP Channel object */ + if (cam->pxp_chan == NULL) { + /* + * PxP Channel has not yet been created and initialized, + * so let's go ahead and try + */ + ret = pxp_chan_init(cam); + if (ret) { + /* + * PxP channel init failed, and we can't use the + * PxP until the PxP DMA driver has loaded, so we abort + */ + pr_err("PxP chan init failed\n"); + return -ENODEV; + } + } + + /* + * Init completion, so that we can be properly informed of + * the completion of the PxP task when it is done. + */ + init_completion(&cam->pxp_tx_cmpl); + + dma_chan = &cam->pxp_chan->dma_chan; + + txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg, 2, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT, + NULL); + if (!txd) { + pr_err("Error preparing a DMA transaction descriptor.\n"); + return -EIO; + } + + txd->callback_param = txd; + txd->callback = pxp_dma_done; + + /* + * Configure PxP for processing of new v4l2 buf + */ + pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_UYVY; + pxp_conf->s0_param.color_key = -1; + pxp_conf->s0_param.color_key_enable = false; + pxp_conf->s0_param.width = cam->v2f.fmt.pix.width; + pxp_conf->s0_param.height = cam->v2f.fmt.pix.height; + + pxp_conf->ol_param[0].combine_enable = false; + + proc_data->srect.top = 0; + proc_data->srect.left = 0; + proc_data->srect.width = pxp_conf->s0_param.width; + proc_data->srect.height = pxp_conf->s0_param.height; + + if (crop_current.c.top != 0) + proc_data->srect.top = crop_current.c.top; + if (crop_current.c.left != 0) + proc_data->srect.left = crop_current.c.left; + if (crop_current.c.width != 0) + proc_data->srect.width = crop_current.c.width; + if (crop_current.c.height != 0) + proc_data->srect.height = crop_current.c.height; + + proc_data->drect.left = 0; + proc_data->drect.top = 0; + proc_data->drect.width = proc_data->srect.width; + proc_data->drect.height = proc_data->srect.height; + + if (win_current.w.left != 0) + proc_data->drect.left = win_current.w.left; + if (win_current.w.top != 0) + proc_data->drect.top = win_current.w.top; + if (win_current.w.width != 0) + proc_data->drect.width = win_current.w.width; + if (win_current.w.height != 0) + proc_data->drect.height = win_current.w.height; + + pr_debug("srect l: %d, t: %d, w: %d, h: %d; " + "drect l: %d, t: %d, w: %d, h: %d\n", + proc_data->srect.left, proc_data->srect.top, + proc_data->srect.width, proc_data->srect.height, + proc_data->drect.left, proc_data->drect.top, + proc_data->drect.width, proc_data->drect.height); + + pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_RGB565; + pxp_conf->out_param.width = proc_data->drect.width; + pxp_conf->out_param.height = proc_data->drect.height; + + if (cam->rotation % 180) + pxp_conf->out_param.stride = pxp_conf->out_param.height; + else + pxp_conf->out_param.stride = pxp_conf->out_param.width; + + desc = to_tx_desc(txd); + length = desc->len; + for (i = 0; i < length; i++) { + if (i == 0) {/* S0 */ + memcpy(&desc->proc_data, proc_data, + sizeof(struct pxp_proc_data)); + pxp_conf->s0_param.paddr = sg_dma_address(&sg[0]); + memcpy(&desc->layer_param.s0_param, &pxp_conf->s0_param, + sizeof(struct pxp_layer_param)); + } else if (i == 1) { + pxp_conf->out_param.paddr = sg_dma_address(&sg[1]); + memcpy(&desc->layer_param.out_param, + &pxp_conf->out_param, + sizeof(struct pxp_layer_param)); + } + + desc = desc->next; + } + + /* Submitting our TX starts the PxP processing task */ + cookie = txd->tx_submit(txd); + if (cookie < 0) { + pr_err("Error sending FB through PxP\n"); + return -EIO; + } + + cam->txd = txd; + + /* trigger PxP */ + dma_async_issue_pending(dma_chan); + + return 0; +} + +static int pxp_complete_update(cam_data *cam) +{ + int ret; + /* + * Wait for completion event, which will be set + * through our TX callback function. + */ + ret = wait_for_completion_timeout(&cam->pxp_tx_cmpl, HZ / 10); + if (ret <= 0) { + pr_warning("PxP operation failed due to %s\n", + ret < 0 ? "user interrupt" : "timeout"); + dma_release_channel(&cam->pxp_chan->dma_chan); + cam->pxp_chan = NULL; + return ret ? : -ETIMEDOUT; + } + + dma_release_channel(&cam->pxp_chan->dma_chan); + cam->pxp_chan = NULL; + + pr_debug("TX completed\n"); + + return 0; +} + +/*! + * Camera V4l2 callback function. + * + * @param mask u32 + * @param dev void device structure + * + * @return none + */ +static void camera_callback(u32 mask, void *dev) +{ + struct mxc_v4l_frame *done_frame; + struct mxc_v4l_frame *ready_frame; + cam_data *cam; + + cam = (cam_data *) dev; + if (cam == NULL) + return; + + spin_lock(&cam->queue_int_lock); + spin_lock(&cam->dqueue_int_lock); + if (!list_empty(&cam->working_q)) { + done_frame = list_entry(cam->working_q.next, + struct mxc_v4l_frame, queue); + + if (done_frame->csi_buf_num != cam->ping_pong_csi) + goto next; + + if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { + done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE; + done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; + + /* Added to the done queue */ + list_del(cam->working_q.next); + list_add_tail(&done_frame->queue, &cam->done_q); + cam->enc_counter++; + wake_up_interruptible(&cam->enc_queue); + } else { + pr_err("ERROR: v4l2 capture: %s: " + "buffer not queued\n", __func__); + } + } + +next: + if (!list_empty(&cam->ready_q)) { + ready_frame = list_entry(cam->ready_q.next, + struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&ready_frame->queue, &cam->working_q); + + __raw_writel(ready_frame->paddress, + cam->ping_pong_csi == 1 ? CSI_CSIDMASA_FB1 : + CSI_CSIDMASA_FB2); + ready_frame->csi_buf_num = cam->ping_pong_csi; + } else { + __raw_writel(cam->dummy_frame.paddress, + cam->ping_pong_csi == 1 ? CSI_CSIDMASA_FB1 : + CSI_CSIDMASA_FB2); + } + spin_unlock(&cam->dqueue_int_lock); + spin_unlock(&cam->queue_int_lock); + + return; +} + +/*! + * Make csi ready for capture image. + * + * @param cam structure cam_data * + * + * @return status 0 success + */ +static int csi_cap_image(cam_data *cam) +{ + unsigned int value; + + value = __raw_readl(CSI_CSICR3); + __raw_writel(value | BIT_FRMCNT_RST, CSI_CSICR3); + value = __raw_readl(CSI_CSISR); + __raw_writel(value, CSI_CSISR); + + return 0; +} + +/*************************************************************************** + * Functions for handling Frame buffers. + **************************************************************************/ + +/*! + * Free frame buffers + * + * @param cam Structure cam_data * + * + * @return status 0 success. + */ +static int csi_free_frame_buf(cam_data *cam) +{ + int i; + + pr_debug("MVC: In %s\n", __func__); + + for (i = 0; i < FRAME_NUM; i++) { + if (cam->frame[i].vaddress != 0) { + dma_free_coherent(0, cam->frame[i].buffer.length, + cam->frame[i].vaddress, + cam->frame[i].paddress); + cam->frame[i].vaddress = 0; + } + } + + if (cam->dummy_frame.vaddress != 0) { + dma_free_coherent(0, cam->dummy_frame.buffer.length, + cam->dummy_frame.vaddress, + cam->dummy_frame.paddress); + cam->dummy_frame.vaddress = 0; + } + + return 0; +} + +/*! + * Allocate frame buffers + * + * @param cam Structure cam_data * + * @param count int number of buffer need to allocated + * + * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. + */ +static int csi_allocate_frame_buf(cam_data *cam, int count) +{ + int i; + + pr_debug("In MVC:%s- size=%d\n", + __func__, cam->v2f.fmt.pix.sizeimage); + for (i = 0; i < count; i++) { + cam->frame[i].vaddress = dma_alloc_coherent(0, PAGE_ALIGN + (cam->v2f.fmt. + pix.sizeimage), + &cam->frame[i]. + paddress, + GFP_DMA | + GFP_KERNEL); + if (cam->frame[i].vaddress == 0) { + pr_err("ERROR: v4l2 capture: " + "%s failed.\n", __func__); + csi_free_frame_buf(cam); + return -ENOBUFS; + } + cam->frame[i].buffer.index = i; + cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; + cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buffer.length = cam->v2f.fmt.pix.sizeimage; + cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buffer.m.offset = cam->frame[i].paddress; + cam->frame[i].index = i; + cam->frame[i].csi_buf_num = 0; + } + + return 0; +} + +/*! + * Free frame buffers status + * + * @param cam Structure cam_data * + * + * @return none + */ +static void csi_free_frames(cam_data *cam) +{ + int i; + + pr_debug("In MVC: %s\n", __func__); + + for (i = 0; i < FRAME_NUM; i++) + cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; + + cam->enc_counter = 0; + INIT_LIST_HEAD(&cam->ready_q); + INIT_LIST_HEAD(&cam->working_q); + INIT_LIST_HEAD(&cam->done_q); + + return; +} + +/*! + * Return the buffer status + * + * @param cam Structure cam_data * + * @param buf Structure v4l2_buffer * + * + * @return status 0 success, EINVAL failed. + */ +static int csi_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf) +{ + pr_debug("In MVC: %s\n", __func__); + + if (buf->index < 0 || buf->index >= FRAME_NUM) { + pr_err("ERROR: v4l2 capture: %s buffers " + "not allocated\n", __func__); + return -EINVAL; + } + + memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf)); + + return 0; +} + +static int csi_v4l2_release_bufs(cam_data *cam) +{ + pr_debug("In MVC:csi_v4l2_release_bufs\n"); + return 0; +} + +static int csi_v4l2_prepare_bufs(cam_data *cam, struct v4l2_buffer *buf) +{ + pr_debug("In MVC:csi_v4l2_prepare_bufs\n"); + + if (buf->index < 0 || buf->index >= FRAME_NUM || buf->length < + cam->v2f.fmt.pix.sizeimage) { + pr_err("ERROR: v4l2 capture: csi_v4l2_prepare_bufs buffers " + "not allocated,index=%d, length=%d\n", buf->index, + buf->length); + return -EINVAL; + } + + cam->frame[buf->index].buffer.index = buf->index; + cam->frame[buf->index].buffer.flags = V4L2_BUF_FLAG_MAPPED; + cam->frame[buf->index].buffer.length = buf->length; + cam->frame[buf->index].buffer.m.offset = cam->frame[buf->index].paddress + = buf->m.offset; + cam->frame[buf->index].buffer.type = buf->type; + cam->frame[buf->index].buffer.memory = V4L2_MEMORY_USERPTR; + cam->frame[buf->index].index = buf->index; + + return 0; +} + +/*! + * Indicates whether the palette is supported. + * + * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_UYVY or V4L2_PIX_FMT_YUV420 + * + * @return 0 if failed + */ +static inline int valid_mode(u32 palette) +{ + return (palette == V4L2_PIX_FMT_RGB565) || + (palette == V4L2_PIX_FMT_YUYV) || + (palette == V4L2_PIX_FMT_UYVY) || (palette == V4L2_PIX_FMT_YUV420); +} + +/*! + * Start stream I/O + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int csi_streamon(cam_data *cam) +{ + struct mxc_v4l_frame *frame; + unsigned long flags; + unsigned long val; + int timeout, timeout2; + + pr_debug("In MVC: %s\n", __func__); + + if (NULL == cam) { + pr_err("ERROR: v4l2 capture: %s cam parameter is NULL\n", + __func__); + return -1; + } + cam->dummy_frame.vaddress = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->dummy_frame.paddress, + GFP_DMA | GFP_KERNEL); + if (cam->dummy_frame.vaddress == 0) { + pr_err("ERROR: v4l2 capture: Allocate dummy frame " + "failed.\n"); + return -ENOBUFS; + } + cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE; + cam->dummy_frame.buffer.length = cam->v2f.fmt.pix.sizeimage; + cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; + + spin_lock_irqsave(&cam->queue_int_lock, flags); + /* move the frame from readyq to workingq */ + if (list_empty(&cam->ready_q)) { + pr_err("ERROR: v4l2 capture: %s: " + "ready_q queue empty\n", __func__); + spin_unlock_irqrestore(&cam->queue_int_lock, flags); + return -1; + } + frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&frame->queue, &cam->working_q); + __raw_writel(frame->paddress, CSI_CSIDMASA_FB1); + frame->csi_buf_num = 1; + + if (list_empty(&cam->ready_q)) { + pr_err("ERROR: v4l2 capture: %s: " + "ready_q queue empty\n", __func__); + spin_unlock_irqrestore(&cam->queue_int_lock, flags); + return -1; + } + frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&frame->queue, &cam->working_q); + __raw_writel(frame->paddress, CSI_CSIDMASA_FB2); + frame->csi_buf_num = 2; + spin_unlock_irqrestore(&cam->queue_int_lock, flags); + + cam->capture_pid = current->pid; + cam->capture_on = true; + csi_cap_image(cam); + + local_irq_save(flags); + for (timeout = 1000000; timeout > 0; timeout--) { + if (__raw_readl(CSI_CSISR) & BIT_SOF_INT) { + val = __raw_readl(CSI_CSICR3); + __raw_writel(val | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + for (timeout2 = 1000000; timeout2 > 0; timeout2--) { + if (__raw_readl(CSI_CSICR3) & + BIT_DMA_REFLASH_RFF) + cpu_relax(); + else + break; + } + if (timeout2 <= 0) { + pr_err("timeout when wait for reflash done.\n"); + local_irq_restore(flags); + return -ETIME; + } + + csi_dmareq_rff_enable(); + csi_enable_int(1); + break; + } else + cpu_relax(); + } + if (timeout <= 0) { + pr_err("timeout when wait for SOF\n"); + local_irq_restore(flags); + return -ETIME; + } + local_irq_restore(flags); + + return 0; +} + +/*! + * Stop stream I/O + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int csi_streamoff(cam_data *cam) +{ + pr_debug("In MVC: %s\n", __func__); + + if (cam->capture_on == false) + return 0; + + csi_dmareq_rff_disable(); + csi_disable_int(); + cam->capture_on = false; + + /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */ + __raw_writel(0, CSI_CSIDMASA_FB1); + __raw_writel(0, CSI_CSIDMASA_FB2); + + csi_free_frames(cam); + csi_free_frame_buf(cam); + + return 0; +} + +/*! + * start the viewfinder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int start_preview(cam_data *cam) +{ + unsigned long fb_addr = (unsigned long)cam->v4l2_fb.base; + + __raw_writel(fb_addr, CSI_CSIDMASA_FB1); + __raw_writel(fb_addr, CSI_CSIDMASA_FB2); + __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + + csi_enable_int(0); + + return 0; +} + +/*! + * shut down the viewfinder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int stop_preview(cam_data *cam) +{ + csi_disable_int(); + + /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */ + __raw_writel(0, CSI_CSIDMASA_FB1); + __raw_writel(0, CSI_CSIDMASA_FB2); + __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3); + + return 0; +} + +/*************************************************************************** + * VIDIOC Functions. + **************************************************************************/ + +/*! + * + * @param cam structure cam_data * + * + * @param f structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int csi_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f) +{ + int retval = 0; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + f->fmt.pix = cam->v2f.fmt.pix; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); + f->fmt.win = cam->win; + break; + default: + pr_debug(" type is invalid\n"); + retval = -EINVAL; + } + + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", + __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + + return retval; +} + +/*! + * V4L2 - csi_v4l2_s_fmt function + * + * @param cam structure cam_data * + * + * @param f structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) +{ + int retval = 0; + int size = 0; + int bytesperline = 0; + int *width, *height; + + pr_debug("In MVC: %s\n", __func__); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + if (!valid_mode(f->fmt.pix.pixelformat)) { + pr_err("ERROR: v4l2 capture: %s: format " + "not supported\n", __func__); + return -EINVAL; + } + + /* Handle case where size requested is larger than cuurent + * camera setting. */ + if ((f->fmt.pix.width > cam->crop_bounds.width) + || (f->fmt.pix.height > cam->crop_bounds.height)) { + /* Need the logic here, calling vidioc_s_param if + * camera can change. */ + pr_debug("csi_v4l2_s_fmt size changed\n"); + } + if (cam->rotation % 180) { + height = &f->fmt.pix.width; + width = &f->fmt.pix.height; + } else { + width = &f->fmt.pix.width; + height = &f->fmt.pix.height; + } + + if ((cam->crop_bounds.width / *width > 8) || + ((cam->crop_bounds.width / *width == 8) && + (cam->crop_bounds.width % *width))) { + *width = cam->crop_bounds.width / 8; + if (*width % 8) + *width += 8 - *width % 8; + pr_err("ERROR: v4l2 capture: width exceeds limit " + "resize to %d.\n", *width); + } + + if ((cam->crop_bounds.height / *height > 8) || + ((cam->crop_bounds.height / *height == 8) && + (cam->crop_bounds.height % *height))) { + *height = cam->crop_bounds.height / 8; + if (*height % 8) + *height += 8 - *height % 8; + pr_err("ERROR: v4l2 capture: height exceeds limit " + "resize to %d.\n", *height); + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + csi_init_format(V4L2_PIX_FMT_UYVY); + csi_set_16bit_imagpara(f->fmt.pix.width, + f->fmt.pix.height); + bytesperline = f->fmt.pix.width * 2; + break; + case V4L2_PIX_FMT_UYVY: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + csi_init_format(f->fmt.pix.pixelformat); + csi_set_16bit_imagpara(f->fmt.pix.width, + f->fmt.pix.height); + bytesperline = f->fmt.pix.width * 2; + break; + case V4L2_PIX_FMT_YUYV: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + csi_init_format(f->fmt.pix.pixelformat); + csi_set_16bit_imagpara(f->fmt.pix.width, + f->fmt.pix.height); + bytesperline = f->fmt.pix.width * 2; + break; + case V4L2_PIX_FMT_YUV420: + size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; + csi_set_12bit_imagpara(f->fmt.pix.width, + f->fmt.pix.height); + bytesperline = f->fmt.pix.width; + break; + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_NV12: + default: + pr_debug(" case not supported\n"); + break; + } + + if (f->fmt.pix.bytesperline < bytesperline) + f->fmt.pix.bytesperline = bytesperline; + else + bytesperline = f->fmt.pix.bytesperline; + + if (f->fmt.pix.sizeimage < size) + f->fmt.pix.sizeimage = size; + else + size = f->fmt.pix.sizeimage; + + cam->v2f.fmt.pix = f->fmt.pix; + + if (cam->v2f.fmt.pix.priv != 0) { + if (copy_from_user(&cam->offset, + (void *)cam->v2f.fmt.pix.priv, + sizeof(cam->offset))) { + retval = -EFAULT; + break; + } + } + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); + cam->win = f->fmt.win; + win_current = f->fmt.win; + size = win_current.w.width * win_current.w.height * 2; + if (cam->v2f.fmt.pix.sizeimage < size) + cam->v2f.fmt.pix.sizeimage = size; + + break; + default: + retval = -EINVAL; + } + + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", + __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + + return retval; +} + +/*! + * V4L2 - csi_v4l2_s_param function + * Allows setting of capturemode and frame rate. + * + * @param cam structure cam_data * + * @param parm structure v4l2_streamparm * + * + * @return status 0 success, EINVAL failed + */ +static int csi_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) +{ + struct v4l2_ifparm ifparm; + struct v4l2_format cam_fmt; + struct v4l2_streamparm currentparm; + int err = 0; + + pr_debug("In %s\n", __func__); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pr_err(KERN_ERR "%s invalid type\n", __func__); + return -EINVAL; + } + + /* Stop the viewfinder */ + if (cam->overlay_on == true) + stop_preview(cam); + + currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* First check that this device can support the changes requested. */ + err = vidioc_int_g_parm(cam->sensor, ¤tparm); + if (err) { + pr_err("%s: vidioc_int_g_parm returned an error %d\n", + __func__, err); + goto exit; + } + + pr_debug(" Current capabilities are %x\n", + currentparm.parm.capture.capability); + pr_debug(" Current capturemode is %d change to %d\n", + currentparm.parm.capture.capturemode, + parm->parm.capture.capturemode); + pr_debug(" Current framerate is %d change to %d\n", + currentparm.parm.capture.timeperframe.denominator, + parm->parm.capture.timeperframe.denominator); + + err = vidioc_int_s_parm(cam->sensor, parm); + if (err) { + pr_err("%s: vidioc_int_s_parm returned an error %d\n", + __func__, err); + goto exit; + } + + vidioc_int_g_ifparm(cam->sensor, &ifparm); + cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); + pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n", + cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); + + cam->crop_bounds.top = cam->crop_bounds.left = 0; + cam->crop_bounds.width = cam_fmt.fmt.pix.width; + cam->crop_bounds.height = cam_fmt.fmt.pix.height; + cam->crop_current.width = cam->crop_bounds.width; + cam->crop_current.height = cam->crop_bounds.height; + +exit: + return err; +} + +static int pxp_set_cstate(cam_data *cam, struct v4l2_control *vc) +{ + struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; + + if (vc->id == V4L2_CID_HFLIP) { + proc_data->hflip = vc->value; + } else if (vc->id == V4L2_CID_VFLIP) { + proc_data->vflip = vc->value; + } else if (vc->id == V4L2_CID_PRIVATE_BASE) { + if (vc->value % 90) + return -ERANGE; + proc_data->rotate = vc->value; + cam->rotation = vc->value; + } + + return 0; +} + +static int pxp_get_cstate(cam_data *cam, struct v4l2_control *vc) +{ + struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; + + if (vc->id == V4L2_CID_HFLIP) + vc->value = proc_data->hflip; + else if (vc->id == V4L2_CID_VFLIP) + vc->value = proc_data->vflip; + else if (vc->id == V4L2_CID_PRIVATE_BASE) + vc->value = proc_data->rotate; + + return 0; +} + + +/*! + * Dequeue one V4L capture buffer + * + * @param cam structure cam_data * + * @param buf structure v4l2_buffer * + * + * @return status 0 success, EINVAL invalid frame number + * ETIME timeout, ERESTARTSYS interrupted by user + */ +static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) +{ + int retval = 0; + struct mxc_v4l_frame *frame; + unsigned long lock_flags; + + if (!wait_event_interruptible_timeout(cam->enc_queue, + cam->enc_counter != 0, 10 * HZ)) { + pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout " + "enc_counter %x\n", cam->enc_counter); + return -ETIME; + } else if (signal_pending(current)) { + pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() " + "interrupt received\n"); + return -ERESTARTSYS; + } + + if (down_interruptible(&cam->busy_lock)) + return -EBUSY; + + spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags); + + if (list_empty(&cam->done_q)) { + spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags); + up(&cam->busy_lock); + return -EINVAL; + } + + cam->enc_counter--; + + frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue); + list_del(cam->done_q.next); + + if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) { + frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE; + } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { + pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: " + "Buffer not filled.\n"); + frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; + retval = -EINVAL; + } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) { + pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: " + "Buffer not queued.\n"); + retval = -EINVAL; + } + + spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags); + + buf->bytesused = cam->v2f.fmt.pix.sizeimage; + buf->index = frame->index; + buf->flags = frame->buffer.flags; + buf->m = cam->frame[frame->index].buffer.m; + + /* + * Note: + * If want to do preview on LCD, use PxP CSC to convert from UYVY + * to RGB565; but for encoding, usually we don't use RGB format. + */ + if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { + sg_dma_address(&cam->sg[0]) = buf->m.offset; + sg_dma_address(&cam->sg[1]) = + cam->frame[req_buf_number].paddress; + retval = pxp_process_update(cam); + if (retval) { + pr_err("Unable to submit PxP update task.\n"); + return retval; + } + pxp_complete_update(cam); + if (cam->frame[buf->index].vaddress) + memcpy(cam->frame[buf->index].vaddress, + cam->frame[req_buf_number].vaddress, + cam->v2f.fmt.pix.sizeimage); + } + up(&cam->busy_lock); + + return retval; +} + +/*! + * V4L interface - open function + * + * @param file structure file * + * + * @return status 0 success, ENODEV invalid device instance, + * ENODEV timeout, ERESTARTSYS interrupted by user + */ +static int csi_v4l_open(struct file *file) +{ + struct v4l2_ifparm ifparm; + struct v4l2_format cam_fmt; + struct video_device *dev = video_devdata(file); + cam_data *cam = video_get_drvdata(dev); + struct sensor_data *sensor; + int err = 0; + + pr_debug(" device name is %s\n", dev->name); + + if (!cam) { + pr_err("%s: Internal error, cam_data not found!\n", __func__); + return -EBADF; + } + + if (!cam->sensor) { + pr_err("%s: Internal error, camera is not found!\n", __func__); + return -EBADF; + } + + sensor = cam->sensor->priv; + if (!sensor) { + pr_err("%s: Internal error, sensor_data is not found!\n", __func__); + return -EBADF; + } + + down(&cam->busy_lock); + err = 0; + if (signal_pending(current)) + goto oops; + + if (cam->open_count++ == 0) { + wait_event_interruptible(cam->power_queue, + cam->low_power == false); + + cam->enc_counter = 0; + INIT_LIST_HEAD(&cam->ready_q); + INIT_LIST_HEAD(&cam->working_q); + INIT_LIST_HEAD(&cam->done_q); + + vidioc_int_g_ifparm(cam->sensor, &ifparm); + + cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + clk_prepare_enable(sensor->sensor_clk); + vidioc_int_s_power(cam->sensor, 1); + vidioc_int_init(cam->sensor); + vidioc_int_dev_init(cam->sensor); + } + + file->private_data = dev; + +oops: + up(&cam->busy_lock); + return err; +} + +/*! + * V4L interface - close function + * + * @param file struct file * + * + * @return 0 success + */ +static int csi_v4l_close(struct file *file) +{ + struct video_device *dev = video_devdata(file); + int err = 0; + cam_data *cam = video_get_drvdata(dev); + struct sensor_data *sensor; + + pr_debug("In MVC:%s\n", __func__); + + if (!cam) { + pr_err("%s: Internal error, cam_data not found!\n", __func__); + return -EBADF; + } + + if (!cam->sensor) { + pr_err("%s: Internal error, camera is not found!\n", __func__); + return -EBADF; + } + + sensor = cam->sensor->priv; + if (!sensor) { + pr_err("%s: Internal error, sensor_data is not found!\n", __func__); + return -EBADF; + } + + /* for the case somebody hit the ctrl C */ + if (cam->overlay_pid == current->pid) { + err = stop_preview(cam); + cam->overlay_on = false; + } + + if (--cam->open_count == 0) { + wait_event_interruptible(cam->power_queue, + cam->low_power == false); + file->private_data = NULL; + vidioc_int_s_power(cam->sensor, 0); + clk_disable_unprepare(sensor->sensor_clk); + } + + return err; +} + +/* + * V4L interface - read function + * + * @param file struct file * + * @param read buf char * + * @param count size_t + * @param ppos structure loff_t * + * + * @return bytes read + */ +static ssize_t csi_v4l_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int err = 0; + struct video_device *dev = video_devdata(file); + cam_data *cam = video_get_drvdata(dev); + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + /* Stop the viewfinder */ + if (cam->overlay_on == true) + stop_preview(cam); + + if (cam->still_buf_vaddr == NULL) { + cam->still_buf_vaddr = dma_alloc_coherent(0, + PAGE_ALIGN + (cam->v2f.fmt. + pix.sizeimage), + &cam-> + still_buf[0], + GFP_DMA | GFP_KERNEL); + if (cam->still_buf_vaddr == NULL) { + pr_err("alloc dma memory failed\n"); + return -ENOMEM; + } + cam->still_counter = 0; + __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB2); + __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB1); + __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, + CSI_CSICR3); + __raw_writel(__raw_readl(CSI_CSISR), CSI_CSISR); + __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, + CSI_CSICR3); + csi_enable_int(1); + } + + wait_event_interruptible(cam->still_queue, cam->still_counter); + csi_disable_int(); + err = copy_to_user(buf, cam->still_buf_vaddr, + cam->v2f.fmt.pix.sizeimage); + + if (cam->still_buf_vaddr != NULL) { + dma_free_coherent(0, PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + cam->still_buf_vaddr, cam->still_buf[0]); + cam->still_buf[0] = 0; + cam->still_buf_vaddr = NULL; + } + + if (cam->overlay_on == true) + start_preview(cam); + + up(&cam->busy_lock); + if (err < 0) + return err; + + return cam->v2f.fmt.pix.sizeimage - err; +} + +/*! + * V4L interface - ioctl function + * + * @param file struct file* + * + * @param ioctlnr unsigned int + * + * @param arg void* + * + * @return 0 success, ENODEV for invalid device instance, + * -1 for other errors. + */ +static long csi_v4l_do_ioctl(struct file *file, + unsigned int ioctlnr, void *arg) +{ + struct video_device *dev = video_devdata(file); + cam_data *cam = video_get_drvdata(dev); + int retval = 0; + unsigned long lock_flags; + + pr_debug("In MVC: %s, %x\n", __func__, ioctlnr); + wait_event_interruptible(cam->power_queue, cam->low_power == false); + /* make this _really_ smp-safe */ + if (ioctlnr != VIDIOC_DQBUF) + if (down_interruptible(&cam->busy_lock)) + return -EBUSY; + + switch (ioctlnr) { + /*! + * V4l2 VIDIOC_G_FMT ioctl + */ + case VIDIOC_G_FMT:{ + struct v4l2_format *gf = arg; + pr_debug(" case VIDIOC_G_FMT\n"); + retval = csi_v4l2_g_fmt(cam, gf); + break; + } + + /*! + * V4l2 VIDIOC_S_FMT ioctl + */ + case VIDIOC_S_FMT:{ + struct v4l2_format *sf = arg; + pr_debug(" case VIDIOC_S_FMT\n"); + retval = csi_v4l2_s_fmt(cam, sf); + vidioc_int_s_fmt_cap(cam->sensor, sf); + break; + } + + /*! + * V4l2 VIDIOC_OVERLAY ioctl + */ + case VIDIOC_OVERLAY:{ + int *on = arg; + pr_debug(" case VIDIOC_OVERLAY\n"); + if (*on) { + cam->overlay_on = true; + cam->overlay_pid = current->pid; + start_preview(cam); + } + if (!*on) { + stop_preview(cam); + cam->overlay_on = false; + } + break; + } + + /*! + * V4l2 VIDIOC_G_FBUF ioctl + */ + case VIDIOC_G_FBUF:{ + struct v4l2_framebuffer *fb = arg; + *fb = cam->v4l2_fb; + fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY; + break; + } + + /*! + * V4l2 VIDIOC_S_FBUF ioctl + */ + case VIDIOC_S_FBUF:{ + struct v4l2_framebuffer *fb = arg; + cam->v4l2_fb = *fb; + break; + } + + case VIDIOC_G_PARM:{ + struct v4l2_streamparm *parm = arg; + pr_debug(" case VIDIOC_G_PARM\n"); + vidioc_int_g_parm(cam->sensor, parm); + break; + } + + case VIDIOC_S_PARM:{ + struct v4l2_streamparm *parm = arg; + pr_debug(" case VIDIOC_S_PARM\n"); + retval = csi_v4l2_s_param(cam, parm); + break; + } + + case VIDIOC_QUERYCAP:{ + struct v4l2_capability *cap = arg; + pr_debug(" case VIDIOC_QUERYCAP\n"); + strcpy(cap->driver, "csi_v4l2"); + cap->version = KERNEL_VERSION(0, 1, 11); + cap->capabilities = V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_READWRITE; + cap->card[0] = '\0'; + cap->bus_info[0] = '\0'; + break; + } + + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *cap = arg; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + cap->bounds = cam->crop_bounds; + cap->defrect = cam->crop_defrect; + break; + } + case VIDIOC_S_CROP: + { + struct v4l2_crop *crop = arg; + struct v4l2_rect *b = &cam->crop_bounds; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + retval = -EINVAL; + break; + } + + crop->c.top = (crop->c.top < b->top) ? b->top + : crop->c.top; + if (crop->c.top > b->top + b->height) + crop->c.top = b->top + b->height - 1; + if (crop->c.height > b->top + b->height - crop->c.top) + crop->c.height = + b->top + b->height - crop->c.top; + + crop->c.left = (crop->c.left < b->left) ? b->left + : crop->c.left; + if (crop->c.left > b->left + b->width) + crop->c.left = b->left + b->width - 1; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = + b->left - crop->c.left + b->width; + + crop->c.width -= crop->c.width % 8; + crop->c.height -= crop->c.height % 8; + + crop_current.c = crop->c; + + break; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + retval = -EINVAL; + break; + } + crop->c = crop_current.c; + + break; + + } + case VIDIOC_REQBUFS: { + struct v4l2_requestbuffers *req = arg; + pr_debug(" case VIDIOC_REQBUFS\n"); + + if (req->count > FRAME_NUM) { + pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: " + "not enough buffers\n"); + req->count = FRAME_NUM; + } + + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: " + "wrong buffer type\n"); + retval = -EINVAL; + break; + } + + csi_streamoff(cam); + if (req->memory & V4L2_MEMORY_MMAP) { + csi_free_frame_buf(cam); + retval = csi_allocate_frame_buf(cam, req->count + 1); + req_buf_number = req->count; + } + break; + } + + case VIDIOC_QUERYBUF: { + struct v4l2_buffer *buf = arg; + int index = buf->index; + pr_debug(" case VIDIOC_QUERYBUF\n"); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + retval = -EINVAL; + break; + } + + if (buf->memory & V4L2_MEMORY_MMAP) { + memset(buf, 0, sizeof(buf)); + buf->index = index; + } + + down(&cam->param_lock); + if (buf->memory & V4L2_MEMORY_USERPTR) { + csi_v4l2_release_bufs(cam); + retval = csi_v4l2_prepare_bufs(cam, buf); + } + if (buf->memory & V4L2_MEMORY_MMAP) + retval = csi_v4l2_buffer_status(cam, buf); + up(&cam->param_lock); + break; + } + + case VIDIOC_QBUF: { + struct v4l2_buffer *buf = arg; + int index = buf->index; + pr_debug(" case VIDIOC_QBUF\n"); + + spin_lock_irqsave(&cam->queue_int_lock, lock_flags); + cam->frame[index].buffer.m.offset = buf->m.offset; + if ((cam->frame[index].buffer.flags & 0x7) == + V4L2_BUF_FLAG_MAPPED) { + cam->frame[index].buffer.flags |= V4L2_BUF_FLAG_QUEUED; + list_add_tail(&cam->frame[index].queue, &cam->ready_q); + } else if (cam->frame[index].buffer.flags & + V4L2_BUF_FLAG_QUEUED) { + pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: " + "buffer already queued\n"); + retval = -EINVAL; + } else if (cam->frame[index].buffer. + flags & V4L2_BUF_FLAG_DONE) { + pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: " + "overwrite done buffer.\n"); + cam->frame[index].buffer.flags &= + ~V4L2_BUF_FLAG_DONE; + cam->frame[index].buffer.flags |= + V4L2_BUF_FLAG_QUEUED; + retval = -EINVAL; + } + buf->flags = cam->frame[index].buffer.flags; + spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags); + + break; + } + + case VIDIOC_DQBUF: { + struct v4l2_buffer *buf = arg; + pr_debug(" case VIDIOC_DQBUF\n"); + + retval = csi_v4l_dqueue(cam, buf); + + break; + } + + case VIDIOC_STREAMON: { + pr_debug(" case VIDIOC_STREAMON\n"); + retval = csi_streamon(cam); + break; + } + + case VIDIOC_STREAMOFF: { + pr_debug(" case VIDIOC_STREAMOFF\n"); + retval = csi_streamoff(cam); + break; + } + case VIDIOC_ENUM_FMT: { + struct v4l2_fmtdesc *fmt = arg; + if (cam->sensor) + retval = vidioc_int_enum_fmt_cap(cam->sensor, fmt); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + case VIDIOC_ENUM_FRAMESIZES: { + struct v4l2_frmsizeenum *fsize = arg; + if (cam->sensor) + retval = vidioc_int_enum_framesizes(cam->sensor, fsize); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + case VIDIOC_ENUM_FRAMEINTERVALS: { + struct v4l2_frmivalenum *fival = arg; + if (cam->sensor) + retval = vidioc_int_enum_frameintervals(cam->sensor, + fival); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + case VIDIOC_DBG_G_CHIP_IDENT: { + struct v4l2_dbg_chip_ident *p = arg; + p->ident = V4L2_IDENT_NONE; + p->revision = 0; + if (cam->sensor) + retval = vidioc_int_g_chip_ident(cam->sensor, (int *)p); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + + case VIDIOC_S_CTRL: + { + struct v4l2_control *vc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (vc->id == pxp_controls[i].id) { + if (vc->value < pxp_controls[i].minimum || + vc->value > pxp_controls[i].maximum) { + retval = -ERANGE; + break; + } + retval = pxp_set_cstate(cam, vc); + break; + } + + if (i >= ARRAY_SIZE(pxp_controls)) + retval = -EINVAL; + break; + + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *vc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (vc->id == pxp_controls[i].id) { + retval = pxp_get_cstate(cam, vc); + break; + } + + if (i >= ARRAY_SIZE(pxp_controls)) + retval = -EINVAL; + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (qc->id && qc->id == pxp_controls[i].id) { + memcpy(qc, &(pxp_controls[i]), sizeof(*qc)); + break; + } + + if (i >= ARRAY_SIZE(pxp_controls)) + retval = -EINVAL; + break; + } + case VIDIOC_G_STD: + case VIDIOC_G_OUTPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_ENUMSTD: + case VIDIOC_S_STD: + case VIDIOC_TRY_FMT: + case VIDIOC_ENUMINPUT: + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + case VIDIOC_ENUMOUTPUT: + default: + pr_debug(" case not supported\n"); + retval = -EINVAL; + break; + } + + if (ioctlnr != VIDIOC_DQBUF) + up(&cam->busy_lock); + return retval; +} + +/* + * V4L interface - ioctl function + * + * @return None + */ +static long csi_v4l_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, csi_v4l_do_ioctl); +} + +/*! + * V4L interface - mmap function + * + * @param file structure file * + * + * @param vma structure vm_area_struct * + * + * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error + */ +static int csi_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = video_devdata(file); + unsigned long size; + int res = 0; + cam_data *cam = video_get_drvdata(dev); + + pr_debug("%s\n", __func__); + pr_debug("\npgoff=0x%lx, start=0x%lx, end=0x%lx\n", + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + size = vma->vm_end - vma->vm_start; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, + vma->vm_pgoff, size, vma->vm_page_prot)) { + pr_err("ERROR: v4l2 capture: %s : " + "remap_pfn_range failed\n", __func__); + res = -ENOBUFS; + goto csi_mmap_exit; + } + + vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ + +csi_mmap_exit: + up(&cam->busy_lock); + return res; +} + +/*! + * This structure defines the functions to be called in this driver. + */ +static struct v4l2_file_operations csi_v4l_fops = { + .owner = THIS_MODULE, + .open = csi_v4l_open, + .release = csi_v4l_close, + .read = csi_v4l_read, + .ioctl = csi_v4l_ioctl, + .mmap = csi_mmap, +}; + +static struct video_device csi_v4l_template = { + .name = "Mx25 Camera", + .fops = &csi_v4l_fops, + .release = video_device_release, +}; + +/*! + * initialize cam_data structure + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static void init_camera_struct(cam_data *cam) +{ + struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data; + pr_debug("In MVC: %s\n", __func__); + + proc_data->hflip = 0; + proc_data->vflip = 0; + proc_data->rotate = 0; + proc_data->bgcolor = 0; + + /* Default everything to 0 */ + memset(cam, 0, sizeof(cam_data)); + + sema_init(&cam->param_lock, 1); + sema_init(&cam->busy_lock, 1); + + cam->video_dev = video_device_alloc(); + if (cam->video_dev == NULL) + return; + + *(cam->video_dev) = csi_v4l_template; + + video_set_drvdata(cam->video_dev, cam); + cam->video_dev->minor = -1; + + init_waitqueue_head(&cam->enc_queue); + init_waitqueue_head(&cam->still_queue); + + cam->streamparm.parm.capture.capturemode = 0; + + cam->standard.index = 0; + cam->standard.id = V4L2_STD_UNKNOWN; + cam->standard.frameperiod.denominator = 30; + cam->standard.frameperiod.numerator = 1; + cam->standard.framelines = 480; + cam->standard_autodetect = true; + cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod; + cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + cam->overlay_on = false; + cam->capture_on = false; + cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY; + + cam->v2f.fmt.pix.sizeimage = 480 * 640 * 2; + cam->v2f.fmt.pix.bytesperline = 640 * 2; + cam->v2f.fmt.pix.width = 640; + cam->v2f.fmt.pix.height = 480; + cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + cam->win.w.width = 160; + cam->win.w.height = 160; + cam->win.w.left = 0; + cam->win.w.top = 0; + cam->still_counter = 0; + /* setup cropping */ + cam->crop_bounds.left = 0; + cam->crop_bounds.width = 640; + cam->crop_bounds.top = 0; + cam->crop_bounds.height = 480; + cam->crop_current = cam->crop_defrect = cam->crop_bounds; + + cam->enc_callback = camera_callback; + csi_start_callback(cam); + init_waitqueue_head(&cam->power_queue); + spin_lock_init(&cam->queue_int_lock); + spin_lock_init(&cam->dqueue_int_lock); +} + +/*! + * camera_power function + * Turns Sensor power On/Off + * + * @param cam cam data struct + * @param cameraOn true to turn camera on, false to turn off power. + * + * @return status + */ +static u8 camera_power(cam_data *cam, bool cameraOn) +{ + pr_debug("In MVC: %s on=%d\n", __func__, cameraOn); + + if (cameraOn == true) { + vidioc_int_s_power(cam->sensor, 1); + } else { + vidioc_int_s_power(cam->sensor, 0); + } + return 0; +} + +static const struct of_device_id imx_csi_v4l2_dt_ids[] = { + { .compatible = "fsl,imx6sl-csi-v4l2", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_csi_v4l2_dt_ids); + +static int csi_v4l2_probe(struct platform_device *pdev) +{ + struct scatterlist *sg; + u8 err = 0; + + /* Create g_cam and initialize it. */ + g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL); + if (g_cam == NULL) { + pr_err("ERROR: v4l2 capture: failed to register camera\n"); + err = -ENOMEM; + goto out; + } + memset(&crop_current, 0, sizeof(crop_current)); + memset(&win_current, 0, sizeof(win_current)); + init_camera_struct(g_cam); + platform_set_drvdata(pdev, (void *)g_cam); + + /* Set up the v4l2 device and register it */ + csi_v4l2_int_device.priv = g_cam; + /* This function contains a bug that won't let this be rmmod'd. */ + v4l2_int_device_register(&csi_v4l2_int_device); + + /* register v4l video device */ + if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr) + == -1) { + kfree(g_cam); + g_cam = NULL; + pr_err("ERROR: v4l2 capture: video_register_device failed\n"); + err = -ENODEV; + goto out; + } + pr_debug(" Video device registered: %s #%d\n", + g_cam->video_dev->name, g_cam->video_dev->minor); + + g_cam->pxp_chan = NULL; + /* Initialize Scatter-gather list containing 2 buffer addresses. */ + sg = g_cam->sg; + sg_init_table(sg, 2); + +out: + return err; +} + +static int csi_v4l2_remove(struct platform_device *pdev) +{ + if (g_cam->open_count) { + pr_err("ERROR: v4l2 capture:camera open " + "-- setting ops to NULL\n"); + } else { + pr_info("V4L2 freeing image input device\n"); + v4l2_int_device_unregister(&csi_v4l2_int_device); + csi_stop_callback(g_cam); + video_unregister_device(g_cam->video_dev); + platform_set_drvdata(pdev, NULL); + + kfree(g_cam); + g_cam = NULL; + } + + return 0; +} + +/*! + * This function is called to put the sensor in a low power state. + * Refer to the document driver-model/driver.txt in the kernel source tree + * for more information. + * + * @param pdev the device structure used to give information on which I2C + * to suspend + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure. + */ +static int csi_v4l2_suspend(struct platform_device *pdev, pm_message_t state) +{ + cam_data *cam = platform_get_drvdata(pdev); + + pr_debug("In MVC: %s\n", __func__); + + if (cam == NULL) + return -1; + + cam->low_power = true; + + if (cam->overlay_on == true) + stop_preview(cam); + + if (cam->capture_on == true || cam->overlay_on == true) + camera_power(cam, false); + + return 0; +} + +/*! + * This function is called to bring the sensor back from a low power state. + * Refer to the document driver-model/driver.txt in the kernel source tree + * for more information. + * + * @param pdev the device structure + * + * @return The function returns 0 on success and -1 on failure + */ +static int csi_v4l2_resume(struct platform_device *pdev) +{ + cam_data *cam = platform_get_drvdata(pdev); + + pr_debug("In MVC: %s\n", __func__); + + if (cam == NULL) + return -1; + + cam->low_power = false; + wake_up_interruptible(&cam->power_queue); + if (cam->capture_on == true || cam->overlay_on == true) + camera_power(cam, true); + + if (cam->overlay_on == true) + start_preview(cam); + + return 0; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver csi_v4l2_driver = { + .driver = { + .name = "csi_v4l2", + .of_match_table = of_match_ptr(imx_csi_v4l2_dt_ids), + }, + .probe = csi_v4l2_probe, + .remove = csi_v4l2_remove, +#ifdef CONFIG_PM + .suspend = csi_v4l2_suspend, + .resume = csi_v4l2_resume, +#endif + .shutdown = NULL, +}; + +/*! + * Initializes the camera driver. + */ +static int csi_v4l2_master_attach(struct v4l2_int_device *slave) +{ + cam_data *cam = slave->u.slave->master->priv; + struct v4l2_format cam_fmt; + + pr_debug("In MVC: %s\n", __func__); + pr_debug(" slave.name = %s\n", slave->name); + pr_debug(" master.name = %s\n", slave->u.slave->master->name); + + cam->sensor = slave; + if (slave == NULL) { + pr_err("ERROR: v4l2 capture: slave parameter not valid.\n"); + return -1; + } + + cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); + + /* Used to detect TV in (type 1) vs. camera (type 0) */ + cam->device_type = cam_fmt.fmt.pix.priv; + + cam->crop_bounds.top = cam->crop_bounds.left = 0; + cam->crop_bounds.width = cam_fmt.fmt.pix.width; + cam->crop_bounds.height = cam_fmt.fmt.pix.height; + + /* This also is the max crop size for this device. */ + cam->crop_defrect.top = cam->crop_defrect.left = 0; + cam->crop_defrect.width = cam_fmt.fmt.pix.width; + cam->crop_defrect.height = cam_fmt.fmt.pix.height; + + /* At this point, this is also the current image size. */ + cam->crop_current.top = cam->crop_current.left = 0; + cam->crop_current.width = cam_fmt.fmt.pix.width; + cam->crop_current.height = cam_fmt.fmt.pix.height; + + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", + __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + + return 0; +} + +/*! + * Disconnects the camera driver. + */ +static void csi_v4l2_master_detach(struct v4l2_int_device *slave) +{ + pr_debug("In MVC: %s\n", __func__); + + vidioc_int_dev_exit(slave); +} + +module_platform_driver(csi_v4l2_driver); + +module_param(video_nr, int, 0444); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("V4L2 capture driver for Mx25 based cameras"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/fsl_csi.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/fsl_csi.c --- linux-3.10.30/drivers/media/platform/mxc/capture/fsl_csi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/fsl_csi.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,302 @@ +/* + * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file fsl_csi.c, this file is derived from mx27_csi.c + * + * @brief mx25 CMOS Sensor interface functions + * + * @ingroup CSI + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mxc_v4l2_capture.h" +#include "fsl_csi.h" + +void __iomem *csi_regbase; +EXPORT_SYMBOL(csi_regbase); +static int irq_nr; +static csi_irq_callback_t g_callback; +static void *g_callback_data; + +static irqreturn_t csi_irq_handler(int irq, void *data) +{ + cam_data *cam = (cam_data *) data; + unsigned long status = __raw_readl(CSI_CSISR); + + __raw_writel(status, CSI_CSISR); + + if (status & BIT_HRESP_ERR_INT) + pr_warning("Hresponse error is detected.\n"); + + if (status & BIT_DMA_TSF_DONE_FB1) { + if (cam->capture_on) { + spin_lock(&cam->queue_int_lock); + cam->ping_pong_csi = 1; + spin_unlock(&cam->queue_int_lock); + cam->enc_callback(0, cam); + } else { + cam->still_counter++; + wake_up_interruptible(&cam->still_queue); + } + } + + if (status & BIT_DMA_TSF_DONE_FB2) { + if (cam->capture_on) { + spin_lock(&cam->queue_int_lock); + cam->ping_pong_csi = 2; + spin_unlock(&cam->queue_int_lock); + cam->enc_callback(0, cam); + } else { + cam->still_counter++; + wake_up_interruptible(&cam->still_queue); + } + } + + if (g_callback) + g_callback(g_callback_data, status); + + pr_debug("CSI status = 0x%08lX\n", status); + + return IRQ_HANDLED; +} + +static void csihw_reset_frame_count(void) +{ + __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3); +} + +static void csihw_reset(void) +{ + csihw_reset_frame_count(); + __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1); + __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2); + __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3); +} + +/*! + * csi_init_interface + * Init csi interface + */ +void csi_init_interface(void) +{ + unsigned int val = 0; + unsigned int imag_para; + + val |= BIT_SOF_POL; + val |= BIT_REDGE; + val |= BIT_GCLK_MODE; + val |= BIT_HSYNC_POL; + val |= BIT_PACK_DIR; + val |= BIT_FCC; + val |= BIT_SWAP16_EN; + val |= 1 << SHIFT_MCLKDIV; + val |= BIT_MCLKEN; + __raw_writel(val, CSI_CSICR1); + + imag_para = (640 << 16) | 960; + __raw_writel(imag_para, CSI_CSIIMAG_PARA); + + val = 0x1010; + val |= BIT_DMA_REFLASH_RFF; + __raw_writel(val, CSI_CSICR3); +} +EXPORT_SYMBOL(csi_init_interface); + +void csi_init_format(int fmt) +{ + unsigned int val; + + val = __raw_readl(CSI_CSICR1); + if (fmt == V4L2_PIX_FMT_YUYV) { + val &= ~BIT_PACK_DIR; + val &= ~BIT_SWAP16_EN; + } else if (fmt == V4L2_PIX_FMT_UYVY) { + val |= BIT_PACK_DIR; + val |= BIT_SWAP16_EN; + } else + pr_warning("unsupported format, old format remains.\n"); + + __raw_writel(val, CSI_CSICR1); +} +EXPORT_SYMBOL(csi_init_format); + +/*! + * csi_read_mclk_flag + * + * @return gcsi_mclk_source + */ +int csi_read_mclk_flag(void) +{ + return 0; +} +EXPORT_SYMBOL(csi_read_mclk_flag); + +void csi_start_callback(void *data) +{ + cam_data *cam = (cam_data *) data; + + if (request_irq(irq_nr, csi_irq_handler, 0, "csi", cam) < 0) + pr_debug("CSI error: irq request fail\n"); + +} +EXPORT_SYMBOL(csi_start_callback); + +void csi_stop_callback(void *data) +{ + cam_data *cam = (cam_data *) data; + + free_irq(irq_nr, cam); +} +EXPORT_SYMBOL(csi_stop_callback); + +void csi_enable_int(int arg) +{ + unsigned long cr1 = __raw_readl(CSI_CSICR1); + + cr1 |= BIT_SOF_INTEN; + if (arg == 1) { + /* still capture needs DMA intterrupt */ + cr1 |= BIT_FB1_DMA_DONE_INTEN; + cr1 |= BIT_FB2_DMA_DONE_INTEN; + } + __raw_writel(cr1, CSI_CSICR1); +} +EXPORT_SYMBOL(csi_enable_int); + +void csi_disable_int(void) +{ + unsigned long cr1 = __raw_readl(CSI_CSICR1); + + cr1 &= ~BIT_SOF_INTEN; + cr1 &= ~BIT_FB1_DMA_DONE_INTEN; + cr1 &= ~BIT_FB2_DMA_DONE_INTEN; + __raw_writel(cr1, CSI_CSICR1); +} +EXPORT_SYMBOL(csi_disable_int); + +void csi_set_16bit_imagpara(int width, int height) +{ + int imag_para = 0; + unsigned long cr3 = __raw_readl(CSI_CSICR3); + + imag_para = (width << 16) | (height * 2); + __raw_writel(imag_para, CSI_CSIIMAG_PARA); + + /* reflash the embeded DMA controller */ + __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3); +} +EXPORT_SYMBOL(csi_set_16bit_imagpara); + +void csi_set_12bit_imagpara(int width, int height) +{ + int imag_para = 0; + unsigned long cr3 = __raw_readl(CSI_CSICR3); + + imag_para = (width << 16) | (height * 3 / 2); + __raw_writel(imag_para, CSI_CSIIMAG_PARA); + + /* reflash the embeded DMA controller */ + __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3); +} +EXPORT_SYMBOL(csi_set_12bit_imagpara); + +void csi_dmareq_rff_enable(void) +{ + unsigned long cr3 = __raw_readl(CSI_CSICR3); + + cr3 |= BIT_DMA_REQ_EN_RFF; + cr3 |= BIT_HRESP_ERR_EN; + __raw_writel(cr3, CSI_CSICR3); +} +EXPORT_SYMBOL(csi_dmareq_rff_enable); + +void csi_dmareq_rff_disable(void) +{ + unsigned long cr3 = __raw_readl(CSI_CSICR3); + + cr3 &= ~BIT_DMA_REQ_EN_RFF; + cr3 &= ~BIT_HRESP_ERR_EN; + __raw_writel(cr3, CSI_CSICR3); +} +EXPORT_SYMBOL(csi_dmareq_rff_disable); + +static const struct of_device_id fsl_csi_dt_ids[] = { + { .compatible = "fsl,imx6sl-csi", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_csi_dt_ids); + +static int csi_probe(struct platform_device *pdev) +{ + int ret = 0; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "No csi irq found.\n"); + ret = -ENODEV; + goto err; + } + irq_nr = res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No csi base address found.\n"); + ret = -ENODEV; + goto err; + } + csi_regbase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!csi_regbase) { + dev_err(&pdev->dev, "ioremap failed with csi base\n"); + ret = -ENOMEM; + goto err; + } + + csihw_reset(); + csi_init_interface(); + csi_dmareq_rff_disable(); + +err: + return ret; +} + +static int csi_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver csi_driver = { + .driver = { + .name = "fsl_csi", + .of_match_table = of_match_ptr(fsl_csi_dt_ids), + }, + .probe = csi_probe, + .remove = csi_remove, +}; + +module_platform_driver(csi_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("fsl CSI driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/fsl_csi.h linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/fsl_csi.h --- linux-3.10.30/drivers/media/platform/mxc/capture/fsl_csi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/fsl_csi.h 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,198 @@ +/* + * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file fsl_csi.h + * + * @brief mx25 CMOS Sensor interface functions + * + * @ingroup CSI + */ + +#ifndef MX25_CSI_H +#define MX25_CSI_H + +#include + +/* reset values */ +#define CSICR1_RESET_VAL 0x40000800 +#define CSICR2_RESET_VAL 0x0 +#define CSICR3_RESET_VAL 0x0 + +/* csi control reg 1 */ +#define BIT_SWAP16_EN (0x1 << 31) +#define BIT_EXT_VSYNC (0x1 << 30) +#define BIT_EOF_INT_EN (0x1 << 29) +#define BIT_PRP_IF_EN (0x1 << 28) +#define BIT_CCIR_MODE (0x1 << 27) +#define BIT_COF_INT_EN (0x1 << 26) +#define BIT_SF_OR_INTEN (0x1 << 25) +#define BIT_RF_OR_INTEN (0x1 << 24) +#define BIT_SFF_DMA_DONE_INTEN (0x1 << 22) +#define BIT_STATFF_INTEN (0x1 << 21) +#define BIT_FB2_DMA_DONE_INTEN (0x1 << 20) +#define BIT_FB1_DMA_DONE_INTEN (0x1 << 19) +#define BIT_RXFF_INTEN (0x1 << 18) +#define BIT_SOF_POL (0x1 << 17) +#define BIT_SOF_INTEN (0x1 << 16) +#define BIT_MCLKDIV (0xF << 12) +#define BIT_HSYNC_POL (0x1 << 11) +#define BIT_CCIR_EN (0x1 << 10) +#define BIT_MCLKEN (0x1 << 9) +#define BIT_FCC (0x1 << 8) +#define BIT_PACK_DIR (0x1 << 7) +#define BIT_CLR_STATFIFO (0x1 << 6) +#define BIT_CLR_RXFIFO (0x1 << 5) +#define BIT_GCLK_MODE (0x1 << 4) +#define BIT_INV_DATA (0x1 << 3) +#define BIT_INV_PCLK (0x1 << 2) +#define BIT_REDGE (0x1 << 1) +#define BIT_PIXEL_BIT (0x1 << 0) + +#define SHIFT_MCLKDIV 12 + +/* control reg 3 */ +#define BIT_FRMCNT (0xFFFF << 16) +#define BIT_FRMCNT_RST (0x1 << 15) +#define BIT_DMA_REFLASH_RFF (0x1 << 14) +#define BIT_DMA_REFLASH_SFF (0x1 << 13) +#define BIT_DMA_REQ_EN_RFF (0x1 << 12) +#define BIT_DMA_REQ_EN_SFF (0x1 << 11) +#define BIT_STATFF_LEVEL (0x7 << 8) +#define BIT_HRESP_ERR_EN (0x1 << 7) +#define BIT_RXFF_LEVEL (0x7 << 4) +#define BIT_TWO_8BIT_SENSOR (0x1 << 3) +#define BIT_ZERO_PACK_EN (0x1 << 2) +#define BIT_ECC_INT_EN (0x1 << 1) +#define BIT_ECC_AUTO_EN (0x1 << 0) + +#define SHIFT_FRMCNT 16 + +/* csi status reg */ +#define BIT_SFF_OR_INT (0x1 << 25) +#define BIT_RFF_OR_INT (0x1 << 24) +#define BIT_DMA_TSF_DONE_SFF (0x1 << 22) +#define BIT_STATFF_INT (0x1 << 21) +#define BIT_DMA_TSF_DONE_FB2 (0x1 << 20) +#define BIT_DMA_TSF_DONE_FB1 (0x1 << 19) +#define BIT_RXFF_INT (0x1 << 18) +#define BIT_EOF_INT (0x1 << 17) +#define BIT_SOF_INT (0x1 << 16) +#define BIT_F2_INT (0x1 << 15) +#define BIT_F1_INT (0x1 << 14) +#define BIT_COF_INT (0x1 << 13) +#define BIT_HRESP_ERR_INT (0x1 << 7) +#define BIT_ECC_INT (0x1 << 1) +#define BIT_DRDY (0x1 << 0) + +#define CSI_MCLK_VF 1 +#define CSI_MCLK_ENC 2 +#define CSI_MCLK_RAW 4 +#define CSI_MCLK_I2C 8 +#endif + +extern void __iomem *csi_regbase; +#define CSI_CSICR1 (csi_regbase) +#define CSI_CSICR2 (csi_regbase + 0x4) +#define CSI_CSICR3 (csi_regbase + 0x8) +#define CSI_STATFIFO (csi_regbase + 0xC) +#define CSI_CSIRXFIFO (csi_regbase + 0x10) +#define CSI_CSIRXCNT (csi_regbase + 0x14) +#define CSI_CSISR (csi_regbase + 0x18) + +#define CSI_CSIDBG (csi_regbase + 0x1C) +#define CSI_CSIDMASA_STATFIFO (csi_regbase + 0x20) +#define CSI_CSIDMATS_STATFIFO (csi_regbase + 0x24) +#define CSI_CSIDMASA_FB1 (csi_regbase + 0x28) +#define CSI_CSIDMASA_FB2 (csi_regbase + 0x2C) +#define CSI_CSIFBUF_PARA (csi_regbase + 0x30) +#define CSI_CSIIMAG_PARA (csi_regbase + 0x34) + +static inline void csi_clear_status(unsigned long status) +{ + __raw_writel(status, CSI_CSISR); +} + +struct csi_signal_cfg_t { + unsigned data_width:3; + unsigned clk_mode:2; + unsigned ext_vsync:1; + unsigned Vsync_pol:1; + unsigned Hsync_pol:1; + unsigned pixclk_pol:1; + unsigned data_pol:1; + unsigned sens_clksrc:1; +}; + +struct csi_config_t { + /* control reg 1 */ + unsigned int swap16_en:1; + unsigned int ext_vsync:1; + unsigned int eof_int_en:1; + unsigned int prp_if_en:1; + unsigned int ccir_mode:1; + unsigned int cof_int_en:1; + unsigned int sf_or_inten:1; + unsigned int rf_or_inten:1; + unsigned int sff_dma_done_inten:1; + unsigned int statff_inten:1; + unsigned int fb2_dma_done_inten:1; + unsigned int fb1_dma_done_inten:1; + unsigned int rxff_inten:1; + unsigned int sof_pol:1; + unsigned int sof_inten:1; + unsigned int mclkdiv:4; + unsigned int hsync_pol:1; + unsigned int ccir_en:1; + unsigned int mclken:1; + unsigned int fcc:1; + unsigned int pack_dir:1; + unsigned int gclk_mode:1; + unsigned int inv_data:1; + unsigned int inv_pclk:1; + unsigned int redge:1; + unsigned int pixel_bit:1; + + /* control reg 3 */ + unsigned int frmcnt:16; + unsigned int frame_reset:1; + unsigned int dma_reflash_rff:1; + unsigned int dma_reflash_sff:1; + unsigned int dma_req_en_rff:1; + unsigned int dma_req_en_sff:1; + unsigned int statff_level:3; + unsigned int hresp_err_en:1; + unsigned int rxff_level:3; + unsigned int two_8bit_sensor:1; + unsigned int zero_pack_en:1; + unsigned int ecc_int_en:1; + unsigned int ecc_auto_en:1; + /* fifo counter */ + unsigned int rxcnt; +}; + +typedef void (*csi_irq_callback_t) (void *data, unsigned long status); + +void csi_init_interface(void); +void csi_init_format(int fmt); +void csi_set_16bit_imagpara(int width, int height); +void csi_set_12bit_imagpara(int width, int height); +int csi_read_mclk_flag(void); +void csi_start_callback(void *data); +void csi_stop_callback(void *data); +void csi_enable_int(int arg); +void csi_disable_int(void); +void csi_mclk_enable(void); +void csi_mclk_disable(void); +void csi_dmareq_rff_enable(void); +void csi_dmareq_rff_disable(void); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,554 @@ + +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ipu_bg_overlay_sdc_bg.c + * + * @brief IPU Use case for PRP-VF back-ground + * + * @ingroup IPU + */ +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +static int csi_buffer_num; +static u32 bpp, csi_mem_bufsize = 3; +static u32 out_format; +static struct ipu_soc *disp_ipu; +static u32 offset; + +static void csi_buf_work_func(struct work_struct *work) +{ + int err = 0; + cam_data *cam = + container_of(work, struct _cam_data, csi_work_struct); + + struct ipu_task task; + memset(&task, 0, sizeof(task)); + + if (csi_buffer_num) + task.input.paddr = cam->vf_bufs[0]; + else + task.input.paddr = cam->vf_bufs[1]; + task.input.width = cam->crop_current.width; + task.input.height = cam->crop_current.height; + task.input.format = IPU_PIX_FMT_UYVY; + + task.output.paddr = offset; + task.output.width = cam->overlay_fb->var.xres; + task.output.height = cam->overlay_fb->var.yres; + task.output.format = out_format; + task.output.rotate = cam->rotation; + task.output.crop.pos.x = cam->win.w.left; + task.output.crop.pos.y = cam->win.w.top; + if (cam->win.w.width > 1024 || cam->win.w.height > 1024) { + task.output.crop.w = cam->overlay_fb->var.xres; + task.output.crop.h = cam->overlay_fb->var.yres; + } else { + task.output.crop.w = cam->win.w.width; + task.output.crop.h = cam->win.w.height; + } +again: + err = ipu_check_task(&task); + if (err != IPU_CHECK_OK) { + if (err > IPU_CHECK_ERR_MIN) { + if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) { + task.input.crop.w -= 8; + goto again; + } + if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) { + task.input.crop.h -= 8; + goto again; + } + if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { + task.output.width -= 8; + task.output.crop.w = task.output.width; + goto again; + } + if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { + task.output.height -= 8; + task.output.crop.h = task.output.height; + goto again; + } + printk(KERN_ERR "check ipu taks fail\n"); + return; + } + printk(KERN_ERR "check ipu taks fail\n"); + return; + } + err = ipu_queue_task(&task); + if (err < 0) + printk(KERN_ERR "queue ipu task error\n"); +} + +static void get_disp_ipu(cam_data *cam) +{ + if (cam->output > 2) + disp_ipu = ipu_get_soc(1); /* using DISP4 */ + else + disp_ipu = ipu_get_soc(0); +} + + +/*! + * csi ENC callback function. + * + * @param irq int irq line + * @param dev_id void * device id + * + * @return status IRQ_HANDLED for handled + */ +static irqreturn_t csi_enc_callback(int irq, void *dev_id) +{ + cam_data *cam = (cam_data *) dev_id; + + ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, csi_buffer_num); + schedule_work(&cam->csi_work_struct); + csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0; + return IRQ_HANDLED; +} + +static int csi_enc_setup(cam_data *cam) +{ + ipu_channel_params_t params; + u32 pixel_fmt; + int err = 0, sensor_protocol = 0; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + if (!cam) { + printk(KERN_ERR "cam private is NULL\n"); + return -ENXIO; + } + + memset(¶ms, 0, sizeof(ipu_channel_params_t)); + params.csi_mem.csi = cam->csi; + + sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi); + switch (sensor_protocol) { + case IPU_CSI_CLK_MODE_GATED_CLK: + case IPU_CSI_CLK_MODE_NONGATED_CLK: + case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: + case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: + case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: + params.csi_mem.interlaced = false; + break; + case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: + case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: + case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: + params.csi_mem.interlaced = true; + break; + default: + printk(KERN_ERR "sensor protocol unsupported\n"); + return -EINVAL; + } + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) { + params.csi_mem.mipi_en = true; + params.csi_mem.mipi_vc = + mipi_csi2_get_virtual_channel(mipi_csi2_info); + params.csi_mem.mipi_id = + mipi_csi2_get_datatype(mipi_csi2_info); + + mipi_csi2_pixelclk_enable(mipi_csi2_info); + } else { + params.csi_mem.mipi_en = false; + params.csi_mem.mipi_vc = 0; + params.csi_mem.mipi_id = 0; + } + } else { + params.csi_mem.mipi_en = false; + params.csi_mem.mipi_vc = 0; + params.csi_mem.mipi_id = 0; + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], + (dma_addr_t) cam->vf_bufs[0]); + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], + (dma_addr_t) cam->vf_bufs[1]); + } + csi_mem_bufsize = + cam->crop_current.width * cam->crop_current.height * 2; + cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize); + cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0, + cam->vf_bufs_size[0], + (dma_addr_t *) & + cam->vf_bufs[0], + GFP_DMA | + GFP_KERNEL); + if (cam->vf_bufs_vaddr[0] == NULL) { + printk(KERN_ERR "Error to allocate vf buffer\n"); + err = -ENOMEM; + goto out_2; + } + cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize); + cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0, + cam->vf_bufs_size[1], + (dma_addr_t *) & + cam->vf_bufs[1], + GFP_DMA | + GFP_KERNEL); + if (cam->vf_bufs_vaddr[1] == NULL) { + printk(KERN_ERR "Error to allocate vf buffer\n"); + err = -ENOMEM; + goto out_1; + } + pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]); + + err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); + if (err != 0) { + printk(KERN_ERR "ipu_init_channel %d\n", err); + goto out_1; + } + + pixel_fmt = IPU_PIX_FMT_UYVY; + err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, + pixel_fmt, cam->crop_current.width, + cam->crop_current.height, + cam->crop_current.width, IPU_ROTATE_NONE, + cam->vf_bufs[0], cam->vf_bufs[1], 0, + cam->offset.u_offset, cam->offset.u_offset); + if (err != 0) { + printk(KERN_ERR "CSI_MEM output buffer\n"); + goto out_1; + } + err = ipu_enable_channel(cam->ipu, CSI_MEM); + if (err < 0) { + printk(KERN_ERR "ipu_enable_channel CSI_MEM\n"); + goto out_1; + } + + csi_buffer_num = 0; + + ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0); + ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 1); + return err; +out_1: + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], + (dma_addr_t) cam->vf_bufs[0]); + cam->vf_bufs_vaddr[0] = NULL; + cam->vf_bufs[0] = 0; + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], + (dma_addr_t) cam->vf_bufs[1]); + cam->vf_bufs_vaddr[1] = NULL; + cam->vf_bufs[1] = 0; + } +out_2: + return err; +} + +/*! + * Enable encoder task + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int csi_enc_enabling_tasks(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + + ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); + err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, + csi_enc_callback, 0, "Mxc Camera", cam); + if (err != 0) { + printk(KERN_ERR "Error registering CSI0_OUT_EOF irq\n"); + return err; + } + + INIT_WORK(&cam->csi_work_struct, csi_buf_work_func); + + err = csi_enc_setup(cam); + if (err != 0) { + printk(KERN_ERR "csi_enc_setup %d\n", err); + goto out1; + } + + return err; +out1: + ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); + return err; +} + +/*! + * bg_overlay_start - start the overlay task + * + * @param private cam_data * mxc v4l2 main structure + * + */ +static int bg_overlay_start(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + + if (!cam) { + printk(KERN_ERR "private is NULL\n"); + return -EIO; + } + + if (cam->overlay_active == true) { + pr_debug("already start.\n"); + return 0; + } + + get_disp_ipu(cam); + + out_format = cam->v4l2_fb.fmt.pixelformat; + if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) { + bpp = 3, csi_mem_bufsize = 3; + pr_info("BGR24\n"); + } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) { + bpp = 2, csi_mem_bufsize = 2; + pr_info("RGB565\n"); + } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) { + bpp = 4, csi_mem_bufsize = 4; + pr_info("BGR32\n"); + } else { + printk(KERN_ERR + "unsupported fix format from the framebuffer.\n"); + return -EINVAL; + } + + offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top + + csi_mem_bufsize * cam->win.w.left; + + if (cam->v4l2_fb.base == 0) + printk(KERN_ERR "invalid frame buffer address.\n"); + else + offset += (u32) cam->v4l2_fb.base; + + csi_mem_bufsize = cam->win.w.width * cam->win.w.height + * csi_mem_bufsize; + + err = csi_enc_enabling_tasks(cam); + if (err != 0) { + printk(KERN_ERR "Error csi enc enable fail\n"); + return err; + } + + cam->overlay_active = true; + return err; +} + +/*! + * bg_overlay_stop - stop the overlay task + * + * @param private cam_data * mxc v4l2 main structure + * + */ +static int bg_overlay_stop(void *private) +{ + int err = 0; + cam_data *cam = (cam_data *) private; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + if (cam->overlay_active == false) + return 0; + + err = ipu_disable_channel(cam->ipu, CSI_MEM, true); + + ipu_uninit_channel(cam->ipu, CSI_MEM); + + csi_buffer_num = 0; + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) + mipi_csi2_pixelclk_disable(mipi_csi2_info); + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + flush_work(&cam->csi_work_struct); + cancel_work_sync(&cam->csi_work_struct); + + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], cam->vf_bufs[0]); + cam->vf_bufs_vaddr[0] = NULL; + cam->vf_bufs[0] = 0; + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], cam->vf_bufs[1]); + cam->vf_bufs_vaddr[1] = NULL; + cam->vf_bufs[1] = 0; + } + if (cam->rot_vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->rot_vf_buf_size[0], + cam->rot_vf_bufs_vaddr[0], + cam->rot_vf_bufs[0]); + cam->rot_vf_bufs_vaddr[0] = NULL; + cam->rot_vf_bufs[0] = 0; + } + if (cam->rot_vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->rot_vf_buf_size[1], + cam->rot_vf_bufs_vaddr[1], + cam->rot_vf_bufs[1]); + cam->rot_vf_bufs_vaddr[1] = NULL; + cam->rot_vf_bufs[1] = 0; + } + + cam->overlay_active = false; + return err; +} + +/*! + * Enable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int bg_overlay_enable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + return ipu_enable_csi(cam->ipu, cam->csi); +} + +/*! + * Disable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int bg_overlay_disable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + /* free csi eof irq firstly. + * when disable csi, wait for idmac eof. + * it requests eof irq again */ + ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); + + return ipu_disable_csi(cam->ipu, cam->csi); +} + +/*! + * function to select bg as the working path + * + * @param private cam_data * mxc v4l2 main structure + * + * @return status + */ +int bg_overlay_sdc_select(void *private) +{ + cam_data *cam = (cam_data *) private; + + if (cam) { + cam->vf_start_sdc = bg_overlay_start; + cam->vf_stop_sdc = bg_overlay_stop; + cam->vf_enable_csi = bg_overlay_enable_csi; + cam->vf_disable_csi = bg_overlay_disable_csi; + cam->overlay_active = false; + } + + return 0; +} +EXPORT_SYMBOL(bg_overlay_sdc_select); + +/*! + * function to de-select bg as the working path + * + * @param private cam_data * mxc v4l2 main structure + * + * @return status + */ +int bg_overlay_sdc_deselect(void *private) +{ + cam_data *cam = (cam_data *) private; + + if (cam) { + cam->vf_start_sdc = NULL; + cam->vf_stop_sdc = NULL; + cam->vf_enable_csi = NULL; + cam->vf_disable_csi = NULL; + } + return 0; +} +EXPORT_SYMBOL(bg_overlay_sdc_deselect); + +/*! + * Init background overlay task. + * + * @return Error code indicating success or failure + */ +__init int bg_overlay_sdc_init(void) +{ + return 0; +} + +/*! + * Deinit background overlay task. + * + * @return Error code indicating success or failure + */ +void __exit bg_overlay_sdc_exit(void) +{ +} + +module_init(bg_overlay_sdc_init); +module_exit(bg_overlay_sdc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ipu_csi_enc.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_csi_enc.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ipu_csi_enc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_csi_enc.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,426 @@ +/* + * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ipu_csi_enc.c + * + * @brief CSI Use case for video capture + * + * @ingroup IPU + */ + +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +#ifdef CAMERA_DBG + #define CAMERA_TRACE(x) (printk)x +#else + #define CAMERA_TRACE(x) +#endif + +/* + * Function definitions + */ + +/*! + * csi ENC callback function. + * + * @param irq int irq line + * @param dev_id void * device id + * + * @return status IRQ_HANDLED for handled + */ +static irqreturn_t csi_enc_callback(int irq, void *dev_id) +{ + cam_data *cam = (cam_data *) dev_id; + + if (cam->enc_callback == NULL) + return IRQ_HANDLED; + + cam->enc_callback(irq, dev_id); + return IRQ_HANDLED; +} + +/*! + * CSI ENC enable channel setup function + * + * @param cam struct cam_data * mxc capture instance + * + * @return status + */ +static int csi_enc_setup(cam_data *cam) +{ + ipu_channel_params_t params; + u32 pixel_fmt; + int err = 0, sensor_protocol = 0; + dma_addr_t dummy = cam->dummy_frame.buffer.m.offset; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + CAMERA_TRACE("In csi_enc_setup\n"); + if (!cam) { + printk(KERN_ERR "cam private is NULL\n"); + return -ENXIO; + } + + memset(¶ms, 0, sizeof(ipu_channel_params_t)); + params.csi_mem.csi = cam->csi; + + sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi); + switch (sensor_protocol) { + case IPU_CSI_CLK_MODE_GATED_CLK: + case IPU_CSI_CLK_MODE_NONGATED_CLK: + case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: + case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: + case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: + params.csi_mem.interlaced = false; + break; + case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: + case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: + case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: + params.csi_mem.interlaced = true; + break; + default: + printk(KERN_ERR "sensor protocol unsupported\n"); + return -EINVAL; + } + + if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + pixel_fmt = IPU_PIX_FMT_YUV420P; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) + pixel_fmt = IPU_PIX_FMT_YVU420P; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) + pixel_fmt = IPU_PIX_FMT_YUV422P; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) + pixel_fmt = IPU_PIX_FMT_UYVY; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pixel_fmt = IPU_PIX_FMT_YUYV; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) + pixel_fmt = IPU_PIX_FMT_NV12; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) + pixel_fmt = IPU_PIX_FMT_BGR24; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) + pixel_fmt = IPU_PIX_FMT_RGB24; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) + pixel_fmt = IPU_PIX_FMT_RGB565; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) + pixel_fmt = IPU_PIX_FMT_BGR32; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) + pixel_fmt = IPU_PIX_FMT_RGB32; + else { + printk(KERN_ERR "format not supported\n"); + return -EINVAL; + } + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) { + params.csi_mem.mipi_en = true; + params.csi_mem.mipi_vc = + mipi_csi2_get_virtual_channel(mipi_csi2_info); + params.csi_mem.mipi_id = + mipi_csi2_get_datatype(mipi_csi2_info); + + mipi_csi2_pixelclk_enable(mipi_csi2_info); + } else { + params.csi_mem.mipi_en = false; + params.csi_mem.mipi_vc = 0; + params.csi_mem.mipi_id = 0; + } + } else { + params.csi_mem.mipi_en = false; + params.csi_mem.mipi_vc = 0; + params.csi_mem.mipi_id = 0; + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); + if (err != 0) { + printk(KERN_ERR "ipu_init_channel %d\n", err); + return err; + } + + err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, + pixel_fmt, cam->v2f.fmt.pix.width, + cam->v2f.fmt.pix.height, + cam->v2f.fmt.pix.bytesperline, + IPU_ROTATE_NONE, + dummy, dummy, 0, + cam->offset.u_offset, + cam->offset.v_offset); + if (err != 0) { + printk(KERN_ERR "CSI_MEM output buffer\n"); + return err; + } + err = ipu_enable_channel(cam->ipu, CSI_MEM); + if (err < 0) { + printk(KERN_ERR "ipu_enable_channel CSI_MEM\n"); + return err; + } + + return err; +} + +/*! + * function to update physical buffer address for encorder IDMA channel + * + * @param eba physical buffer address for encorder IDMA channel + * @param buffer_num int buffer 0 or buffer 1 + * + * @return status + */ +static int csi_enc_eba_update(struct ipu_soc *ipu, dma_addr_t eba, + int *buffer_num) +{ + int err = 0; + + pr_debug("eba %x\n", eba); + err = ipu_update_channel_buffer(ipu, CSI_MEM, IPU_OUTPUT_BUFFER, + *buffer_num, eba); + if (err != 0) { + ipu_clear_buffer_ready(ipu, CSI_MEM, IPU_OUTPUT_BUFFER, + *buffer_num); + + err = ipu_update_channel_buffer(ipu, CSI_MEM, IPU_OUTPUT_BUFFER, + *buffer_num, eba); + if (err != 0) { + pr_err("ERROR: v4l2 capture: fail to update " + "buf%d\n", *buffer_num); + return err; + } + } + + ipu_select_buffer(ipu, CSI_MEM, IPU_OUTPUT_BUFFER, *buffer_num); + + *buffer_num = (*buffer_num == 0) ? 1 : 0; + + return 0; +} + +/*! + * Enable encoder task + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int csi_enc_enabling_tasks(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n"); + + cam->dummy_frame.vaddress = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->dummy_frame.paddress, + GFP_DMA | GFP_KERNEL); + if (cam->dummy_frame.vaddress == 0) { + pr_err("ERROR: v4l2 capture: Allocate dummy frame " + "failed.\n"); + return -ENOBUFS; + } + cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE; + cam->dummy_frame.buffer.length = + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); + cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; + + ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); + err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, + csi_enc_callback, 0, "Mxc Camera", cam); + if (err != 0) { + printk(KERN_ERR "Error registering rot irq\n"); + return err; + } + + err = csi_enc_setup(cam); + if (err != 0) { + printk(KERN_ERR "csi_enc_setup %d\n", err); + return err; + } + + return err; +} + +/*! + * Disable encoder task + * @param private struct cam_data * mxc capture instance + * + * @return int + */ +static int csi_enc_disabling_tasks(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + err = ipu_disable_channel(cam->ipu, CSI_MEM, true); + + ipu_uninit_channel(cam->ipu, CSI_MEM); + + if (cam->dummy_frame.vaddress != 0) { + dma_free_coherent(0, cam->dummy_frame.buffer.length, + cam->dummy_frame.vaddress, + cam->dummy_frame.paddress); + cam->dummy_frame.vaddress = 0; + } + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) + mipi_csi2_pixelclk_disable(mipi_csi2_info); + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + return err; +} + +/*! + * Enable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int csi_enc_enable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + return ipu_enable_csi(cam->ipu, cam->csi); +} + +/*! + * Disable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int csi_enc_disable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + /* free csi eof irq firstly. + * when disable csi, wait for idmac eof. + * it requests eof irq again */ + ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); + + return ipu_disable_csi(cam->ipu, cam->csi); +} + +/*! + * function to select CSI ENC as the working path + * + * @param private struct cam_data * mxc capture instance + * + * @return int + */ +int csi_enc_select(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + + if (cam) { + cam->enc_update_eba = csi_enc_eba_update; + cam->enc_enable = csi_enc_enabling_tasks; + cam->enc_disable = csi_enc_disabling_tasks; + cam->enc_enable_csi = csi_enc_enable_csi; + cam->enc_disable_csi = csi_enc_disable_csi; + } else { + err = -EIO; + } + + return err; +} +EXPORT_SYMBOL(csi_enc_select); + +/*! + * function to de-select CSI ENC as the working path + * + * @param private struct cam_data * mxc capture instance + * + * @return int + */ +int csi_enc_deselect(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + + if (cam) { + cam->enc_update_eba = NULL; + cam->enc_enable = NULL; + cam->enc_disable = NULL; + cam->enc_enable_csi = NULL; + cam->enc_disable_csi = NULL; + } + + return err; +} +EXPORT_SYMBOL(csi_enc_deselect); + +/*! + * Init the Encorder channels + * + * @return Error code indicating success or failure + */ +__init int csi_enc_init(void) +{ + return 0; +} + +/*! + * Deinit the Encorder channels + * + */ +void __exit csi_enc_exit(void) +{ +} + +module_init(csi_enc_init); +module_exit(csi_enc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("CSI ENC Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,642 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ +/* * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ipu_foreground_sdc.c + * + * @brief IPU Use case for PRP-VF + * + * @ingroup IPU + */ + +#include +#include +#include +#include +#include +#include + +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +#ifdef CAMERA_DBG + #define CAMERA_TRACE(x) (printk)x +#else + #define CAMERA_TRACE(x) +#endif + +static int csi_buffer_num, buffer_num; +static u32 csi_mem_bufsize; +static struct ipu_soc *disp_ipu; +static struct fb_info *fbi; +static struct fb_var_screeninfo fbvar; +static u32 vf_out_format; +static void csi_buf_work_func(struct work_struct *work) +{ + int err = 0; + cam_data *cam = + container_of(work, struct _cam_data, csi_work_struct); + + struct ipu_task task; + memset(&task, 0, sizeof(task)); + + if (csi_buffer_num) + task.input.paddr = cam->vf_bufs[0]; + else + task.input.paddr = cam->vf_bufs[1]; + task.input.width = cam->crop_current.width; + task.input.height = cam->crop_current.height; + task.input.format = IPU_PIX_FMT_NV12; + + if (buffer_num == 0) + task.output.paddr = fbi->fix.smem_start + + (fbi->fix.line_length * fbvar.yres); + else + task.output.paddr = fbi->fix.smem_start; + task.output.width = cam->win.w.width; + task.output.height = cam->win.w.height; + task.output.format = vf_out_format; + task.output.rotate = cam->rotation; +again: + err = ipu_check_task(&task); + if (err != IPU_CHECK_OK) { + if (err > IPU_CHECK_ERR_MIN) { + if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) { + task.input.crop.w -= 8; + goto again; + } + if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) { + task.input.crop.h -= 8; + goto again; + } + if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { + task.output.width -= 8; + task.output.crop.w = task.output.width; + goto again; + } + if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { + task.output.height -= 8; + task.output.crop.h = task.output.height; + goto again; + } + printk(KERN_ERR "check ipu taks fail\n"); + return; + } + printk(KERN_ERR "check ipu taks fail\n"); + return; + } + err = ipu_queue_task(&task); + if (err < 0) + printk(KERN_ERR "queue ipu task error\n"); + ipu_select_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, buffer_num); + buffer_num = (buffer_num == 0) ? 1 : 0; +} + +static void get_disp_ipu(cam_data *cam) +{ + if (cam->output > 2) + disp_ipu = ipu_get_soc(1); /* using DISP4 */ + else + disp_ipu = ipu_get_soc(0); +} + +/*! + * csi ENC callback function. + * + * @param irq int irq line + * @param dev_id void * device id + * + * @return status IRQ_HANDLED for handled + */ +static irqreturn_t csi_enc_callback(int irq, void *dev_id) +{ + cam_data *cam = (cam_data *) dev_id; + + ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, csi_buffer_num); + if ((cam->crop_current.width != cam->win.w.width) || + (cam->crop_current.height != cam->win.w.height) || + (vf_out_format != IPU_PIX_FMT_NV12) || + (cam->rotation >= IPU_ROTATE_VERT_FLIP)) + schedule_work(&cam->csi_work_struct); + csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0; + return IRQ_HANDLED; +} + +static int csi_enc_setup(cam_data *cam) +{ + ipu_channel_params_t params; + int err = 0, sensor_protocol = 0; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + CAMERA_TRACE("In csi_enc_setup\n"); + if (!cam) { + printk(KERN_ERR "cam private is NULL\n"); + return -ENXIO; + } + + memset(¶ms, 0, sizeof(ipu_channel_params_t)); + params.csi_mem.csi = cam->csi; + + sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi); + switch (sensor_protocol) { + case IPU_CSI_CLK_MODE_GATED_CLK: + case IPU_CSI_CLK_MODE_NONGATED_CLK: + case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: + case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: + case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: + params.csi_mem.interlaced = false; + break; + case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: + case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: + case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: + params.csi_mem.interlaced = true; + break; + default: + printk(KERN_ERR "sensor protocol unsupported\n"); + return -EINVAL; + } + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) { + params.csi_mem.mipi_en = true; + params.csi_mem.mipi_vc = + mipi_csi2_get_virtual_channel(mipi_csi2_info); + params.csi_mem.mipi_id = + mipi_csi2_get_datatype(mipi_csi2_info); + + mipi_csi2_pixelclk_enable(mipi_csi2_info); + } else { + params.csi_mem.mipi_en = false; + params.csi_mem.mipi_vc = 0; + params.csi_mem.mipi_id = 0; + } + } else { + params.csi_mem.mipi_en = false; + params.csi_mem.mipi_vc = 0; + params.csi_mem.mipi_id = 0; + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], + (dma_addr_t) cam->vf_bufs[0]); + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], + (dma_addr_t) cam->vf_bufs[1]); + } + csi_mem_bufsize = cam->crop_current.width * + cam->crop_current.height * 3/2; + cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize); + cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0, + cam->vf_bufs_size[0], + (dma_addr_t *) & + cam->vf_bufs[0], + GFP_DMA | + GFP_KERNEL); + if (cam->vf_bufs_vaddr[0] == NULL) { + printk(KERN_ERR "Error to allocate vf buffer\n"); + err = -ENOMEM; + goto out_2; + } + cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize); + cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0, + cam->vf_bufs_size[1], + (dma_addr_t *) & + cam->vf_bufs[1], + GFP_DMA | + GFP_KERNEL); + if (cam->vf_bufs_vaddr[1] == NULL) { + printk(KERN_ERR "Error to allocate vf buffer\n"); + err = -ENOMEM; + goto out_1; + } + pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]); + + err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); + if (err != 0) { + printk(KERN_ERR "ipu_init_channel %d\n", err); + goto out_1; + } + + if ((cam->crop_current.width == cam->win.w.width) && + (cam->crop_current.height == cam->win.w.height) && + (vf_out_format == IPU_PIX_FMT_NV12) && + (cam->rotation < IPU_ROTATE_VERT_FLIP)) { + err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, + IPU_OUTPUT_BUFFER, + IPU_PIX_FMT_NV12, + cam->crop_current.width, + cam->crop_current.height, + cam->crop_current.width, IPU_ROTATE_NONE, + fbi->fix.smem_start + + (fbi->fix.line_length * fbvar.yres), + fbi->fix.smem_start, 0, + cam->offset.u_offset, cam->offset.u_offset); + } else { + err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, + IPU_OUTPUT_BUFFER, + IPU_PIX_FMT_NV12, + cam->crop_current.width, + cam->crop_current.height, + cam->crop_current.width, IPU_ROTATE_NONE, + cam->vf_bufs[0], cam->vf_bufs[1], 0, + cam->offset.u_offset, cam->offset.u_offset); + } + if (err != 0) { + printk(KERN_ERR "CSI_MEM output buffer\n"); + goto out_1; + } + err = ipu_enable_channel(cam->ipu, CSI_MEM); + if (err < 0) { + printk(KERN_ERR "ipu_enable_channel CSI_MEM\n"); + goto out_1; + } + + csi_buffer_num = 0; + + ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0); + ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 1); + return err; +out_1: + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], + (dma_addr_t) cam->vf_bufs[0]); + cam->vf_bufs_vaddr[0] = NULL; + cam->vf_bufs[0] = 0; + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], + (dma_addr_t) cam->vf_bufs[1]); + cam->vf_bufs_vaddr[1] = NULL; + cam->vf_bufs[1] = 0; + } +out_2: + return err; +} + +/*! + * Enable encoder task + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int csi_enc_enabling_tasks(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n"); + + ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); + err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, + csi_enc_callback, 0, "Mxc Camera", cam); + if (err != 0) { + printk(KERN_ERR "Error registering CSI0_OUT_EOF irq\n"); + return err; + } + + INIT_WORK(&cam->csi_work_struct, csi_buf_work_func); + + err = csi_enc_setup(cam); + if (err != 0) { + printk(KERN_ERR "csi_enc_setup %d\n", err); + goto out1; + } + + return err; +out1: + ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); + return err; +} + +/* + * Function definitions + */ + +/*! + * foreground_start - start the vf task + * + * @param private cam_data * mxc v4l2 main structure + * + */ +static int foreground_start(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0, i = 0, screen_size; + char *base; + + if (!cam) { + printk(KERN_ERR "private is NULL\n"); + return -EIO; + } + + if (cam->overlay_active == true) { + pr_debug("already started.\n"); + return 0; + } + + get_disp_ipu(cam); + + for (i = 0; i < num_registered_fb; i++) { + char *idstr = registered_fb[i]->fix.id; + if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) || + ((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) { + fbi = registered_fb[i]; + break; + } + } + + if (fbi == NULL) { + printk(KERN_ERR "DISP FG fb not found\n"); + return -EPERM; + } + + fbvar = fbi->var; + + /* Store the overlay frame buffer's original std */ + cam->fb_origin_std = fbvar.nonstd; + + if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) { + /* Use DP to do CSC so that we can get better performance */ + vf_out_format = IPU_PIX_FMT_NV12; + fbvar.nonstd = vf_out_format; + } else { + vf_out_format = IPU_PIX_FMT_RGB565; + fbvar.nonstd = 0; + } + + fbvar.bits_per_pixel = 16; + fbvar.xres = fbvar.xres_virtual = cam->win.w.width; + fbvar.yres = cam->win.w.height; + fbvar.yres_virtual = cam->win.w.height * 2; + fbvar.yoffset = 0; + fbvar.vmode &= ~FB_VMODE_YWRAP; + fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG; + fbvar.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbvar); + + ipu_disp_set_window_pos(disp_ipu, MEM_FG_SYNC, cam->win.w.left, + cam->win.w.top); + + /* Fill black color for framebuffer */ + base = (char *) fbi->screen_base; + screen_size = fbi->var.xres * fbi->var.yres; + if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) { + memset(base, 0, screen_size); + base += screen_size; + for (i = 0; i < screen_size / 2; i++, base++) + *base = 0x80; + } else { + for (i = 0; i < screen_size * 2; i++, base++) + *base = 0x00; + } + + console_lock(); + fb_blank(fbi, FB_BLANK_UNBLANK); + console_unlock(); + + /* correct display ch buffer address */ + ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, + 0, fbi->fix.smem_start + + (fbi->fix.line_length * fbvar.yres)); + ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, + 1, fbi->fix.smem_start); + + err = csi_enc_enabling_tasks(cam); + if (err != 0) { + printk(KERN_ERR "Error csi enc enable fail\n"); + return err; + } + + cam->overlay_active = true; + return err; + +} + +/*! + * foreground_stop - stop the vf task + * + * @param private cam_data * mxc v4l2 main structure + * + */ +static int foreground_stop(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0, i = 0; + struct fb_info *fbi = NULL; + struct fb_var_screeninfo fbvar; + +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + if (cam->overlay_active == false) + return 0; + + err = ipu_disable_channel(cam->ipu, CSI_MEM, true); + + ipu_uninit_channel(cam->ipu, CSI_MEM); + + csi_buffer_num = 0; + buffer_num = 0; + + for (i = 0; i < num_registered_fb; i++) { + char *idstr = registered_fb[i]->fix.id; + if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) || + ((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) { + fbi = registered_fb[i]; + break; + } + } + + if (fbi == NULL) { + printk(KERN_ERR "DISP FG fb not found\n"); + return -EPERM; + } + + console_lock(); + fb_blank(fbi, FB_BLANK_POWERDOWN); + console_unlock(); + + /* Set the overlay frame buffer std to what it is used to be */ + fbvar = fbi->var; + fbvar.accel_flags = FB_ACCEL_TRIPLE_FLAG; + fbvar.nonstd = cam->fb_origin_std; + fbvar.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbvar); + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) + mipi_csi2_pixelclk_disable(mipi_csi2_info); + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + flush_work(&cam->csi_work_struct); + cancel_work_sync(&cam->csi_work_struct); + + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], + (dma_addr_t) cam->vf_bufs[0]); + cam->vf_bufs_vaddr[0] = NULL; + cam->vf_bufs[0] = 0; + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], + (dma_addr_t) cam->vf_bufs[1]); + cam->vf_bufs_vaddr[1] = NULL; + cam->vf_bufs[1] = 0; + } + + cam->overlay_active = false; + return err; +} + +/*! + * Enable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int foreground_enable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + return ipu_enable_csi(cam->ipu, cam->csi); +} + +/*! + * Disable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int foreground_disable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + /* free csi eof irq firstly. + * when disable csi, wait for idmac eof. + * it requests eof irq again */ + ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); + + return ipu_disable_csi(cam->ipu, cam->csi); +} + +/*! + * function to select foreground as the working path + * + * @param private cam_data * mxc v4l2 main structure + * + * @return status + */ +int foreground_sdc_select(void *private) +{ + cam_data *cam; + int err = 0; + if (private) { + cam = (cam_data *) private; + cam->vf_start_sdc = foreground_start; + cam->vf_stop_sdc = foreground_stop; + cam->vf_enable_csi = foreground_enable_csi; + cam->vf_disable_csi = foreground_disable_csi; + cam->overlay_active = false; + } else + err = -EIO; + + return err; +} +EXPORT_SYMBOL(foreground_sdc_select); + +/*! + * function to de-select foreground as the working path + * + * @param private cam_data * mxc v4l2 main structure + * + * @return int + */ +int foreground_sdc_deselect(void *private) +{ + cam_data *cam; + + if (private) { + cam = (cam_data *) private; + cam->vf_start_sdc = NULL; + cam->vf_stop_sdc = NULL; + cam->vf_enable_csi = NULL; + cam->vf_disable_csi = NULL; + } + return 0; +} +EXPORT_SYMBOL(foreground_sdc_deselect); + +/*! + * Init viewfinder task. + * + * @return Error code indicating success or failure + */ +__init int foreground_sdc_init(void) +{ + return 0; +} + +/*! + * Deinit viewfinder task. + * + * @return Error code indicating success or failure + */ +void __exit foreground_sdc_exit(void) +{ +} + +module_init(foreground_sdc_init); +module_exit(foreground_sdc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("IPU PRP VF SDC Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ipu_prp_enc.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_prp_enc.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ipu_prp_enc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_prp_enc.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,603 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ipu_prp_enc.c + * + * @brief IPU Use case for PRP-ENC + * + * @ingroup IPU + */ + +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +#ifdef CAMERA_DBG + #define CAMERA_TRACE(x) (printk)x +#else + #define CAMERA_TRACE(x) +#endif + +static ipu_rotate_mode_t grotation = IPU_ROTATE_NONE; + +/* + * Function definitions + */ + +/*! + * IPU ENC callback function. + * + * @param irq int irq line + * @param dev_id void * device id + * + * @return status IRQ_HANDLED for handled + */ +static irqreturn_t prp_enc_callback(int irq, void *dev_id) +{ + cam_data *cam = (cam_data *) dev_id; + + if (cam->enc_callback == NULL) + return IRQ_HANDLED; + + cam->enc_callback(irq, dev_id); + + return IRQ_HANDLED; +} + +/*! + * PrpENC enable channel setup function + * + * @param cam struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_enc_setup(cam_data *cam) +{ + ipu_channel_params_t enc; + int err = 0; + dma_addr_t dummy = cam->dummy_frame.buffer.m.offset; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + CAMERA_TRACE("In prp_enc_setup\n"); + if (!cam) { + printk(KERN_ERR "cam private is NULL\n"); + return -ENXIO; + } + memset(&enc, 0, sizeof(ipu_channel_params_t)); + + ipu_csi_get_window_size(cam->ipu, &enc.csi_prp_enc_mem.in_width, + &enc.csi_prp_enc_mem.in_height, cam->csi); + + enc.csi_prp_enc_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY; + enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.width; + enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.height; + enc.csi_prp_enc_mem.csi = cam->csi; + if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.height; + enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.width; + } + + if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV420P; + pr_info("YUV420\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YVU420P; + pr_info("YVU420\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV422P; + pr_info("YUV422P\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUYV; + pr_info("YUYV\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_UYVY; + pr_info("UYVY\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_NV12; + pr_info("NV12\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR24; + pr_info("BGR24\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB24; + pr_info("RGB24\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB565; + pr_info("RGB565\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR32; + pr_info("BGR32\n"); + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) { + enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB32; + pr_info("RGB32\n"); + } else { + printk(KERN_ERR "format not supported\n"); + return -EINVAL; + } + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) { + enc.csi_prp_enc_mem.mipi_en = true; + enc.csi_prp_enc_mem.mipi_vc = + mipi_csi2_get_virtual_channel(mipi_csi2_info); + enc.csi_prp_enc_mem.mipi_id = + mipi_csi2_get_datatype(mipi_csi2_info); + + mipi_csi2_pixelclk_enable(mipi_csi2_info); + } else { + enc.csi_prp_enc_mem.mipi_en = false; + enc.csi_prp_enc_mem.mipi_vc = 0; + enc.csi_prp_enc_mem.mipi_id = 0; + } + } else { + enc.csi_prp_enc_mem.mipi_en = false; + enc.csi_prp_enc_mem.mipi_vc = 0; + enc.csi_prp_enc_mem.mipi_id = 0; + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + err = ipu_init_channel(cam->ipu, CSI_PRP_ENC_MEM, &enc); + if (err != 0) { + printk(KERN_ERR "ipu_init_channel %d\n", err); + return err; + } + + grotation = cam->rotation; + if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + if (cam->rot_enc_bufs_vaddr[0]) { + dma_free_coherent(0, cam->rot_enc_buf_size[0], + cam->rot_enc_bufs_vaddr[0], + cam->rot_enc_bufs[0]); + } + if (cam->rot_enc_bufs_vaddr[1]) { + dma_free_coherent(0, cam->rot_enc_buf_size[1], + cam->rot_enc_bufs_vaddr[1], + cam->rot_enc_bufs[1]); + } + cam->rot_enc_buf_size[0] = + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); + cam->rot_enc_bufs_vaddr[0] = + (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[0], + &cam->rot_enc_bufs[0], + GFP_DMA | GFP_KERNEL); + if (!cam->rot_enc_bufs_vaddr[0]) { + printk(KERN_ERR "alloc enc_bufs0\n"); + return -ENOMEM; + } + cam->rot_enc_buf_size[1] = + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); + cam->rot_enc_bufs_vaddr[1] = + (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[1], + &cam->rot_enc_bufs[1], + GFP_DMA | GFP_KERNEL); + if (!cam->rot_enc_bufs_vaddr[1]) { + dma_free_coherent(0, cam->rot_enc_buf_size[0], + cam->rot_enc_bufs_vaddr[0], + cam->rot_enc_bufs[0]); + cam->rot_enc_bufs_vaddr[0] = NULL; + cam->rot_enc_bufs[0] = 0; + printk(KERN_ERR "alloc enc_bufs1\n"); + return -ENOMEM; + } + + err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM, + IPU_OUTPUT_BUFFER, + enc.csi_prp_enc_mem.out_pixel_fmt, + enc.csi_prp_enc_mem.out_width, + enc.csi_prp_enc_mem.out_height, + enc.csi_prp_enc_mem.out_width, + IPU_ROTATE_NONE, + cam->rot_enc_bufs[0], + cam->rot_enc_bufs[1], 0, 0, 0); + if (err != 0) { + printk(KERN_ERR "CSI_PRP_ENC_MEM err\n"); + return err; + } + + err = ipu_init_channel(cam->ipu, MEM_ROT_ENC_MEM, NULL); + if (err != 0) { + printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n"); + return err; + } + + err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM, + IPU_INPUT_BUFFER, + enc.csi_prp_enc_mem.out_pixel_fmt, + enc.csi_prp_enc_mem.out_width, + enc.csi_prp_enc_mem.out_height, + enc.csi_prp_enc_mem.out_width, + cam->rotation, + cam->rot_enc_bufs[0], + cam->rot_enc_bufs[1], 0, 0, 0); + if (err != 0) { + printk(KERN_ERR "MEM_ROT_ENC_MEM input buffer\n"); + return err; + } + + err = + ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM, + IPU_OUTPUT_BUFFER, + enc.csi_prp_enc_mem.out_pixel_fmt, + enc.csi_prp_enc_mem.out_height, + enc.csi_prp_enc_mem.out_width, + cam->v2f.fmt.pix.bytesperline / + bytes_per_pixel(enc.csi_prp_enc_mem. + out_pixel_fmt), + IPU_ROTATE_NONE, + dummy, dummy, 0, + cam->offset.u_offset, + cam->offset.v_offset); + if (err != 0) { + printk(KERN_ERR "MEM_ROT_ENC_MEM output buffer\n"); + return err; + } + + err = ipu_link_channels(cam->ipu, + CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM); + if (err < 0) { + printk(KERN_ERR + "link CSI_PRP_ENC_MEM-MEM_ROT_ENC_MEM\n"); + return err; + } + + err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM); + if (err < 0) { + printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n"); + return err; + } + err = ipu_enable_channel(cam->ipu, MEM_ROT_ENC_MEM); + if (err < 0) { + printk(KERN_ERR "ipu_enable_channel MEM_ROT_ENC_MEM\n"); + return err; + } + + ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM, + IPU_OUTPUT_BUFFER, 0); + ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM, + IPU_OUTPUT_BUFFER, 1); + } else { + err = + ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM, + IPU_OUTPUT_BUFFER, + enc.csi_prp_enc_mem.out_pixel_fmt, + enc.csi_prp_enc_mem.out_width, + enc.csi_prp_enc_mem.out_height, + cam->v2f.fmt.pix.bytesperline / + bytes_per_pixel(enc.csi_prp_enc_mem. + out_pixel_fmt), + cam->rotation, + dummy, dummy, 0, + cam->offset.u_offset, + cam->offset.v_offset); + if (err != 0) { + printk(KERN_ERR "CSI_PRP_ENC_MEM output buffer\n"); + return err; + } + err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM); + if (err < 0) { + printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n"); + return err; + } + } + + return err; +} + +/*! + * function to update physical buffer address for encorder IDMA channel + * + * @param eba physical buffer address for encorder IDMA channel + * @param buffer_num int buffer 0 or buffer 1 + * + * @return status + */ +static int prp_enc_eba_update(struct ipu_soc *ipu, dma_addr_t eba, + int *buffer_num) +{ + int err = 0; + + pr_debug("eba %x\n", eba); + if (grotation >= IPU_ROTATE_90_RIGHT) { + err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM, + IPU_OUTPUT_BUFFER, *buffer_num, + eba); + } else { + err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM, + IPU_OUTPUT_BUFFER, *buffer_num, + eba); + } + if (err != 0) { + if (grotation >= IPU_ROTATE_90_RIGHT) { + ipu_clear_buffer_ready(ipu, MEM_ROT_ENC_MEM, + IPU_OUTPUT_BUFFER, + *buffer_num); + err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM, + IPU_OUTPUT_BUFFER, + *buffer_num, + eba); + } else { + ipu_clear_buffer_ready(ipu, CSI_PRP_ENC_MEM, + IPU_OUTPUT_BUFFER, + *buffer_num); + err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM, + IPU_OUTPUT_BUFFER, + *buffer_num, + eba); + } + + if (err != 0) { + pr_err("ERROR: v4l2 capture: fail to update " + "buf%d\n", *buffer_num); + return err; + } + } + + if (grotation >= IPU_ROTATE_90_RIGHT) { + ipu_select_buffer(ipu, MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER, + *buffer_num); + } else { + ipu_select_buffer(ipu, CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, + *buffer_num); + } + + *buffer_num = (*buffer_num == 0) ? 1 : 0; + return 0; +} + +/*! + * Enable encoder task + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_enc_enabling_tasks(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n"); + + cam->dummy_frame.vaddress = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->dummy_frame.paddress, + GFP_DMA | GFP_KERNEL); + if (cam->dummy_frame.vaddress == 0) { + pr_err("ERROR: v4l2 capture: Allocate dummy frame " + "failed.\n"); + return -ENOBUFS; + } + cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE; + cam->dummy_frame.buffer.length = + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); + cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; + + if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, + prp_enc_callback, 0, "Mxc Camera", cam); + } else { + err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, + prp_enc_callback, 0, "Mxc Camera", cam); + } + if (err != 0) { + printk(KERN_ERR "Error registering rot irq\n"); + return err; + } + + err = prp_enc_setup(cam); + if (err != 0) { + printk(KERN_ERR "prp_enc_setup %d\n", err); + return err; + } + + return err; +} + +/*! + * Disable encoder task + * @param private struct cam_data * mxc capture instance + * + * @return int + */ +static int prp_enc_disabling_tasks(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, cam); + ipu_unlink_channels(cam->ipu, CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM); + } + + err = ipu_disable_channel(cam->ipu, CSI_PRP_ENC_MEM, true); + if (cam->rotation >= IPU_ROTATE_90_RIGHT) + err |= ipu_disable_channel(cam->ipu, MEM_ROT_ENC_MEM, true); + + ipu_uninit_channel(cam->ipu, CSI_PRP_ENC_MEM); + if (cam->rotation >= IPU_ROTATE_90_RIGHT) + ipu_uninit_channel(cam->ipu, MEM_ROT_ENC_MEM); + + if (cam->dummy_frame.vaddress != 0) { + dma_free_coherent(0, cam->dummy_frame.buffer.length, + cam->dummy_frame.vaddress, + cam->dummy_frame.paddress); + cam->dummy_frame.vaddress = 0; + } + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) + mipi_csi2_pixelclk_disable(mipi_csi2_info); + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + return err; +} + +/*! + * Enable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_enc_enable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + return ipu_enable_csi(cam->ipu, cam->csi); +} + +/*! + * Disable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_enc_disable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + /* free csi eof irq firstly. + * when disable csi, wait for idmac eof. + * it requests eof irq again */ + if (cam->rotation < IPU_ROTATE_90_RIGHT) + ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, cam); + + return ipu_disable_csi(cam->ipu, cam->csi); +} + +/*! + * function to select PRP-ENC as the working path + * + * @param private struct cam_data * mxc capture instance + * + * @return int + */ +int prp_enc_select(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + + if (cam) { + cam->enc_update_eba = prp_enc_eba_update; + cam->enc_enable = prp_enc_enabling_tasks; + cam->enc_disable = prp_enc_disabling_tasks; + cam->enc_enable_csi = prp_enc_enable_csi; + cam->enc_disable_csi = prp_enc_disable_csi; + } else { + err = -EIO; + } + + return err; +} +EXPORT_SYMBOL(prp_enc_select); + +/*! + * function to de-select PRP-ENC as the working path + * + * @param private struct cam_data * mxc capture instance + * + * @return int + */ +int prp_enc_deselect(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + + if (cam) { + cam->enc_update_eba = NULL; + cam->enc_enable = NULL; + cam->enc_disable = NULL; + cam->enc_enable_csi = NULL; + cam->enc_disable_csi = NULL; + if (cam->rot_enc_bufs_vaddr[0]) { + dma_free_coherent(0, cam->rot_enc_buf_size[0], + cam->rot_enc_bufs_vaddr[0], + cam->rot_enc_bufs[0]); + cam->rot_enc_bufs_vaddr[0] = NULL; + cam->rot_enc_bufs[0] = 0; + } + if (cam->rot_enc_bufs_vaddr[1]) { + dma_free_coherent(0, cam->rot_enc_buf_size[1], + cam->rot_enc_bufs_vaddr[1], + cam->rot_enc_bufs[1]); + cam->rot_enc_bufs_vaddr[1] = NULL; + cam->rot_enc_bufs[1] = 0; + } + } + + return err; +} +EXPORT_SYMBOL(prp_enc_deselect); + +/*! + * Init the Encorder channels + * + * @return Error code indicating success or failure + */ +__init int prp_enc_init(void) +{ + return 0; +} + +/*! + * Deinit the Encorder channels + * + */ +void __exit prp_enc_exit(void) +{ +} + +module_init(prp_enc_init); +module_exit(prp_enc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("IPU PRP ENC Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ipu_prp_sw.h linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_prp_sw.h --- linux-3.10.30/drivers/media/platform/mxc/capture/ipu_prp_sw.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_prp_sw.h 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,43 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ipu_prp_sw.h + * + * @brief This file contains the IPU PRP use case driver header. + * + * @ingroup IPU + */ + +#ifndef _INCLUDE_IPU__PRP_SW_H_ +#define _INCLUDE_IPU__PRP_SW_H_ + +int csi_enc_select(void *private); +int csi_enc_deselect(void *private); +int prp_enc_select(void *private); +int prp_enc_deselect(void *private); +#ifdef CONFIG_MXC_IPU_PRP_VF_SDC +int prp_vf_sdc_select(void *private); +int prp_vf_sdc_deselect(void *private); +int prp_vf_sdc_select_bg(void *private); +int prp_vf_sdc_deselect_bg(void *private); +#else +int foreground_sdc_select(void *private); +int foreground_sdc_deselect(void *private); +int bg_overlay_sdc_select(void *private); +int bg_overlay_sdc_deselect(void *private); +#endif +int prp_still_select(void *private); +int prp_still_deselect(void *private); + +#endif diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,590 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ +/* * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ipu_prp_vf_sdc.c + * + * @brief IPU Use case for PRP-VF + * + * @ingroup IPU + */ + +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +static int buffer_num; +static struct ipu_soc *disp_ipu; + +static void get_disp_ipu(cam_data *cam) +{ + if (cam->output > 2) + disp_ipu = ipu_get_soc(1); /* using DISP4 */ + else + disp_ipu = ipu_get_soc(0); +} + +static irqreturn_t prpvf_rot_eof_callback(int irq, void *dev_id) +{ + cam_data *cam = dev_id; + pr_debug("buffer_num %d\n", buffer_num); + + if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { + ipu_select_buffer(disp_ipu, MEM_FG_SYNC, + IPU_INPUT_BUFFER, buffer_num); + buffer_num = (buffer_num == 0) ? 1 : 0; + ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_OUTPUT_BUFFER, buffer_num); + } else { + ipu_select_buffer(disp_ipu, MEM_FG_SYNC, + IPU_INPUT_BUFFER, buffer_num); + buffer_num = (buffer_num == 0) ? 1 : 0; + ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, + IPU_OUTPUT_BUFFER, buffer_num); + } + return IRQ_HANDLED; +} +/* + * Function definitions + */ + +/*! + * prpvf_start - start the vf task + * + * @param private cam_data * mxc v4l2 main structure + * + */ +static int prpvf_start(void *private) +{ + struct fb_var_screeninfo fbvar; + struct fb_info *fbi = NULL; + cam_data *cam = (cam_data *) private; + ipu_channel_params_t vf; + u32 vf_out_format = 0; + u32 size = 2, temp = 0; + int err = 0, i = 0; + short *tmp, color; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + if (!cam) { + printk(KERN_ERR "private is NULL\n"); + return -EIO; + } + + if (cam->overlay_active == true) { + pr_debug("already started.\n"); + return 0; + } + + get_disp_ipu(cam); + + for (i = 0; i < num_registered_fb; i++) { + char *idstr = registered_fb[i]->fix.id; + if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) || + ((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) { + fbi = registered_fb[i]; + break; + } + } + + if (fbi == NULL) { + printk(KERN_ERR "DISP FG fb not found\n"); + return -EPERM; + } + + fbvar = fbi->var; + + /* Store the overlay frame buffer's original std */ + cam->fb_origin_std = fbvar.nonstd; + + if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) { + /* Use DP to do CSC so that we can get better performance */ + vf_out_format = IPU_PIX_FMT_UYVY; + fbvar.nonstd = vf_out_format; + color = 0x80; + } else { + vf_out_format = IPU_PIX_FMT_RGB565; + fbvar.nonstd = 0; + color = 0x0; + } + + fbvar.bits_per_pixel = 16; + fbvar.xres = fbvar.xres_virtual = cam->win.w.width; + fbvar.yres = cam->win.w.height; + fbvar.yres_virtual = cam->win.w.height * 2; + fbvar.yoffset = 0; + fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG; + fbvar.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbvar); + + ipu_disp_set_window_pos(disp_ipu, MEM_FG_SYNC, cam->win.w.left, + cam->win.w.top); + + /* Fill black color for framebuffer */ + tmp = (short *) fbi->screen_base; + for (i = 0; i < (fbi->fix.line_length * fbi->var.yres)/2; + i++, tmp++) + *tmp = color; + + console_lock(); + fb_blank(fbi, FB_BLANK_UNBLANK); + console_unlock(); + + /* correct display ch buffer address */ + ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, + 0, fbi->fix.smem_start + + (fbi->fix.line_length * fbvar.yres)); + ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, + 1, fbi->fix.smem_start); + + memset(&vf, 0, sizeof(ipu_channel_params_t)); + ipu_csi_get_window_size(cam->ipu, &vf.csi_prp_vf_mem.in_width, + &vf.csi_prp_vf_mem.in_height, cam->csi); + vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY; + vf.csi_prp_vf_mem.out_width = cam->win.w.width; + vf.csi_prp_vf_mem.out_height = cam->win.w.height; + vf.csi_prp_vf_mem.csi = cam->csi; + if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) { + vf.csi_prp_vf_mem.out_width = cam->win.w.height; + vf.csi_prp_vf_mem.out_height = cam->win.w.width; + } + vf.csi_prp_vf_mem.out_pixel_fmt = vf_out_format; + size = cam->win.w.width * cam->win.w.height * size; + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) { + vf.csi_prp_vf_mem.mipi_en = true; + vf.csi_prp_vf_mem.mipi_vc = + mipi_csi2_get_virtual_channel(mipi_csi2_info); + vf.csi_prp_vf_mem.mipi_id = + mipi_csi2_get_datatype(mipi_csi2_info); + + mipi_csi2_pixelclk_enable(mipi_csi2_info); + } else { + vf.csi_prp_vf_mem.mipi_en = false; + vf.csi_prp_vf_mem.mipi_vc = 0; + vf.csi_prp_vf_mem.mipi_id = 0; + } + } else { + vf.csi_prp_vf_mem.mipi_en = false; + vf.csi_prp_vf_mem.mipi_vc = 0; + vf.csi_prp_vf_mem.mipi_id = 0; + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf); + if (err != 0) + goto out_5; + + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], + (dma_addr_t) cam->vf_bufs[0]); + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], + (dma_addr_t) cam->vf_bufs[1]); + } + cam->vf_bufs_size[0] = PAGE_ALIGN(size); + cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0, + cam->vf_bufs_size[0], + (dma_addr_t *) & + cam->vf_bufs[0], + GFP_DMA | + GFP_KERNEL); + if (cam->vf_bufs_vaddr[0] == NULL) { + printk(KERN_ERR "Error to allocate vf buffer\n"); + err = -ENOMEM; + goto out_4; + } + cam->vf_bufs_size[1] = PAGE_ALIGN(size); + cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0, + cam->vf_bufs_size[1], + (dma_addr_t *) & + cam->vf_bufs[1], + GFP_DMA | + GFP_KERNEL); + if (cam->vf_bufs_vaddr[1] == NULL) { + printk(KERN_ERR "Error to allocate vf buffer\n"); + err = -ENOMEM; + goto out_3; + } + pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]); + + if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { + err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM, + IPU_OUTPUT_BUFFER, + vf_out_format, + vf.csi_prp_vf_mem.out_width, + vf.csi_prp_vf_mem.out_height, + vf.csi_prp_vf_mem.out_width, + IPU_ROTATE_NONE, + cam->vf_bufs[0], cam->vf_bufs[1], + 0, 0, 0); + if (err != 0) + goto out_3; + + err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL); + if (err != 0) { + printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n"); + goto out_3; + } + + err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_INPUT_BUFFER, + vf_out_format, + vf.csi_prp_vf_mem.out_width, + vf.csi_prp_vf_mem.out_height, + vf.csi_prp_vf_mem.out_width, + cam->vf_rotation, + cam->vf_bufs[0], + cam->vf_bufs[1], + 0, 0, 0); + if (err != 0) { + printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n"); + goto out_2; + } + + if (cam->vf_rotation < IPU_ROTATE_90_RIGHT) { + temp = vf.csi_prp_vf_mem.out_width; + vf.csi_prp_vf_mem.out_width = + vf.csi_prp_vf_mem.out_height; + vf.csi_prp_vf_mem.out_height = temp; + } + + err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_OUTPUT_BUFFER, + vf_out_format, + vf.csi_prp_vf_mem.out_height, + vf.csi_prp_vf_mem.out_width, + vf.csi_prp_vf_mem.out_height, + IPU_ROTATE_NONE, + fbi->fix.smem_start + + (fbi->fix.line_length * + fbi->var.yres), + fbi->fix.smem_start, 0, 0, 0); + + if (err != 0) { + printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n"); + goto out_2; + } + + ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF); + err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF, + prpvf_rot_eof_callback, + 0, "Mxc Camera", cam); + if (err < 0) { + printk(KERN_ERR "Error request irq:IPU_IRQ_PRP_VF_ROT_OUT_EOF\n"); + goto out_2; + } + + err = ipu_link_channels(cam->ipu, + CSI_PRP_VF_MEM, MEM_ROT_VF_MEM); + if (err < 0) { + printk(KERN_ERR + "Error link CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n"); + goto out_1; + } + + ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM); + ipu_enable_channel(cam->ipu, MEM_ROT_VF_MEM); + + ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, + IPU_OUTPUT_BUFFER, 0); + ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, + IPU_OUTPUT_BUFFER, 1); + ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_OUTPUT_BUFFER, 0); + } else { + err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM, + IPU_OUTPUT_BUFFER, + vf_out_format, cam->win.w.width, + cam->win.w.height, + cam->win.w.width, + cam->vf_rotation, + fbi->fix.smem_start + + (fbi->fix.line_length * + fbi->var.yres), + fbi->fix.smem_start, 0, 0, 0); + if (err != 0) { + printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n"); + goto out_4; + } + ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF); + err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, + prpvf_rot_eof_callback, + 0, "Mxc Camera", cam); + if (err < 0) { + printk(KERN_ERR "Error request irq:IPU_IRQ_PRP_VF_OUT_EOF\n"); + goto out_4; + } + + ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM); + + ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, + IPU_OUTPUT_BUFFER, 0); + } + + cam->overlay_active = true; + return err; + +out_1: + ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL); +out_2: + if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) + ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); +out_3: + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], + (dma_addr_t) cam->vf_bufs[0]); + cam->vf_bufs_vaddr[0] = NULL; + cam->vf_bufs[0] = 0; + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], + (dma_addr_t) cam->vf_bufs[1]); + cam->vf_bufs_vaddr[1] = NULL; + cam->vf_bufs[1] = 0; + } +out_4: + ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); +out_5: + return err; +} + +/*! + * prpvf_stop - stop the vf task + * + * @param private cam_data * mxc v4l2 main structure + * + */ +static int prpvf_stop(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0, i = 0; + struct fb_info *fbi = NULL; + struct fb_var_screeninfo fbvar; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + if (cam->overlay_active == false) + return 0; + + for (i = 0; i < num_registered_fb; i++) { + char *idstr = registered_fb[i]->fix.id; + if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) || + ((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) { + fbi = registered_fb[i]; + break; + } + } + + if (fbi == NULL) { + printk(KERN_ERR "DISP FG fb not found\n"); + return -EPERM; + } + + if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { + ipu_unlink_channels(cam->ipu, CSI_PRP_VF_MEM, MEM_ROT_VF_MEM); + ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF, cam); + } + buffer_num = 0; + + ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true); + + if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { + ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true); + ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); + } + ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); + + console_lock(); + fb_blank(fbi, FB_BLANK_POWERDOWN); + console_unlock(); + + /* Set the overlay frame buffer std to what it is used to be */ + fbvar = fbi->var; + fbvar.accel_flags = FB_ACCEL_TRIPLE_FLAG; + fbvar.nonstd = cam->fb_origin_std; + fbvar.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbvar); + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) + mipi_csi2_pixelclk_disable(mipi_csi2_info); + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], + (dma_addr_t) cam->vf_bufs[0]); + cam->vf_bufs_vaddr[0] = NULL; + cam->vf_bufs[0] = 0; + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], + (dma_addr_t) cam->vf_bufs[1]); + cam->vf_bufs_vaddr[1] = NULL; + cam->vf_bufs[1] = 0; + } + + cam->overlay_active = false; + return err; +} + +/*! + * Enable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_vf_enable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + return ipu_enable_csi(cam->ipu, cam->csi); +} + +/*! + * Disable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_vf_disable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + /* free csi eof irq firstly. + * when disable csi, wait for idmac eof. + * it requests eof irq again */ + if (cam->vf_rotation < IPU_ROTATE_VERT_FLIP) + ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam); + + return ipu_disable_csi(cam->ipu, cam->csi); +} + +/*! + * function to select PRP-VF as the working path + * + * @param private cam_data * mxc v4l2 main structure + * + * @return status + */ +int prp_vf_sdc_select(void *private) +{ + cam_data *cam; + int err = 0; + if (private) { + cam = (cam_data *) private; + cam->vf_start_sdc = prpvf_start; + cam->vf_stop_sdc = prpvf_stop; + cam->vf_enable_csi = prp_vf_enable_csi; + cam->vf_disable_csi = prp_vf_disable_csi; + cam->overlay_active = false; + } else + err = -EIO; + + return err; +} +EXPORT_SYMBOL(prp_vf_sdc_select); + +/*! + * function to de-select PRP-VF as the working path + * + * @param private cam_data * mxc v4l2 main structure + * + * @return int + */ +int prp_vf_sdc_deselect(void *private) +{ + cam_data *cam; + + if (private) { + cam = (cam_data *) private; + cam->vf_start_sdc = NULL; + cam->vf_stop_sdc = NULL; + cam->vf_enable_csi = NULL; + cam->vf_disable_csi = NULL; + } + return 0; +} +EXPORT_SYMBOL(prp_vf_sdc_deselect); + +/*! + * Init viewfinder task. + * + * @return Error code indicating success or failure + */ +__init int prp_vf_sdc_init(void) +{ + return 0; +} + +/*! + * Deinit viewfinder task. + * + * @return Error code indicating success or failure + */ +void __exit prp_vf_sdc_exit(void) +{ +} + +module_init(prp_vf_sdc_init); +module_exit(prp_vf_sdc_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("IPU PRP VF SDC Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,529 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ipu_prp_vf_sdc_bg.c + * + * @brief IPU Use case for PRP-VF back-ground + * + * @ingroup IPU + */ +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +static int buffer_num; +static int buffer_ready; +static struct ipu_soc *disp_ipu; + +static void get_disp_ipu(cam_data *cam) +{ + if (cam->output > 2) + disp_ipu = ipu_get_soc(1); /* using DISP4 */ + else + disp_ipu = ipu_get_soc(0); +} + +/* + * Function definitions + */ + +/*! + * SDC V-Sync callback function. + * + * @param irq int irq line + * @param dev_id void * device id + * + * @return status IRQ_HANDLED for handled + */ +static irqreturn_t prpvf_sdc_vsync_callback(int irq, void *dev_id) +{ + cam_data *cam = dev_id; + if (buffer_ready > 0) { + ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_OUTPUT_BUFFER, 0); + buffer_ready--; + } + + return IRQ_HANDLED; +} + +/*! + * VF EOF callback function. + * + * @param irq int irq line + * @param dev_id void * device id + * + * @return status IRQ_HANDLED for handled + */ +static irqreturn_t prpvf_vf_eof_callback(int irq, void *dev_id) +{ + cam_data *cam = dev_id; + pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num); + + ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_INPUT_BUFFER, buffer_num); + buffer_num = (buffer_num == 0) ? 1 : 0; + ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, + IPU_OUTPUT_BUFFER, buffer_num); + buffer_ready++; + return IRQ_HANDLED; +} + +/*! + * prpvf_start - start the vf task + * + * @param private cam_data * mxc v4l2 main structure + * + */ +static int prpvf_start(void *private) +{ + cam_data *cam = (cam_data *) private; + ipu_channel_params_t vf; + u32 format; + u32 offset; + u32 bpp, size = 3; + int err = 0; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + if (!cam) { + printk(KERN_ERR "private is NULL\n"); + return -EIO; + } + + if (cam->overlay_active == true) { + pr_debug("already start.\n"); + return 0; + } + + get_disp_ipu(cam); + + format = cam->v4l2_fb.fmt.pixelformat; + if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) { + bpp = 3, size = 3; + pr_info("BGR24\n"); + } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) { + bpp = 2, size = 2; + pr_info("RGB565\n"); + } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) { + bpp = 4, size = 4; + pr_info("BGR32\n"); + } else { + printk(KERN_ERR + "unsupported fix format from the framebuffer.\n"); + return -EINVAL; + } + + offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top + + size * cam->win.w.left; + + if (cam->v4l2_fb.base == 0) + printk(KERN_ERR "invalid frame buffer address.\n"); + else + offset += (u32) cam->v4l2_fb.base; + + memset(&vf, 0, sizeof(ipu_channel_params_t)); + ipu_csi_get_window_size(cam->ipu, &vf.csi_prp_vf_mem.in_width, + &vf.csi_prp_vf_mem.in_height, cam->csi); + vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY; + vf.csi_prp_vf_mem.out_width = cam->win.w.width; + vf.csi_prp_vf_mem.out_height = cam->win.w.height; + vf.csi_prp_vf_mem.csi = cam->csi; + if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) { + vf.csi_prp_vf_mem.out_width = cam->win.w.height; + vf.csi_prp_vf_mem.out_height = cam->win.w.width; + } + vf.csi_prp_vf_mem.out_pixel_fmt = format; + size = cam->win.w.width * cam->win.w.height * size; + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) { + vf.csi_prp_vf_mem.mipi_en = true; + vf.csi_prp_vf_mem.mipi_vc = + mipi_csi2_get_virtual_channel(mipi_csi2_info); + vf.csi_prp_vf_mem.mipi_id = + mipi_csi2_get_datatype(mipi_csi2_info); + + mipi_csi2_pixelclk_enable(mipi_csi2_info); + } else { + vf.csi_prp_vf_mem.mipi_en = false; + vf.csi_prp_vf_mem.mipi_vc = 0; + vf.csi_prp_vf_mem.mipi_id = 0; + } + } else { + vf.csi_prp_vf_mem.mipi_en = false; + vf.csi_prp_vf_mem.mipi_vc = 0; + vf.csi_prp_vf_mem.mipi_id = 0; + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf); + if (err != 0) + goto out_4; + + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], cam->vf_bufs[0]); + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], cam->vf_bufs[1]); + } + cam->vf_bufs_size[0] = PAGE_ALIGN(size); + cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0, + cam->vf_bufs_size[0], + &cam->vf_bufs[0], + GFP_DMA | + GFP_KERNEL); + if (cam->vf_bufs_vaddr[0] == NULL) { + printk(KERN_ERR "Error to allocate vf buffer\n"); + err = -ENOMEM; + goto out_3; + } + cam->vf_bufs_size[1] = PAGE_ALIGN(size); + cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0, + cam->vf_bufs_size[1], + &cam->vf_bufs[1], + GFP_DMA | + GFP_KERNEL); + if (cam->vf_bufs_vaddr[1] == NULL) { + printk(KERN_ERR "Error to allocate vf buffer\n"); + err = -ENOMEM; + goto out_3; + } + + err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM, + IPU_OUTPUT_BUFFER, + format, vf.csi_prp_vf_mem.out_width, + vf.csi_prp_vf_mem.out_height, + vf.csi_prp_vf_mem.out_width, + IPU_ROTATE_NONE, + cam->vf_bufs[0], + cam->vf_bufs[1], + 0, 0, 0); + if (err != 0) { + printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n"); + goto out_3; + } + err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL); + if (err != 0) { + printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n"); + goto out_3; + } + + err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_INPUT_BUFFER, + format, vf.csi_prp_vf_mem.out_width, + vf.csi_prp_vf_mem.out_height, + vf.csi_prp_vf_mem.out_width, + cam->vf_rotation, + cam->vf_bufs[0], + cam->vf_bufs[1], + 0, 0, 0); + if (err != 0) { + printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n"); + goto out_2; + } + + if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) { + err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_OUTPUT_BUFFER, + format, + vf.csi_prp_vf_mem.out_height, + vf.csi_prp_vf_mem.out_width, + cam->overlay_fb->var.xres * bpp, + IPU_ROTATE_NONE, + offset, 0, 0, 0, 0); + + if (err != 0) { + printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n"); + goto out_2; + } + } else { + err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM, + IPU_OUTPUT_BUFFER, + format, + vf.csi_prp_vf_mem.out_width, + vf.csi_prp_vf_mem.out_height, + cam->overlay_fb->var.xres * bpp, + IPU_ROTATE_NONE, + offset, 0, 0, 0, 0); + if (err != 0) { + printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n"); + goto out_2; + } + } + + ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF); + err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, + prpvf_vf_eof_callback, + 0, "Mxc Camera", cam); + if (err != 0) { + printk(KERN_ERR + "Error registering IPU_IRQ_PRP_VF_OUT_EOF irq.\n"); + goto out_2; + } + + ipu_clear_irq(disp_ipu, IPU_IRQ_BG_SF_END); + err = ipu_request_irq(disp_ipu, IPU_IRQ_BG_SF_END, + prpvf_sdc_vsync_callback, + 0, "Mxc Camera", cam); + if (err != 0) { + printk(KERN_ERR "Error registering IPU_IRQ_BG_SF_END irq.\n"); + goto out_1; + } + + ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM); + ipu_enable_channel(cam->ipu, MEM_ROT_VF_MEM); + + buffer_num = 0; + buffer_ready = 0; + ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0); + + cam->overlay_active = true; + return err; + +out_1: + ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL); +out_2: + ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); +out_3: + ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); +out_4: + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], cam->vf_bufs[0]); + cam->vf_bufs_vaddr[0] = NULL; + cam->vf_bufs[0] = 0; + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], cam->vf_bufs[1]); + cam->vf_bufs_vaddr[1] = NULL; + cam->vf_bufs[1] = 0; + } + if (cam->rot_vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->rot_vf_buf_size[0], + cam->rot_vf_bufs_vaddr[0], + cam->rot_vf_bufs[0]); + cam->rot_vf_bufs_vaddr[0] = NULL; + cam->rot_vf_bufs[0] = 0; + } + if (cam->rot_vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->rot_vf_buf_size[1], + cam->rot_vf_bufs_vaddr[1], + cam->rot_vf_bufs[1]); + cam->rot_vf_bufs_vaddr[1] = NULL; + cam->rot_vf_bufs[1] = 0; + } + return err; +} + +/*! + * prpvf_stop - stop the vf task + * + * @param private cam_data * mxc v4l2 main structure + * + */ +static int prpvf_stop(void *private) +{ + cam_data *cam = (cam_data *) private; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + + if (cam->overlay_active == false) + return 0; + + ipu_free_irq(disp_ipu, IPU_IRQ_BG_SF_END, cam); + + ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true); + ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true); + ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); + ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); + +#ifdef CONFIG_MXC_MIPI_CSI2 + mipi_csi2_info = mipi_csi2_get_info(); + + if (mipi_csi2_info) { + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) + mipi_csi2_pixelclk_disable(mipi_csi2_info); + } + } else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } +#endif + + if (cam->vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->vf_bufs_size[0], + cam->vf_bufs_vaddr[0], cam->vf_bufs[0]); + cam->vf_bufs_vaddr[0] = NULL; + cam->vf_bufs[0] = 0; + } + if (cam->vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->vf_bufs_size[1], + cam->vf_bufs_vaddr[1], cam->vf_bufs[1]); + cam->vf_bufs_vaddr[1] = NULL; + cam->vf_bufs[1] = 0; + } + if (cam->rot_vf_bufs_vaddr[0]) { + dma_free_coherent(0, cam->rot_vf_buf_size[0], + cam->rot_vf_bufs_vaddr[0], + cam->rot_vf_bufs[0]); + cam->rot_vf_bufs_vaddr[0] = NULL; + cam->rot_vf_bufs[0] = 0; + } + if (cam->rot_vf_bufs_vaddr[1]) { + dma_free_coherent(0, cam->rot_vf_buf_size[1], + cam->rot_vf_bufs_vaddr[1], + cam->rot_vf_bufs[1]); + cam->rot_vf_bufs_vaddr[1] = NULL; + cam->rot_vf_bufs[1] = 0; + } + + buffer_num = 0; + buffer_ready = 0; + cam->overlay_active = false; + return 0; +} + +/*! + * Enable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_vf_enable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + return ipu_enable_csi(cam->ipu, cam->csi); +} + +/*! + * Disable csi + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_vf_disable_csi(void *private) +{ + cam_data *cam = (cam_data *) private; + + /* free csi eof irq firstly. + * when disable csi, wait for idmac eof. + * it requests eof irq again */ + ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam); + + return ipu_disable_csi(cam->ipu, cam->csi); +} + +/*! + * function to select PRP-VF as the working path + * + * @param private cam_data * mxc v4l2 main structure + * + * @return status + */ +int prp_vf_sdc_select_bg(void *private) +{ + cam_data *cam = (cam_data *) private; + + if (cam) { + cam->vf_start_sdc = prpvf_start; + cam->vf_stop_sdc = prpvf_stop; + cam->vf_enable_csi = prp_vf_enable_csi; + cam->vf_disable_csi = prp_vf_disable_csi; + cam->overlay_active = false; + } + + return 0; +} +EXPORT_SYMBOL(prp_vf_sdc_select_bg); + +/*! + * function to de-select PRP-VF as the working path + * + * @param private cam_data * mxc v4l2 main structure + * + * @return status + */ +int prp_vf_sdc_deselect_bg(void *private) +{ + cam_data *cam = (cam_data *) private; + + if (cam) { + cam->vf_start_sdc = NULL; + cam->vf_stop_sdc = NULL; + cam->vf_enable_csi = NULL; + cam->vf_disable_csi = NULL; + } + return 0; +} +EXPORT_SYMBOL(prp_vf_sdc_deselect_bg); + +/*! + * Init viewfinder task. + * + * @return Error code indicating success or failure + */ +__init int prp_vf_sdc_init_bg(void) +{ + return 0; +} + +/*! + * Deinit viewfinder task. + * + * @return Error code indicating success or failure + */ +void __exit prp_vf_sdc_exit_bg(void) +{ +} + +module_init(prp_vf_sdc_init_bg); +module_exit(prp_vf_sdc_exit_bg); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ipu_still.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_still.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ipu_still.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ipu_still.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,268 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file ipu_still.c + * + * @brief IPU Use case for still image capture + * + * @ingroup IPU + */ + +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +static int callback_eof_flag; +#ifndef CONFIG_MXC_IPU_V1 +static int buffer_num; +#endif + +#ifdef CONFIG_MXC_IPU_V1 +static int callback_flag; +/* + * Function definitions + */ +/*! + * CSI EOF callback function. + * + * @param irq int irq line + * @param dev_id void * device id + * + * @return status IRQ_HANDLED for handled + */ +static irqreturn_t prp_csi_eof_callback(int irq, void *dev_id) +{ + cam_data *cam = devid; + ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, + callback_flag%2 ? 1 : 0); + if (callback_flag == 0) + ipu_enable_channel(cam->ipu, CSI_MEM); + + callback_flag++; + return IRQ_HANDLED; +} +#endif + +/*! + * CSI callback function. + * + * @param irq int irq line + * @param dev_id void * device id + * + * @return status IRQ_HANDLED for handled + */ +static irqreturn_t prp_still_callback(int irq, void *dev_id) +{ + cam_data *cam = (cam_data *) dev_id; + + callback_eof_flag++; + if (callback_eof_flag < 5) { +#ifndef CONFIG_MXC_IPU_V1 + buffer_num = (buffer_num == 0) ? 1 : 0; + ipu_select_buffer(cam->ipu, CSI_MEM, + IPU_OUTPUT_BUFFER, buffer_num); +#endif + } else { + cam->still_counter++; + wake_up_interruptible(&cam->still_queue); + } + + return IRQ_HANDLED; +} + +/*! + * start csi->mem task + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_still_start(void *private) +{ + cam_data *cam = (cam_data *) private; + u32 pixel_fmt; + int err; + ipu_channel_params_t params; + + if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + pixel_fmt = IPU_PIX_FMT_YUV420P; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) + pixel_fmt = IPU_PIX_FMT_NV12; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) + pixel_fmt = IPU_PIX_FMT_YUV422P; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) + pixel_fmt = IPU_PIX_FMT_UYVY; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pixel_fmt = IPU_PIX_FMT_YUYV; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) + pixel_fmt = IPU_PIX_FMT_BGR24; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) + pixel_fmt = IPU_PIX_FMT_RGB24; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) + pixel_fmt = IPU_PIX_FMT_RGB565; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) + pixel_fmt = IPU_PIX_FMT_BGR32; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) + pixel_fmt = IPU_PIX_FMT_RGB32; + else { + printk(KERN_ERR "format not supported\n"); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(params)); + err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); + if (err != 0) + return err; + + err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, + pixel_fmt, cam->v2f.fmt.pix.width, + cam->v2f.fmt.pix.height, + cam->v2f.fmt.pix.width, IPU_ROTATE_NONE, + cam->still_buf[0], cam->still_buf[1], 0, + 0, 0); + if (err != 0) + return err; + +#ifdef CONFIG_MXC_IPU_V1 + ipu_clear_irq(IPU_IRQ_SENSOR_OUT_EOF); + err = ipu_request_irq(IPU_IRQ_SENSOR_OUT_EOF, prp_still_callback, + 0, "Mxc Camera", cam); + if (err != 0) { + printk(KERN_ERR "Error registering irq.\n"); + return err; + } + callback_flag = 0; + callback_eof_flag = 0; + ipu_clear_irq(IPU_IRQ_SENSOR_EOF); + err = ipu_request_irq(IPU_IRQ_SENSOR_EOF, prp_csi_eof_callback, + 0, "Mxc Camera", cam); + if (err != 0) { + printk(KERN_ERR "Error IPU_IRQ_SENSOR_EOF\n"); + return err; + } +#else + callback_eof_flag = 0; + buffer_num = 0; + + ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); + err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, + prp_still_callback, + 0, "Mxc Camera", cam); + if (err != 0) { + printk(KERN_ERR "Error registering irq.\n"); + return err; + } + + ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0); + ipu_enable_channel(cam->ipu, CSI_MEM); + ipu_enable_csi(cam->ipu, cam->csi); +#endif + + return err; +} + +/*! + * stop csi->mem encoder task + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +static int prp_still_stop(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + +#ifdef CONFIG_MXC_IPU_V1 + ipu_free_irq(IPU_IRQ_SENSOR_EOF, NULL); + ipu_free_irq(IPU_IRQ_SENSOR_OUT_EOF, cam); +#else + ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); +#endif + + ipu_disable_csi(cam->ipu, cam->csi); + ipu_disable_channel(cam->ipu, CSI_MEM, true); + ipu_uninit_channel(cam->ipu, CSI_MEM); + + return err; +} + +/*! + * function to select CSI_MEM as the working path + * + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +int prp_still_select(void *private) +{ + cam_data *cam = (cam_data *) private; + + if (cam) { + cam->csi_start = prp_still_start; + cam->csi_stop = prp_still_stop; + } + + return 0; +} +EXPORT_SYMBOL(prp_still_select); + +/*! + * function to de-select CSI_MEM as the working path + * + * @param private struct cam_data * mxc capture instance + * + * @return status + */ +int prp_still_deselect(void *private) +{ + cam_data *cam = (cam_data *) private; + int err = 0; + + err = prp_still_stop(cam); + + if (cam) { + cam->csi_start = NULL; + cam->csi_stop = NULL; + } + + return err; +} +EXPORT_SYMBOL(prp_still_deselect); + +/*! + * Init the Encorder channels + * + * @return Error code indicating success or failure + */ +__init int prp_still_init(void) +{ + return 0; +} + +/*! + * Deinit the Encorder channels + * + */ +void __exit prp_still_exit(void) +{ +} + +module_init(prp_still_init); +module_exit(prp_still_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("IPU PRP STILL IMAGE Driver"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c --- linux-3.10.30/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,3109 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @file drivers/media/video/mxc/capture/mxc_v4l2_capture.c + * + * @brief Mxc Video For Linux 2 driver + * + * @ingroup MXC_V4L2_CAPTURE + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" +#include "ipu_prp_sw.h" + +#define init_MUTEX(sem) sema_init(sem, 1) + +static struct platform_device_id imx_v4l2_devtype[] = { + { + .name = "v4l2-capture-imx5", + .driver_data = IMX5_V4L2, + }, { + .name = "v4l2-capture-imx6", + .driver_data = IMX6_V4L2, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, imx_v4l2_devtype); + +static const struct of_device_id mxc_v4l2_dt_ids[] = { + { + .compatible = "fsl,imx6q-v4l2-capture", + .data = &imx_v4l2_devtype[IMX6_V4L2], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, mxc_v4l2_dt_ids); + +static int video_nr = -1; + +/*! This data is used for the output to the display. */ +#define MXC_V4L2_CAPTURE_NUM_OUTPUTS 6 +#define MXC_V4L2_CAPTURE_NUM_INPUTS 2 +static struct v4l2_output mxc_capture_outputs[MXC_V4L2_CAPTURE_NUM_OUTPUTS] = { + { + .index = 0, + .name = "DISP3 BG", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + }, + { + .index = 1, + .name = "DISP3 BG - DI1", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + }, + { + .index = 2, + .name = "DISP3 FG", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + }, + { + .index = 3, + .name = "DISP4 BG", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + }, + { + .index = 4, + .name = "DISP4 BG - DI1", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + }, + { + .index = 5, + .name = "DISP4 FG", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .audioset = 0, + .modulator = 0, + .std = V4L2_STD_UNKNOWN, + }, +}; + +static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = { + { + .index = 0, + .name = "CSI IC MEM", + .type = V4L2_INPUT_TYPE_CAMERA, + .audioset = 0, + .tuner = 0, + .std = V4L2_STD_UNKNOWN, + .status = 0, + }, + { + .index = 1, + .name = "CSI MEM", + .type = V4L2_INPUT_TYPE_CAMERA, + .audioset = 0, + .tuner = 0, + .std = V4L2_STD_UNKNOWN, + .status = V4L2_IN_ST_NO_POWER, + }, +}; + +/*! List of TV input video formats supported. The video formats is corresponding + * to the v4l2_id in video_fmt_t. + * Currently, only PAL and NTSC is supported. Needs to be expanded in the + * future. + */ +typedef enum { + TV_NTSC = 0, /*!< Locked on (M) NTSC video signal. */ + TV_PAL, /*!< (B, G, H, I, N)PAL video signal. */ + TV_NOT_LOCKED, /*!< Not locked on a signal. */ +} video_fmt_idx; + +/*! Number of video standards supported (including 'not locked' signal). */ +#define TV_STD_MAX (TV_NOT_LOCKED + 1) + +/*! Video format structure. */ +typedef struct { + int v4l2_id; /*!< Video for linux ID. */ + char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */ + u16 raw_width; /*!< Raw width. */ + u16 raw_height; /*!< Raw height. */ + u16 active_width; /*!< Active width. */ + u16 active_height; /*!< Active height. */ + u16 active_top; /*!< Active top. */ + u16 active_left; /*!< Active left. */ +} video_fmt_t; + +/*! + * Description of video formats supported. + * + * PAL: raw=720x625, active=720x576. + * NTSC: raw=720x525, active=720x480. + */ +static video_fmt_t video_fmts[] = { + { /*! NTSC */ + .v4l2_id = V4L2_STD_NTSC, + .name = "NTSC", + .raw_width = 720, /* SENS_FRM_WIDTH */ + .raw_height = 525, /* SENS_FRM_HEIGHT */ + .active_width = 720, /* ACT_FRM_WIDTH */ + .active_height = 480, /* ACT_FRM_HEIGHT */ + .active_top = 13, + .active_left = 0, + }, + { /*! (B, G, H, I, N) PAL */ + .v4l2_id = V4L2_STD_PAL, + .name = "PAL", + .raw_width = 720, + .raw_height = 625, + .active_width = 720, + .active_height = 576, + .active_top = 0, + .active_left = 0, + }, + { /*! Unlocked standard */ + .v4l2_id = V4L2_STD_ALL, + .name = "Autodetect", + .raw_width = 720, + .raw_height = 625, + .active_width = 720, + .active_height = 576, + .active_top = 0, + .active_left = 0, + }, +}; + +/*!* Standard index of TV. */ +static video_fmt_idx video_index = TV_NOT_LOCKED; + +static int mxc_v4l2_master_attach(struct v4l2_int_device *slave); +static void mxc_v4l2_master_detach(struct v4l2_int_device *slave); +static int start_preview(cam_data *cam); +static int stop_preview(cam_data *cam); + +/*! Information about this driver. */ +static struct v4l2_int_master mxc_v4l2_master = { + .attach = mxc_v4l2_master_attach, + .detach = mxc_v4l2_master_detach, +}; + +/*************************************************************************** + * Functions for handling Frame buffers. + **************************************************************************/ + +/*! + * Free frame buffers + * + * @param cam Structure cam_data * + * + * @return status 0 success. + */ +static int mxc_free_frame_buf(cam_data *cam) +{ + int i; + + pr_debug("MVC: In mxc_free_frame_buf\n"); + + for (i = 0; i < FRAME_NUM; i++) { + if (cam->frame[i].vaddress != 0) { + dma_free_coherent(0, cam->frame[i].buffer.length, + cam->frame[i].vaddress, + cam->frame[i].paddress); + cam->frame[i].vaddress = 0; + } + } + + return 0; +} + +/*! + * Allocate frame buffers + * + * @param cam Structure cam_data* + * @param count int number of buffer need to allocated + * + * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. + */ +static int mxc_allocate_frame_buf(cam_data *cam, int count) +{ + int i; + + pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n", + cam->v2f.fmt.pix.sizeimage); + + for (i = 0; i < count; i++) { + cam->frame[i].vaddress = + dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->frame[i].paddress, + GFP_DMA | GFP_KERNEL); + if (cam->frame[i].vaddress == 0) { + pr_err("ERROR: v4l2 capture: " + "mxc_allocate_frame_buf failed.\n"); + mxc_free_frame_buf(cam); + return -ENOBUFS; + } + cam->frame[i].buffer.index = i; + cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; + cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buffer.length = + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); + cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buffer.m.offset = cam->frame[i].paddress; + cam->frame[i].index = i; + } + + return 0; +} + +/*! + * Free frame buffers status + * + * @param cam Structure cam_data * + * + * @return none + */ +static void mxc_free_frames(cam_data *cam) +{ + int i; + + pr_debug("In MVC:mxc_free_frames\n"); + + for (i = 0; i < FRAME_NUM; i++) + cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; + + cam->enc_counter = 0; + INIT_LIST_HEAD(&cam->ready_q); + INIT_LIST_HEAD(&cam->working_q); + INIT_LIST_HEAD(&cam->done_q); +} + +/*! + * Return the buffer status + * + * @param cam Structure cam_data * + * @param buf Structure v4l2_buffer * + * + * @return status 0 success, EINVAL failed. + */ +static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf) +{ + pr_debug("In MVC:mxc_v4l2_buffer_status\n"); + + if (buf->index < 0 || buf->index >= FRAME_NUM) { + pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers " + "not allocated\n"); + return -EINVAL; + } + + memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf)); + return 0; +} + +static int mxc_v4l2_release_bufs(cam_data *cam) +{ + pr_debug("In MVC:mxc_v4l2_release_bufs\n"); + return 0; +} + +static int mxc_v4l2_prepare_bufs(cam_data *cam, struct v4l2_buffer *buf) +{ + pr_debug("In MVC:mxc_v4l2_prepare_bufs\n"); + + if (buf->index < 0 || buf->index >= FRAME_NUM || buf->length < + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage)) { + pr_err("ERROR: v4l2 capture: mxc_v4l2_prepare_bufs buffers " + "not allocated,index=%d, length=%d\n", buf->index, + buf->length); + return -EINVAL; + } + + cam->frame[buf->index].buffer.index = buf->index; + cam->frame[buf->index].buffer.flags = V4L2_BUF_FLAG_MAPPED; + cam->frame[buf->index].buffer.length = buf->length; + cam->frame[buf->index].buffer.m.offset = cam->frame[buf->index].paddress + = buf->m.offset; + cam->frame[buf->index].buffer.type = buf->type; + cam->frame[buf->index].buffer.memory = V4L2_MEMORY_USERPTR; + cam->frame[buf->index].index = buf->index; + + return 0; +} + +/*************************************************************************** + * Functions for handling the video stream. + **************************************************************************/ + +/*! + * Indicates whether the palette is supported. + * + * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 + * + * @return 0 if failed + */ +static inline int valid_mode(u32 palette) +{ + return ((palette == V4L2_PIX_FMT_RGB565) || + (palette == V4L2_PIX_FMT_BGR24) || + (palette == V4L2_PIX_FMT_RGB24) || + (palette == V4L2_PIX_FMT_BGR32) || + (palette == V4L2_PIX_FMT_RGB32) || + (palette == V4L2_PIX_FMT_YUV422P) || + (palette == V4L2_PIX_FMT_UYVY) || + (palette == V4L2_PIX_FMT_YUYV) || + (palette == V4L2_PIX_FMT_YUV420) || + (palette == V4L2_PIX_FMT_YVU420) || + (palette == V4L2_PIX_FMT_NV12)); +} + +/*! + * Start the encoder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int mxc_streamon(cam_data *cam) +{ + struct mxc_v4l_frame *frame; + unsigned long lock_flags; + int err = 0; + + pr_debug("In MVC:mxc_streamon\n"); + + if (NULL == cam) { + pr_err("ERROR! cam parameter is NULL\n"); + return -1; + } + + if (cam->capture_on) { + pr_err("ERROR: v4l2 capture: Capture stream has been turned " + " on\n"); + return -1; + } + + if (list_empty(&cam->ready_q)) { + pr_err("ERROR: v4l2 capture: mxc_streamon buffer has not been " + "queued yet\n"); + return -EINVAL; + } + if (cam->enc_update_eba && + cam->ready_q.prev == cam->ready_q.next) { + pr_err("ERROR: v4l2 capture: mxc_streamon buffer need " + "ping pong at least two buffers\n"); + return -EINVAL; + } + + cam->capture_pid = current->pid; + + if (cam->overlay_on == true) + stop_preview(cam); + + if (cam->enc_enable) { + err = cam->enc_enable(cam); + if (err != 0) + return err; + } + + spin_lock_irqsave(&cam->queue_int_lock, lock_flags); + cam->ping_pong_csi = 0; + cam->local_buf_num = 0; + if (cam->enc_update_eba) { + frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&frame->queue, &cam->working_q); + frame->ipu_buf_num = cam->ping_pong_csi; + err = cam->enc_update_eba(cam->ipu, frame->buffer.m.offset, + &cam->ping_pong_csi); + + frame = + list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue); + list_del(cam->ready_q.next); + list_add_tail(&frame->queue, &cam->working_q); + frame->ipu_buf_num = cam->ping_pong_csi; + err |= cam->enc_update_eba(cam->ipu, frame->buffer.m.offset, + &cam->ping_pong_csi); + spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags); + } else { + spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags); + return -EINVAL; + } + + if (cam->overlay_on == true) + start_preview(cam); + + if (cam->enc_enable_csi) { + err = cam->enc_enable_csi(cam); + if (err != 0) + return err; + } + + cam->capture_on = true; + + return err; +} + +/*! + * Shut down the encoder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int mxc_streamoff(cam_data *cam) +{ + int err = 0; + + pr_debug("In MVC:mxc_streamoff\n"); + + if (cam->capture_on == false) + return 0; + + /* For both CSI--MEM and CSI--IC--MEM + * 1. wait for idmac eof + * 2. disable csi first + * 3. disable idmac + * 4. disable smfc (CSI--MEM channel) + */ + if (mxc_capture_inputs[cam->current_input].name != NULL) { + if (cam->enc_disable_csi) { + err = cam->enc_disable_csi(cam); + if (err != 0) + return err; + } + if (cam->enc_disable) { + err = cam->enc_disable(cam); + if (err != 0) + return err; + } + } + + mxc_free_frames(cam); + mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER; + cam->capture_on = false; + return err; +} + +/*! + * Valid and adjust the overlay window size, position + * + * @param cam structure cam_data * + * @param win struct v4l2_window * + * + * @return 0 + */ +static int verify_preview(cam_data *cam, struct v4l2_window *win) +{ + int i = 0, width_bound = 0, height_bound = 0; + int *width, *height; + unsigned int ipu_ch = CHAN_NONE; + struct fb_info *bg_fbi = NULL, *fbi = NULL; + bool foregound_fb = false; + mm_segment_t old_fs; + + pr_debug("In MVC: verify_preview\n"); + + do { + fbi = (struct fb_info *)registered_fb[i]; + if (fbi == NULL) { + pr_err("ERROR: verify_preview frame buffer NULL.\n"); + return -1; + } + + /* Which DI supports 2 layers? */ + if (((strncmp(fbi->fix.id, "DISP3 BG", 8) == 0) && + (cam->output < 3)) || + ((strncmp(fbi->fix.id, "DISP4 BG", 8) == 0) && + (cam->output >= 3))) { + if (fbi->fbops->fb_ioctl) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN, + (unsigned long)&ipu_ch); + set_fs(old_fs); + } + if (ipu_ch == MEM_BG_SYNC) { + bg_fbi = fbi; + pr_debug("Found background frame buffer.\n"); + } + } + + /* Found the frame buffer to preview on. */ + if (strcmp(fbi->fix.id, + mxc_capture_outputs[cam->output].name) == 0) { + if (((strcmp(fbi->fix.id, "DISP3 FG") == 0) && + (cam->output < 3)) || + ((strcmp(fbi->fix.id, "DISP4 FG") == 0) && + (cam->output >= 3))) + foregound_fb = true; + + cam->overlay_fb = fbi; + break; + } + } while (++i < FB_MAX); + + if (foregound_fb) { + width_bound = bg_fbi->var.xres; + height_bound = bg_fbi->var.yres; + + if (win->w.width + win->w.left > bg_fbi->var.xres || + win->w.height + win->w.top > bg_fbi->var.yres) { + pr_err("ERROR: FG window position exceeds.\n"); + return -1; + } + } else { + /* 4 bytes alignment for BG */ + width_bound = cam->overlay_fb->var.xres; + height_bound = cam->overlay_fb->var.yres; + + if (cam->overlay_fb->var.bits_per_pixel == 24) + win->w.left -= win->w.left % 4; + else if (cam->overlay_fb->var.bits_per_pixel == 16) + win->w.left -= win->w.left % 2; + + if (win->w.width + win->w.left > cam->overlay_fb->var.xres) + win->w.width = cam->overlay_fb->var.xres - win->w.left; + if (win->w.height + win->w.top > cam->overlay_fb->var.yres) + win->w.height = cam->overlay_fb->var.yres - win->w.top; + } + + /* stride line limitation */ + win->w.height -= win->w.height % 8; + win->w.width -= win->w.width % 8; + + if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + height = &win->w.width; + width = &win->w.height; + } else { + width = &win->w.width; + height = &win->w.height; + } + + if (*width == 0 || *height == 0) { + pr_err("ERROR: v4l2 capture: width or height" + " too small.\n"); + return -EINVAL; + } + + if ((cam->crop_bounds.width / *width > 8) || + ((cam->crop_bounds.width / *width == 8) && + (cam->crop_bounds.width % *width))) { + *width = cam->crop_bounds.width / 8; + if (*width % 8) + *width += 8 - *width % 8; + if (*width + win->w.left > width_bound) { + pr_err("ERROR: v4l2 capture: width exceeds " + "resize limit.\n"); + return -1; + } + pr_err("ERROR: v4l2 capture: width exceeds limit. " + "Resize to %d.\n", + *width); + } + + if ((cam->crop_bounds.height / *height > 8) || + ((cam->crop_bounds.height / *height == 8) && + (cam->crop_bounds.height % *height))) { + *height = cam->crop_bounds.height / 8; + if (*height % 8) + *height += 8 - *height % 8; + if (*height + win->w.top > height_bound) { + pr_err("ERROR: v4l2 capture: height exceeds " + "resize limit.\n"); + return -1; + } + pr_err("ERROR: v4l2 capture: height exceeds limit " + "resize to %d.\n", + *height); + } + + return 0; +} + +/*! + * start the viewfinder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int start_preview(cam_data *cam) +{ + int err = 0; + + pr_debug("MVC: start_preview\n"); + + if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) + #ifdef CONFIG_MXC_IPU_PRP_VF_SDC + err = prp_vf_sdc_select(cam); + #else + err = foreground_sdc_select(cam); + #endif + else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY) + #ifdef CONFIG_MXC_IPU_PRP_VF_SDC + err = prp_vf_sdc_select_bg(cam); + #else + err = bg_overlay_sdc_select(cam); + #endif + if (err != 0) + return err; + + if (cam->vf_start_sdc) { + err = cam->vf_start_sdc(cam); + if (err != 0) + return err; + } + + if (cam->vf_enable_csi) + err = cam->vf_enable_csi(cam); + + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", + __func__, + cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", + __func__, + cam->crop_bounds.width, cam->crop_bounds.height); + pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", + __func__, + cam->crop_defrect.width, cam->crop_defrect.height); + pr_debug("End of %s: crop_current widthxheight %d x %d\n", + __func__, + cam->crop_current.width, cam->crop_current.height); + + return err; +} + +/*! + * shut down the viewfinder job + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int stop_preview(cam_data *cam) +{ + int err = 0; + + if (cam->vf_disable_csi) { + err = cam->vf_disable_csi(cam); + if (err != 0) + return err; + } + + if (cam->vf_stop_sdc) { + err = cam->vf_stop_sdc(cam); + if (err != 0) + return err; + } + + if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) + #ifdef CONFIG_MXC_IPU_PRP_VF_SDC + err = prp_vf_sdc_deselect(cam); + #else + err = foreground_sdc_deselect(cam); + #endif + else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY) + #ifdef CONFIG_MXC_IPU_PRP_VF_SDC + err = prp_vf_sdc_deselect_bg(cam); + #else + err = bg_overlay_sdc_deselect(cam); + #endif + + return err; +} + +/*************************************************************************** + * VIDIOC Functions. + **************************************************************************/ + +/*! + * V4L2 - mxc_v4l2_g_fmt function + * + * @param cam structure cam_data * + * + * @param f structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f) +{ + int retval = 0; + + pr_debug("In MVC: mxc_v4l2_g_fmt type=%d\n", f->type); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + f->fmt.pix = cam->v2f.fmt.pix; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); + f->fmt.win = cam->win; + break; + default: + pr_debug(" type is invalid\n"); + retval = -EINVAL; + } + + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", + __func__, + cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", + __func__, + cam->crop_bounds.width, cam->crop_bounds.height); + pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", + __func__, + cam->crop_defrect.width, cam->crop_defrect.height); + pr_debug("End of %s: crop_current widthxheight %d x %d\n", + __func__, + cam->crop_current.width, cam->crop_current.height); + + return retval; +} + +/*! + * V4L2 - mxc_v4l2_s_fmt function + * + * @param cam structure cam_data * + * + * @param f structure v4l2_format * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) +{ + int retval = 0; + int size = 0; + int bytesperline = 0; + int *width, *height; + + pr_debug("In MVC: mxc_v4l2_s_fmt\n"); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + if (!valid_mode(f->fmt.pix.pixelformat)) { + pr_err("ERROR: v4l2 capture: mxc_v4l2_s_fmt: format " + "not supported\n"); + return -EINVAL; + } + + /* + * Force the capture window resolution to be crop bounds + * for CSI MEM input mode. + */ + if (strcmp(mxc_capture_inputs[cam->current_input].name, + "CSI MEM") == 0) { + f->fmt.pix.width = cam->crop_current.width; + f->fmt.pix.height = cam->crop_current.height; + } + + if (cam->rotation >= IPU_ROTATE_90_RIGHT) { + height = &f->fmt.pix.width; + width = &f->fmt.pix.height; + } else { + width = &f->fmt.pix.width; + height = &f->fmt.pix.height; + } + + /* stride line limitation */ + *width -= *width % 8; + *height -= *height % 8; + + if (*width == 0 || *height == 0) { + pr_err("ERROR: v4l2 capture: width or height" + " too small.\n"); + return -EINVAL; + } + + if ((cam->crop_current.width / *width > 8) || + ((cam->crop_current.width / *width == 8) && + (cam->crop_current.width % *width))) { + *width = cam->crop_current.width / 8; + if (*width % 8) + *width += 8 - *width % 8; + pr_err("ERROR: v4l2 capture: width exceeds limit " + "resize to %d.\n", + *width); + } + + if ((cam->crop_current.height / *height > 8) || + ((cam->crop_current.height / *height == 8) && + (cam->crop_current.height % *height))) { + *height = cam->crop_current.height / 8; + if (*height % 8) + *height += 8 - *height % 8; + pr_err("ERROR: v4l2 capture: height exceeds limit " + "resize to %d.\n", + *height); + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + bytesperline = f->fmt.pix.width * 2; + break; + case V4L2_PIX_FMT_BGR24: + size = f->fmt.pix.width * f->fmt.pix.height * 3; + bytesperline = f->fmt.pix.width * 3; + break; + case V4L2_PIX_FMT_RGB24: + size = f->fmt.pix.width * f->fmt.pix.height * 3; + bytesperline = f->fmt.pix.width * 3; + break; + case V4L2_PIX_FMT_BGR32: + size = f->fmt.pix.width * f->fmt.pix.height * 4; + bytesperline = f->fmt.pix.width * 4; + break; + case V4L2_PIX_FMT_RGB32: + size = f->fmt.pix.width * f->fmt.pix.height * 4; + bytesperline = f->fmt.pix.width * 4; + break; + case V4L2_PIX_FMT_YUV422P: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + bytesperline = f->fmt.pix.width; + break; + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + size = f->fmt.pix.width * f->fmt.pix.height * 2; + bytesperline = f->fmt.pix.width * 2; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; + bytesperline = f->fmt.pix.width; + break; + case V4L2_PIX_FMT_NV12: + size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; + bytesperline = f->fmt.pix.width; + break; + default: + break; + } + + if (f->fmt.pix.bytesperline < bytesperline) + f->fmt.pix.bytesperline = bytesperline; + else + bytesperline = f->fmt.pix.bytesperline; + + if (f->fmt.pix.sizeimage < size) + f->fmt.pix.sizeimage = size; + else + size = f->fmt.pix.sizeimage; + + cam->v2f.fmt.pix = f->fmt.pix; + + if (cam->v2f.fmt.pix.priv != 0) { + if (copy_from_user(&cam->offset, + (void *)cam->v2f.fmt.pix.priv, + sizeof(cam->offset))) { + retval = -EFAULT; + break; + } + } + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); + retval = verify_preview(cam, &f->fmt.win); + cam->win = f->fmt.win; + break; + default: + retval = -EINVAL; + } + + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", + __func__, + cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", + __func__, + cam->crop_bounds.width, cam->crop_bounds.height); + pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", + __func__, + cam->crop_defrect.width, cam->crop_defrect.height); + pr_debug("End of %s: crop_current widthxheight %d x %d\n", + __func__, + cam->crop_current.width, cam->crop_current.height); + + return retval; +} + +/*! + * get control param + * + * @param cam structure cam_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_g_ctrl(cam_data *cam, struct v4l2_control *c) +{ + int status = 0; + + pr_debug("In MVC:mxc_v4l2_g_ctrl\n"); + + /* probably don't need to store the values that can be retrieved, + * locally, but they are for now. */ + switch (c->id) { + case V4L2_CID_HFLIP: + /* This is handled in the ipu. */ + if (cam->rotation == IPU_ROTATE_HORIZ_FLIP) + c->value = 1; + break; + case V4L2_CID_VFLIP: + /* This is handled in the ipu. */ + if (cam->rotation == IPU_ROTATE_VERT_FLIP) + c->value = 1; + break; + case V4L2_CID_MXC_ROT: + /* This is handled in the ipu. */ + c->value = cam->rotation; + break; + case V4L2_CID_BRIGHTNESS: + if (cam->sensor) { + c->value = cam->bright; + status = vidioc_int_g_ctrl(cam->sensor, c); + cam->bright = c->value; + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + status = -ENODEV; + } + break; + case V4L2_CID_HUE: + if (cam->sensor) { + c->value = cam->hue; + status = vidioc_int_g_ctrl(cam->sensor, c); + cam->hue = c->value; + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + status = -ENODEV; + } + break; + case V4L2_CID_CONTRAST: + if (cam->sensor) { + c->value = cam->contrast; + status = vidioc_int_g_ctrl(cam->sensor, c); + cam->contrast = c->value; + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + status = -ENODEV; + } + break; + case V4L2_CID_SATURATION: + if (cam->sensor) { + c->value = cam->saturation; + status = vidioc_int_g_ctrl(cam->sensor, c); + cam->saturation = c->value; + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + status = -ENODEV; + } + break; + case V4L2_CID_RED_BALANCE: + if (cam->sensor) { + c->value = cam->red; + status = vidioc_int_g_ctrl(cam->sensor, c); + cam->red = c->value; + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + status = -ENODEV; + } + break; + case V4L2_CID_BLUE_BALANCE: + if (cam->sensor) { + c->value = cam->blue; + status = vidioc_int_g_ctrl(cam->sensor, c); + cam->blue = c->value; + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + status = -ENODEV; + } + break; + case V4L2_CID_BLACK_LEVEL: + if (cam->sensor) { + c->value = cam->ae_mode; + status = vidioc_int_g_ctrl(cam->sensor, c); + cam->ae_mode = c->value; + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + status = -ENODEV; + } + break; + default: + pr_err("ERROR: v4l2 capture: unsupported ioctrl!\n"); + } + + return status; +} + +/*! + * V4L2 - set_control function + * V4L2_CID_PRIVATE_BASE is the extention for IPU preprocessing. + * 0 for normal operation + * 1 for vertical flip + * 2 for horizontal flip + * 3 for horizontal and vertical flip + * 4 for 90 degree rotation + * @param cam structure cam_data * + * + * @param c structure v4l2_control * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c) +{ + int i, ret = 0; + int tmp_rotation = IPU_ROTATE_NONE; + struct sensor_data *sensor_data; + + pr_debug("In MVC:mxc_v4l2_s_ctrl\n"); + + switch (c->id) { + case V4L2_CID_HFLIP: + /* This is done by the IPU */ + if (c->value == 1) { + if ((cam->rotation != IPU_ROTATE_VERT_FLIP) && + (cam->rotation != IPU_ROTATE_180)) + cam->rotation = IPU_ROTATE_HORIZ_FLIP; + else + cam->rotation = IPU_ROTATE_180; + } else { + if (cam->rotation == IPU_ROTATE_HORIZ_FLIP) + cam->rotation = IPU_ROTATE_NONE; + if (cam->rotation == IPU_ROTATE_180) + cam->rotation = IPU_ROTATE_VERT_FLIP; + } + break; + case V4L2_CID_VFLIP: + /* This is done by the IPU */ + if (c->value == 1) { + if ((cam->rotation != IPU_ROTATE_HORIZ_FLIP) && + (cam->rotation != IPU_ROTATE_180)) + cam->rotation = IPU_ROTATE_VERT_FLIP; + else + cam->rotation = IPU_ROTATE_180; + } else { + if (cam->rotation == IPU_ROTATE_VERT_FLIP) + cam->rotation = IPU_ROTATE_NONE; + if (cam->rotation == IPU_ROTATE_180) + cam->rotation = IPU_ROTATE_HORIZ_FLIP; + } + break; + case V4L2_CID_MXC_ROT: + case V4L2_CID_MXC_VF_ROT: + /* This is done by the IPU */ + switch (c->value) { + case V4L2_MXC_ROTATE_NONE: + tmp_rotation = IPU_ROTATE_NONE; + break; + case V4L2_MXC_ROTATE_VERT_FLIP: + tmp_rotation = IPU_ROTATE_VERT_FLIP; + break; + case V4L2_MXC_ROTATE_HORIZ_FLIP: + tmp_rotation = IPU_ROTATE_HORIZ_FLIP; + break; + case V4L2_MXC_ROTATE_180: + tmp_rotation = IPU_ROTATE_180; + break; + case V4L2_MXC_ROTATE_90_RIGHT: + tmp_rotation = IPU_ROTATE_90_RIGHT; + break; + case V4L2_MXC_ROTATE_90_RIGHT_VFLIP: + tmp_rotation = IPU_ROTATE_90_RIGHT_VFLIP; + break; + case V4L2_MXC_ROTATE_90_RIGHT_HFLIP: + tmp_rotation = IPU_ROTATE_90_RIGHT_HFLIP; + break; + case V4L2_MXC_ROTATE_90_LEFT: + tmp_rotation = IPU_ROTATE_90_LEFT; + break; + default: + ret = -EINVAL; + } + #ifdef CONFIG_MXC_IPU_PRP_VF_SDC + if (c->id == V4L2_CID_MXC_VF_ROT) + cam->vf_rotation = tmp_rotation; + else + cam->rotation = tmp_rotation; + #else + cam->rotation = tmp_rotation; + #endif + + break; + case V4L2_CID_HUE: + if (cam->sensor) { + cam->hue = c->value; + ret = vidioc_int_s_ctrl(cam->sensor, c); + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + ret = -ENODEV; + } + break; + case V4L2_CID_CONTRAST: + if (cam->sensor) { + cam->contrast = c->value; + ret = vidioc_int_s_ctrl(cam->sensor, c); + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + ret = -ENODEV; + } + break; + case V4L2_CID_BRIGHTNESS: + if (cam->sensor) { + cam->bright = c->value; + ret = vidioc_int_s_ctrl(cam->sensor, c); + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + ret = -ENODEV; + } + break; + case V4L2_CID_SATURATION: + if (cam->sensor) { + cam->saturation = c->value; + ret = vidioc_int_s_ctrl(cam->sensor, c); + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + ret = -ENODEV; + } + break; + case V4L2_CID_RED_BALANCE: + if (cam->sensor) { + cam->red = c->value; + ret = vidioc_int_s_ctrl(cam->sensor, c); + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + ret = -ENODEV; + } + break; + case V4L2_CID_BLUE_BALANCE: + if (cam->sensor) { + cam->blue = c->value; + ret = vidioc_int_s_ctrl(cam->sensor, c); + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + ret = -ENODEV; + } + break; + case V4L2_CID_EXPOSURE: + if (cam->sensor) { + cam->ae_mode = c->value; + ret = vidioc_int_s_ctrl(cam->sensor, c); + } else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + ret = -ENODEV; + } + break; + case V4L2_CID_MXC_FLASH: +#ifdef CONFIG_MXC_IPU_V1 + ipu_csi_flash_strobe(true); +#endif + break; + case V4L2_CID_MXC_SWITCH_CAM: + if (cam->sensor == cam->all_sensors[c->value]) + break; + + /* power down other cameraes before enable new one */ + for (i = 0; i < cam->sensor_index; i++) { + if (i != c->value) { + vidioc_int_dev_exit(cam->all_sensors[i]); + vidioc_int_s_power(cam->all_sensors[i], 0); + if (cam->mclk_on[cam->mclk_source]) { + ipu_csi_enable_mclk_if(cam->ipu, + CSI_MCLK_I2C, + cam->mclk_source, + false, false); + cam->mclk_on[cam->mclk_source] = + false; + } + } + } + sensor_data = cam->all_sensors[c->value]->priv; + if (sensor_data->io_init) + sensor_data->io_init(); + cam->sensor = cam->all_sensors[c->value]; + cam->mclk_source = sensor_data->mclk_source; + ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, + cam->mclk_source, true, true); + cam->mclk_on[cam->mclk_source] = true; + vidioc_int_s_power(cam->sensor, 1); + vidioc_int_dev_init(cam->sensor); + break; + default: + pr_debug(" default case\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * V4L2 - mxc_v4l2_s_param function + * Allows setting of capturemode and frame rate. + * + * @param cam structure cam_data * + * @param parm structure v4l2_streamparm * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) +{ + struct v4l2_ifparm ifparm; + struct v4l2_format cam_fmt; + struct v4l2_streamparm currentparm; + ipu_csi_signal_cfg_t csi_param; + u32 current_fps, parm_fps; + int err = 0; + + pr_debug("In mxc_v4l2_s_param\n"); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n"); + return -EINVAL; + } + + /* Stop the viewfinder */ + if (cam->overlay_on == true) + stop_preview(cam); + + currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* First check that this device can support the changes requested. */ + err = vidioc_int_g_parm(cam->sensor, ¤tparm); + if (err) { + pr_err("%s: vidioc_int_g_parm returned an error %d\n", + __func__, err); + goto exit; + } + + current_fps = currentparm.parm.capture.timeperframe.denominator + / currentparm.parm.capture.timeperframe.numerator; + parm_fps = parm->parm.capture.timeperframe.denominator + / parm->parm.capture.timeperframe.numerator; + + pr_debug(" Current capabilities are %x\n", + currentparm.parm.capture.capability); + pr_debug(" Current capturemode is %d change to %d\n", + currentparm.parm.capture.capturemode, + parm->parm.capture.capturemode); + pr_debug(" Current framerate is %d change to %d\n", + current_fps, parm_fps); + + /* This will change any camera settings needed. */ + err = vidioc_int_s_parm(cam->sensor, parm); + if (err) { + pr_err("%s: vidioc_int_s_parm returned an error %d\n", + __func__, err); + goto exit; + } + + /* If resolution changed, need to re-program the CSI */ + /* Get new values. */ + vidioc_int_g_ifparm(cam->sensor, &ifparm); + + csi_param.data_width = 0; + csi_param.clk_mode = 0; + csi_param.ext_vsync = 0; + csi_param.Vsync_pol = 0; + csi_param.Hsync_pol = 0; + csi_param.pixclk_pol = 0; + csi_param.data_pol = 0; + csi_param.sens_clksrc = 0; + csi_param.pack_tight = 0; + csi_param.force_eof = 0; + csi_param.data_en_pol = 0; + csi_param.data_fmt = 0; + csi_param.csi = cam->csi; + csi_param.mclk = 0; + + /*This may not work on other platforms. Check when adding a new one.*/ + /*The mclk clock was never set correclty in the ipu register*/ + /*for now we are going to use this mclk as pixel clock*/ + /*to set csi0_data_dest register.*/ + /*This is a workaround which should be fixed*/ + pr_debug(" clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr); + if (ifparm.u.bt656.clock_curr == 0) { + csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; + /*protocol bt656 use 27Mhz pixel clock */ + csi_param.mclk = 27000000; + } else { + csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; + } + + csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; + + if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) { + csi_param.data_width = IPU_CSI_DATA_WIDTH_8; + } else if (ifparm.u.bt656.mode + == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) { + csi_param.data_width = IPU_CSI_DATA_WIDTH_10; + } else { + csi_param.data_width = IPU_CSI_DATA_WIDTH_8; + } + + csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; + csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; + csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct; + + /* if the capturemode changed, the size bounds will have changed. */ + cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); + pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n", + cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); + + csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; + + cam->crop_bounds.top = cam->crop_bounds.left = 0; + cam->crop_bounds.width = cam_fmt.fmt.pix.width; + cam->crop_bounds.height = cam_fmt.fmt.pix.height; + + /* + * Set the default current cropped resolution to be the same with + * the cropping boundary(except for tvin module). + */ + if (cam->device_type != 1) { + cam->crop_current.width = cam->crop_bounds.width; + cam->crop_current.height = cam->crop_bounds.height; + } + + /* This essentially loses the data at the left and bottom of the image + * giving a digital zoom image, if crop_current is less than the full + * size of the image. */ + ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, + cam->crop_current.height, cam->csi); + ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, + cam->crop_current.top, + cam->csi); + ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width, + cam->crop_bounds.height, + cam_fmt.fmt.pix.pixelformat, csi_param); + + +exit: + if (cam->overlay_on == true) + start_preview(cam); + + return err; +} + +/*! + * V4L2 - mxc_v4l2_s_std function + * + * Sets the TV standard to be used. + * + * @param cam structure cam_data * + * @param parm structure v4l2_streamparm * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_s_std(cam_data *cam, v4l2_std_id e) +{ + printk(KERN_ERR "In mxc_v4l2_s_std %Lx\n", e); + if (e == V4L2_STD_PAL) { + pr_debug(" Setting standard to PAL %Lx\n", V4L2_STD_PAL); + cam->standard.id = V4L2_STD_PAL; + video_index = TV_PAL; + } else if (e == V4L2_STD_NTSC) { + pr_debug(" Setting standard to NTSC %Lx\n", + V4L2_STD_NTSC); + /* Get rid of the white dot line in NTSC signal input */ + cam->standard.id = V4L2_STD_NTSC; + video_index = TV_NTSC; + } else { + cam->standard.id = V4L2_STD_ALL; + video_index = TV_NOT_LOCKED; + pr_err("ERROR: unrecognized std! %Lx (PAL=%Lx, NTSC=%Lx\n", + e, V4L2_STD_PAL, V4L2_STD_NTSC); + } + + cam->standard.index = video_index; + strcpy(cam->standard.name, video_fmts[video_index].name); + cam->crop_bounds.width = video_fmts[video_index].raw_width; + cam->crop_bounds.height = video_fmts[video_index].raw_height; + cam->crop_current.width = video_fmts[video_index].active_width; + cam->crop_current.height = video_fmts[video_index].active_height; + cam->crop_current.top = video_fmts[video_index].active_top; + cam->crop_current.left = video_fmts[video_index].active_left; + + return 0; +} + +/*! + * V4L2 - mxc_v4l2_g_std function + * + * Gets the TV standard from the TV input device. + * + * @param cam structure cam_data * + * + * @param e structure v4l2_streamparm * + * + * @return status 0 success, EINVAL failed + */ +static int mxc_v4l2_g_std(cam_data *cam, v4l2_std_id *e) +{ + struct v4l2_format tv_fmt; + + pr_debug("In mxc_v4l2_g_std\n"); + + if (cam->device_type == 1) { + /* Use this function to get what the TV-In device detects the + * format to be. pixelformat is used to return the std value + * since the interface has no vidioc_g_std.*/ + tv_fmt.type = V4L2_BUF_TYPE_PRIVATE; + vidioc_int_g_fmt_cap(cam->sensor, &tv_fmt); + + /* If the TV-in automatically detects the standard, then if it + * changes, the settings need to change. */ + if (cam->standard_autodetect) { + if (cam->standard.id != tv_fmt.fmt.pix.pixelformat) { + pr_debug("MVC: mxc_v4l2_g_std: " + "Changing standard\n"); + mxc_v4l2_s_std(cam, tv_fmt.fmt.pix.pixelformat); + } + } + + *e = tv_fmt.fmt.pix.pixelformat; + } + + return 0; +} + +/*! + * Dequeue one V4L capture buffer + * + * @param cam structure cam_data * + * @param buf structure v4l2_buffer * + * + * @return status 0 success, EINVAL invalid frame number, + * ETIME timeout, ERESTARTSYS interrupted by user + */ +static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) +{ + int retval = 0; + struct mxc_v4l_frame *frame; + unsigned long lock_flags; + + pr_debug("In MVC:mxc_v4l_dqueue\n"); + + if (!wait_event_interruptible_timeout(cam->enc_queue, + cam->enc_counter != 0, 10 * HZ)) { + pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout " + "enc_counter %x\n", + cam->enc_counter); + return -ETIME; + } else if (signal_pending(current)) { + pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() " + "interrupt received\n"); + return -ERESTARTSYS; + } + + if (down_interruptible(&cam->busy_lock)) + return -EBUSY; + + spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags); + cam->enc_counter--; + + frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue); + list_del(cam->done_q.next); + if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) { + frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE; + } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { + pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: " + "Buffer not filled.\n"); + frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; + retval = -EINVAL; + } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) { + pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: " + "Buffer not queued.\n"); + retval = -EINVAL; + } + + cam->frame[frame->index].buffer.field = cam->device_type ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; + + buf->bytesused = cam->v2f.fmt.pix.sizeimage; + buf->index = frame->index; + buf->flags = frame->buffer.flags; + buf->m = cam->frame[frame->index].buffer.m; + buf->timestamp = cam->frame[frame->index].buffer.timestamp; + buf->field = cam->frame[frame->index].buffer.field; + spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags); + + up(&cam->busy_lock); + return retval; +} + +/*! + * V4L interface - open function + * + * @param file structure file * + * + * @return status 0 success, ENODEV invalid device instance, + * ENODEV timeout, ERESTARTSYS interrupted by user + */ +static int mxc_v4l_open(struct file *file) +{ + struct v4l2_ifparm ifparm; + struct v4l2_format cam_fmt; + ipu_csi_signal_cfg_t csi_param; + struct video_device *dev = video_devdata(file); + cam_data *cam = video_get_drvdata(dev); + int err = 0; + struct sensor_data *sensor; + + pr_debug("\nIn MVC: mxc_v4l_open\n"); + pr_debug(" device name is %s\n", dev->name); + + if (!cam) { + pr_err("ERROR: v4l2 capture: Internal error, " + "cam_data not found!\n"); + return -EBADF; + } + + if (cam->sensor == NULL || + cam->sensor->type != v4l2_int_type_slave) { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + return -EAGAIN; + } + + sensor = cam->sensor->priv; + if (!sensor) { + pr_err("%s: Internal error, sensor_data is not found!\n", __func__); + return -EBADF; + } + + down(&cam->busy_lock); + err = 0; + if (signal_pending(current)) + goto oops; + + if (cam->open_count++ == 0) { + wait_event_interruptible(cam->power_queue, + cam->low_power == false); + + if (strcmp(mxc_capture_inputs[cam->current_input].name, + "CSI MEM") == 0) { +#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) + err = csi_enc_select(cam); +#endif + } else if (strcmp(mxc_capture_inputs[cam->current_input].name, + "CSI IC MEM") == 0) { +#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) + err = prp_enc_select(cam); +#endif + } + + cam->enc_counter = 0; + INIT_LIST_HEAD(&cam->ready_q); + INIT_LIST_HEAD(&cam->working_q); + INIT_LIST_HEAD(&cam->done_q); + + vidioc_int_g_ifparm(cam->sensor, &ifparm); + + csi_param.sens_clksrc = 0; + + csi_param.clk_mode = 0; + csi_param.data_pol = 0; + csi_param.ext_vsync = 0; + + csi_param.pack_tight = 0; + csi_param.force_eof = 0; + csi_param.data_en_pol = 0; + + csi_param.mclk = ifparm.u.bt656.clock_curr; + + csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; + + if (ifparm.u.bt656.mode + == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) + csi_param.data_width = IPU_CSI_DATA_WIDTH_8; + else if (ifparm.u.bt656.mode + == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) + csi_param.data_width = IPU_CSI_DATA_WIDTH_10; + else + csi_param.data_width = IPU_CSI_DATA_WIDTH_8; + + + csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; + csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; + + csi_param.csi = cam->csi; + + cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); + + /* Reset the sizes. Needed to prevent carryover of last + * operation.*/ + cam->crop_bounds.top = cam->crop_bounds.left = 0; + cam->crop_bounds.width = cam_fmt.fmt.pix.width; + cam->crop_bounds.height = cam_fmt.fmt.pix.height; + + /* This also is the max crop size for this device. */ + cam->crop_defrect.top = cam->crop_defrect.left = 0; + cam->crop_defrect.width = cam_fmt.fmt.pix.width; + cam->crop_defrect.height = cam_fmt.fmt.pix.height; + + /* At this point, this is also the current image size. */ + cam->crop_current.top = cam->crop_current.left = 0; + cam->crop_current.width = cam_fmt.fmt.pix.width; + cam->crop_current.height = cam_fmt.fmt.pix.height; + + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", + __func__, + cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", + __func__, + cam->crop_bounds.width, cam->crop_bounds.height); + pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", + __func__, + cam->crop_defrect.width, cam->crop_defrect.height); + pr_debug("End of %s: crop_current widthxheight %d x %d\n", + __func__, + cam->crop_current.width, cam->crop_current.height); + + csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; + pr_debug("On Open: Input to ipu size is %d x %d\n", + cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); + ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, + cam->crop_current.height, + cam->csi); + ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, + cam->crop_current.top, + cam->csi); + ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width, + cam->crop_bounds.height, + cam_fmt.fmt.pix.pixelformat, + csi_param); + clk_prepare_enable(sensor->sensor_clk); + vidioc_int_s_power(cam->sensor, 1); + vidioc_int_init(cam->sensor); + vidioc_int_dev_init(cam->sensor); + } + + file->private_data = dev; + +oops: + up(&cam->busy_lock); + return err; +} + +/*! + * V4L interface - close function + * + * @param file struct file * + * + * @return 0 success + */ +static int mxc_v4l_close(struct file *file) +{ + struct video_device *dev = video_devdata(file); + int err = 0; + cam_data *cam = video_get_drvdata(dev); + struct sensor_data *sensor; + pr_debug("In MVC:mxc_v4l_close\n"); + + if (!cam) { + pr_err("ERROR: v4l2 capture: Internal error, " + "cam_data not found!\n"); + return -EBADF; + } + + if (!cam->sensor) { + pr_err("%s: Internal error, camera is not found!\n", __func__); + return -EBADF; + } + + sensor = cam->sensor->priv; + if (!sensor) { + pr_err("%s: Internal error, sensor_data is not found!\n", __func__); + return -EBADF; + } + + down(&cam->busy_lock); + + /* for the case somebody hit the ctrl C */ + if (cam->overlay_pid == current->pid && cam->overlay_on) { + err = stop_preview(cam); + cam->overlay_on = false; + } + if (cam->capture_pid == current->pid) { + err |= mxc_streamoff(cam); + wake_up_interruptible(&cam->enc_queue); + } + + if (--cam->open_count == 0) { + vidioc_int_s_power(cam->sensor, 0); + clk_disable_unprepare(sensor->sensor_clk); + wait_event_interruptible(cam->power_queue, + cam->low_power == false); + pr_debug("mxc_v4l_close: release resource\n"); + + if (strcmp(mxc_capture_inputs[cam->current_input].name, + "CSI MEM") == 0) { +#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) + err |= csi_enc_deselect(cam); +#endif + } else if (strcmp(mxc_capture_inputs[cam->current_input].name, + "CSI IC MEM") == 0) { +#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) + err |= prp_enc_deselect(cam); +#endif + } + + mxc_free_frame_buf(cam); + file->private_data = NULL; + + /* capture off */ + wake_up_interruptible(&cam->enc_queue); + mxc_free_frames(cam); + cam->enc_counter++; + } + + up(&cam->busy_lock); + + return err; +} + +#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC) || \ + defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) || \ + defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) +/* + * V4L interface - read function + * + * @param file struct file * + * @param read buf char * + * @param count size_t + * @param ppos structure loff_t * + * + * @return bytes read + */ +static ssize_t mxc_v4l_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int err = 0; + u8 *v_address[2]; + struct video_device *dev = video_devdata(file); + cam_data *cam = video_get_drvdata(dev); + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + /* Stop the viewfinder */ + if (cam->overlay_on == true) + stop_preview(cam); + + v_address[0] = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->still_buf[0], + GFP_DMA | GFP_KERNEL); + + v_address[1] = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->still_buf[1], + GFP_DMA | GFP_KERNEL); + + if (!v_address[0] || !v_address[1]) { + err = -ENOBUFS; + goto exit0; + } + + err = prp_still_select(cam); + if (err != 0) { + err = -EIO; + goto exit0; + } + + cam->still_counter = 0; + err = cam->csi_start(cam); + if (err != 0) { + err = -EIO; + goto exit1; + } + + if (!wait_event_interruptible_timeout(cam->still_queue, + cam->still_counter != 0, + 10 * HZ)) { + pr_err("ERROR: v4l2 capture: mxc_v4l_read timeout counter %x\n", + cam->still_counter); + err = -ETIME; + goto exit1; + } + err = copy_to_user(buf, v_address[1], cam->v2f.fmt.pix.sizeimage); + +exit1: + prp_still_deselect(cam); + +exit0: + if (v_address[0] != 0) + dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address[0], + cam->still_buf[0]); + if (v_address[1] != 0) + dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address[1], + cam->still_buf[1]); + + cam->still_buf[0] = cam->still_buf[1] = 0; + + if (cam->overlay_on == true) + start_preview(cam); + + up(&cam->busy_lock); + if (err < 0) + return err; + + return cam->v2f.fmt.pix.sizeimage - err; +} +#endif + +/*! + * V4L interface - ioctl function + * + * @param file struct file* + * + * @param ioctlnr unsigned int + * + * @param arg void* + * + * @return 0 success, ENODEV for invalid device instance, + * -1 for other errors. + */ +static long mxc_v4l_do_ioctl(struct file *file, + unsigned int ioctlnr, void *arg) +{ + struct video_device *dev = video_devdata(file); + cam_data *cam = video_get_drvdata(dev); + int retval = 0; + unsigned long lock_flags; + + pr_debug("In MVC: mxc_v4l_do_ioctl %x\n", ioctlnr); + wait_event_interruptible(cam->power_queue, cam->low_power == false); + /* make this _really_ smp-safe */ + if (ioctlnr != VIDIOC_DQBUF) + if (down_interruptible(&cam->busy_lock)) + return -EBUSY; + + switch (ioctlnr) { + /*! + * V4l2 VIDIOC_QUERYCAP ioctl + */ + case VIDIOC_QUERYCAP: { + struct v4l2_capability *cap = arg; + pr_debug(" case VIDIOC_QUERYCAP\n"); + strcpy(cap->driver, "mxc_v4l2"); + cap->version = KERNEL_VERSION(0, 1, 11); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->card[0] = '\0'; + cap->bus_info[0] = '\0'; + break; + } + + /*! + * V4l2 VIDIOC_G_FMT ioctl + */ + case VIDIOC_G_FMT: { + struct v4l2_format *gf = arg; + pr_debug(" case VIDIOC_G_FMT\n"); + retval = mxc_v4l2_g_fmt(cam, gf); + break; + } + + /*! + * V4l2 VIDIOC_S_FMT ioctl + */ + case VIDIOC_S_FMT: { + struct v4l2_format *sf = arg; + pr_debug(" case VIDIOC_S_FMT\n"); + retval = mxc_v4l2_s_fmt(cam, sf); + break; + } + + /*! + * V4l2 VIDIOC_REQBUFS ioctl + */ + case VIDIOC_REQBUFS: { + struct v4l2_requestbuffers *req = arg; + pr_debug(" case VIDIOC_REQBUFS\n"); + + if (req->count > FRAME_NUM) { + pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: " + "not enough buffers\n"); + req->count = FRAME_NUM; + } + + if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: " + "wrong buffer type\n"); + retval = -EINVAL; + break; + } + + mxc_streamoff(cam); + if (req->memory & V4L2_MEMORY_MMAP) { + mxc_free_frame_buf(cam); + retval = mxc_allocate_frame_buf(cam, req->count); + } + break; + } + + /*! + * V4l2 VIDIOC_QUERYBUF ioctl + */ + case VIDIOC_QUERYBUF: { + struct v4l2_buffer *buf = arg; + int index = buf->index; + pr_debug(" case VIDIOC_QUERYBUF\n"); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pr_err("ERROR: v4l2 capture: " + "VIDIOC_QUERYBUFS: " + "wrong buffer type\n"); + retval = -EINVAL; + break; + } + + if (buf->memory & V4L2_MEMORY_MMAP) { + memset(buf, 0, sizeof(buf)); + buf->index = index; + } + + down(&cam->param_lock); + if (buf->memory & V4L2_MEMORY_USERPTR) { + mxc_v4l2_release_bufs(cam); + retval = mxc_v4l2_prepare_bufs(cam, buf); + } + + if (buf->memory & V4L2_MEMORY_MMAP) + retval = mxc_v4l2_buffer_status(cam, buf); + up(&cam->param_lock); + break; + } + + /*! + * V4l2 VIDIOC_QBUF ioctl + */ + case VIDIOC_QBUF: { + struct v4l2_buffer *buf = arg; + int index = buf->index; + pr_debug(" case VIDIOC_QBUF\n"); + + spin_lock_irqsave(&cam->queue_int_lock, lock_flags); + if ((cam->frame[index].buffer.flags & 0x7) == + V4L2_BUF_FLAG_MAPPED) { + cam->frame[index].buffer.flags |= + V4L2_BUF_FLAG_QUEUED; + list_add_tail(&cam->frame[index].queue, + &cam->ready_q); + } else if (cam->frame[index].buffer. + flags & V4L2_BUF_FLAG_QUEUED) { + pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: " + "buffer already queued\n"); + retval = -EINVAL; + } else if (cam->frame[index].buffer. + flags & V4L2_BUF_FLAG_DONE) { + pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: " + "overwrite done buffer.\n"); + cam->frame[index].buffer.flags &= + ~V4L2_BUF_FLAG_DONE; + cam->frame[index].buffer.flags |= + V4L2_BUF_FLAG_QUEUED; + retval = -EINVAL; + } + + buf->flags = cam->frame[index].buffer.flags; + spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags); + break; + } + + /*! + * V4l2 VIDIOC_DQBUF ioctl + */ + case VIDIOC_DQBUF: { + struct v4l2_buffer *buf = arg; + pr_debug(" case VIDIOC_DQBUF\n"); + + if ((cam->enc_counter == 0) && + (file->f_flags & O_NONBLOCK)) { + retval = -EAGAIN; + break; + } + + retval = mxc_v4l_dqueue(cam, buf); + break; + } + + /*! + * V4l2 VIDIOC_STREAMON ioctl + */ + case VIDIOC_STREAMON: { + pr_debug(" case VIDIOC_STREAMON\n"); + retval = mxc_streamon(cam); + break; + } + + /*! + * V4l2 VIDIOC_STREAMOFF ioctl + */ + case VIDIOC_STREAMOFF: { + pr_debug(" case VIDIOC_STREAMOFF\n"); + retval = mxc_streamoff(cam); + break; + } + + /*! + * V4l2 VIDIOC_G_CTRL ioctl + */ + case VIDIOC_G_CTRL: { + pr_debug(" case VIDIOC_G_CTRL\n"); + retval = mxc_v4l2_g_ctrl(cam, arg); + break; + } + + /*! + * V4l2 VIDIOC_S_CTRL ioctl + */ + case VIDIOC_S_CTRL: { + pr_debug(" case VIDIOC_S_CTRL\n"); + retval = mxc_v4l2_s_ctrl(cam, arg); + break; + } + + /*! + * V4l2 VIDIOC_CROPCAP ioctl + */ + case VIDIOC_CROPCAP: { + struct v4l2_cropcap *cap = arg; + pr_debug(" case VIDIOC_CROPCAP\n"); + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + cap->bounds = cam->crop_bounds; + cap->defrect = cam->crop_defrect; + break; + } + + /*! + * V4l2 VIDIOC_G_CROP ioctl + */ + case VIDIOC_G_CROP: { + struct v4l2_crop *crop = arg; + pr_debug(" case VIDIOC_G_CROP\n"); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + crop->c = cam->crop_current; + break; + } + + /*! + * V4l2 VIDIOC_S_CROP ioctl + */ + case VIDIOC_S_CROP: { + struct v4l2_crop *crop = arg; + struct v4l2_rect *b = &cam->crop_bounds; + pr_debug(" case VIDIOC_S_CROP\n"); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + retval = -EINVAL; + break; + } + + crop->c.top = (crop->c.top < b->top) ? b->top + : crop->c.top; + if (crop->c.top > b->top + b->height) + crop->c.top = b->top + b->height - 1; + if (crop->c.height > b->top + b->height - crop->c.top) + crop->c.height = + b->top + b->height - crop->c.top; + + crop->c.left = (crop->c.left < b->left) ? b->left + : crop->c.left; + if (crop->c.left > b->left + b->width) + crop->c.left = b->left + b->width - 1; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = + b->left - crop->c.left + b->width; + + crop->c.width -= crop->c.width % 8; + crop->c.left -= crop->c.left % 4; + cam->crop_current = crop->c; + + pr_debug(" Cropping Input to ipu size %d x %d\n", + cam->crop_current.width, + cam->crop_current.height); + ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, + cam->crop_current.height, + cam->csi); + ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, + cam->crop_current.top, + cam->csi); + break; + } + + /*! + * V4l2 VIDIOC_OVERLAY ioctl + */ + case VIDIOC_OVERLAY: { + int *on = arg; + pr_debug(" VIDIOC_OVERLAY on=%d\n", *on); + if (*on) { + cam->overlay_on = true; + cam->overlay_pid = current->pid; + retval = start_preview(cam); + } + if (!*on) { + retval = stop_preview(cam); + cam->overlay_on = false; + } + break; + } + + /*! + * V4l2 VIDIOC_G_FBUF ioctl + */ + case VIDIOC_G_FBUF: { + struct v4l2_framebuffer *fb = arg; + pr_debug(" case VIDIOC_G_FBUF\n"); + *fb = cam->v4l2_fb; + fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY; + break; + } + + /*! + * V4l2 VIDIOC_S_FBUF ioctl + */ + case VIDIOC_S_FBUF: { + struct v4l2_framebuffer *fb = arg; + pr_debug(" case VIDIOC_S_FBUF\n"); + cam->v4l2_fb = *fb; + break; + } + + case VIDIOC_G_PARM: { + struct v4l2_streamparm *parm = arg; + pr_debug(" case VIDIOC_G_PARM\n"); + if (cam->sensor) + retval = vidioc_int_g_parm(cam->sensor, parm); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + + case VIDIOC_S_PARM: { + struct v4l2_streamparm *parm = arg; + pr_debug(" case VIDIOC_S_PARM\n"); + if (cam->sensor) + retval = mxc_v4l2_s_param(cam, parm); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + + /* linux v4l2 bug, kernel c0485619 user c0405619 */ + case VIDIOC_ENUMSTD: { + struct v4l2_standard *e = arg; + pr_debug(" case VIDIOC_ENUMSTD\n"); + *e = cam->standard; + break; + } + + case VIDIOC_G_STD: { + v4l2_std_id *e = arg; + pr_debug(" case VIDIOC_G_STD\n"); + if (cam->sensor) + retval = mxc_v4l2_g_std(cam, e); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + + case VIDIOC_S_STD: { + v4l2_std_id *e = arg; + pr_debug(" case VIDIOC_S_STD\n"); + retval = mxc_v4l2_s_std(cam, *e); + + break; + } + + case VIDIOC_ENUMOUTPUT: { + struct v4l2_output *output = arg; + pr_debug(" case VIDIOC_ENUMOUTPUT\n"); + if (output->index >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) { + retval = -EINVAL; + break; + } + *output = mxc_capture_outputs[output->index]; + + break; + } + case VIDIOC_G_OUTPUT: { + int *p_output_num = arg; + pr_debug(" case VIDIOC_G_OUTPUT\n"); + *p_output_num = cam->output; + break; + } + + case VIDIOC_S_OUTPUT: { + int *p_output_num = arg; + pr_debug(" case VIDIOC_S_OUTPUT\n"); + if (*p_output_num >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) { + retval = -EINVAL; + break; + } + cam->output = *p_output_num; + break; + } + + case VIDIOC_ENUMINPUT: { + struct v4l2_input *input = arg; + pr_debug(" case VIDIOC_ENUMINPUT\n"); + if (input->index >= MXC_V4L2_CAPTURE_NUM_INPUTS) { + retval = -EINVAL; + break; + } + *input = mxc_capture_inputs[input->index]; + break; + } + + case VIDIOC_G_INPUT: { + int *index = arg; + pr_debug(" case VIDIOC_G_INPUT\n"); + *index = cam->current_input; + break; + } + + case VIDIOC_S_INPUT: { + int *index = arg; + pr_debug(" case VIDIOC_S_INPUT\n"); + if (*index >= MXC_V4L2_CAPTURE_NUM_INPUTS) { + retval = -EINVAL; + break; + } + + if (*index == cam->current_input) + break; + + if ((mxc_capture_inputs[cam->current_input].status & + V4L2_IN_ST_NO_POWER) == 0) { + retval = mxc_streamoff(cam); + if (retval) + break; + mxc_capture_inputs[cam->current_input].status |= + V4L2_IN_ST_NO_POWER; + } + + if (strcmp(mxc_capture_inputs[*index].name, "CSI MEM") == 0) { +#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE) + retval = csi_enc_select(cam); + if (retval) + break; +#endif + } else if (strcmp(mxc_capture_inputs[*index].name, + "CSI IC MEM") == 0) { +#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) + retval = prp_enc_select(cam); + if (retval) + break; +#endif + } + + mxc_capture_inputs[*index].status &= ~V4L2_IN_ST_NO_POWER; + cam->current_input = *index; + break; + } + case VIDIOC_ENUM_FMT: { + struct v4l2_fmtdesc *f = arg; + if (cam->sensor) + retval = vidioc_int_enum_fmt_cap(cam->sensor, f); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + case VIDIOC_ENUM_FRAMESIZES: { + struct v4l2_frmsizeenum *fsize = arg; + if (cam->sensor) + retval = vidioc_int_enum_framesizes(cam->sensor, fsize); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + case VIDIOC_DBG_G_CHIP_IDENT: { + struct v4l2_dbg_chip_ident *p = arg; + p->ident = V4L2_IDENT_NONE; + p->revision = 0; + if (cam->sensor) + retval = vidioc_int_g_chip_ident(cam->sensor, (int *)p); + else { + pr_err("ERROR: v4l2 capture: slave not found!\n"); + retval = -ENODEV; + } + break; + } + case VIDIOC_TRY_FMT: + case VIDIOC_QUERYCTRL: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + default: + pr_debug(" case default or not supported\n"); + retval = -EINVAL; + break; + } + + if (ioctlnr != VIDIOC_DQBUF) + up(&cam->busy_lock); + return retval; +} + +/* + * V4L interface - ioctl function + * + * @return None + */ +static long mxc_v4l_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + pr_debug("In MVC:mxc_v4l_ioctl\n"); + return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl); +} + +/*! + * V4L interface - mmap function + * + * @param file structure file * + * + * @param vma structure vm_area_struct * + * + * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error + */ +static int mxc_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = video_devdata(file); + unsigned long size; + int res = 0; + cam_data *cam = video_get_drvdata(dev); + + pr_debug("In MVC:mxc_mmap\n"); + pr_debug(" pgoff=0x%lx, start=0x%lx, end=0x%lx\n", + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + /* make this _really_ smp-safe */ + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + size = vma->vm_end - vma->vm_start; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, + vma->vm_pgoff, size, vma->vm_page_prot)) { + pr_err("ERROR: v4l2 capture: mxc_mmap: " + "remap_pfn_range failed\n"); + res = -ENOBUFS; + goto mxc_mmap_exit; + } + + vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ + +mxc_mmap_exit: + up(&cam->busy_lock); + return res; +} + +/*! + * V4L interface - poll function + * + * @param file structure file * + * + * @param wait structure poll_table_struct * + * + * @return status POLLIN | POLLRDNORM + */ +static unsigned int mxc_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *dev = video_devdata(file); + cam_data *cam = video_get_drvdata(dev); + wait_queue_head_t *queue = NULL; + int res = POLLIN | POLLRDNORM; + + pr_debug("In MVC:mxc_poll\n"); + + if (down_interruptible(&cam->busy_lock)) + return -EINTR; + + queue = &cam->enc_queue; + poll_wait(file, queue, wait); + + up(&cam->busy_lock); + + return res; +} + +/*! + * This structure defines the functions to be called in this driver. + */ +static struct v4l2_file_operations mxc_v4l_fops = { + .owner = THIS_MODULE, + .open = mxc_v4l_open, + .release = mxc_v4l_close, + .read = mxc_v4l_read, + .ioctl = mxc_v4l_ioctl, + .mmap = mxc_mmap, + .poll = mxc_poll, +}; + +static struct video_device mxc_v4l_template = { + .name = "Mxc Camera", + .fops = &mxc_v4l_fops, + .release = video_device_release, +}; + +/*! + * This function can be used to release any platform data on closing. + */ +static void camera_platform_release(struct device *device) +{ +} + +/*! + * Camera V4l2 callback function. + * + * @param mask u32 + * + * @param dev void device structure + * + * @return status + */ +static void camera_callback(u32 mask, void *dev) +{ + struct mxc_v4l_frame *done_frame; + struct mxc_v4l_frame *ready_frame; + struct timeval cur_time; + + cam_data *cam = (cam_data *) dev; + if (cam == NULL) + return; + + pr_debug("In MVC:camera_callback\n"); + + spin_lock(&cam->queue_int_lock); + spin_lock(&cam->dqueue_int_lock); + if (!list_empty(&cam->working_q)) { + do_gettimeofday(&cur_time); + + done_frame = list_entry(cam->working_q.next, + struct mxc_v4l_frame, + queue); + + if (done_frame->ipu_buf_num != cam->local_buf_num) + goto next; + + /* + * Set the current time to done frame buffer's + * timestamp. Users can use this information to judge + * the frame's usage. + */ + done_frame->buffer.timestamp = cur_time; + + if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) { + done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE; + done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED; + + /* Added to the done queue */ + list_del(cam->working_q.next); + list_add_tail(&done_frame->queue, &cam->done_q); + + /* Wake up the queue */ + cam->enc_counter++; + wake_up_interruptible(&cam->enc_queue); + } else + pr_err("ERROR: v4l2 capture: camera_callback: " + "buffer not queued\n"); + } + +next: + if (!list_empty(&cam->ready_q)) { + ready_frame = list_entry(cam->ready_q.next, + struct mxc_v4l_frame, + queue); + if (cam->enc_update_eba) + if (cam->enc_update_eba(cam->ipu, + ready_frame->buffer.m.offset, + &cam->ping_pong_csi) == 0) { + list_del(cam->ready_q.next); + list_add_tail(&ready_frame->queue, + &cam->working_q); + ready_frame->ipu_buf_num = cam->local_buf_num; + } + } else { + if (cam->enc_update_eba) + cam->enc_update_eba( + cam->ipu, cam->dummy_frame.buffer.m.offset, + &cam->ping_pong_csi); + } + + cam->local_buf_num = (cam->local_buf_num == 0) ? 1 : 0; + spin_unlock(&cam->dqueue_int_lock); + spin_unlock(&cam->queue_int_lock); + + return; +} + +/*! + * initialize cam_data structure + * + * @param cam structure cam_data * + * + * @return status 0 Success + */ +static int init_camera_struct(cam_data *cam, struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(mxc_v4l2_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; + int ipu_id, csi_id, mclk_source; + int ret = 0; + + pr_debug("In MVC: init_camera_struct\n"); + + ret = of_property_read_u32(np, "ipu_id", &ipu_id); + if (ret) { + dev_err(&pdev->dev, "ipu_id missing or invalid\n"); + return ret; + } + + ret = of_property_read_u32(np, "csi_id", &csi_id); + if (ret) { + dev_err(&pdev->dev, "csi_id missing or invalid\n"); + return ret; + } + + ret = of_property_read_u32(np, "mclk_source", &mclk_source); + if (ret) { + dev_err(&pdev->dev, "sensor mclk missing or invalid\n"); + return ret; + } + + /* Default everything to 0 */ + memset(cam, 0, sizeof(cam_data)); + + /* get devtype to distinguish if the cpu is imx5 or imx6 + * IMX5_V4L2 specify the cpu is imx5 + * IMX6_V4L2 specify the cpu is imx6q or imx6sdl + */ + if (of_id) + pdev->id_entry = of_id->data; + cam->devtype = pdev->id_entry->driver_data; + + cam->ipu = ipu_get_soc(ipu_id); + if (cam->ipu == NULL) { + pr_err("ERROR: v4l2 capture: failed to get ipu\n"); + return -EINVAL; + } else if (cam->ipu == ERR_PTR(-ENODEV)) { + pr_err("ERROR: v4l2 capture: get invalid ipu\n"); + return -ENODEV; + } + + init_MUTEX(&cam->param_lock); + init_MUTEX(&cam->busy_lock); + + cam->video_dev = video_device_alloc(); + if (cam->video_dev == NULL) + return -ENODEV; + + *(cam->video_dev) = mxc_v4l_template; + + video_set_drvdata(cam->video_dev, cam); + dev_set_drvdata(&pdev->dev, (void *)cam); + cam->video_dev->minor = -1; + + init_waitqueue_head(&cam->enc_queue); + init_waitqueue_head(&cam->still_queue); + + /* setup cropping */ + cam->crop_bounds.left = 0; + cam->crop_bounds.width = 640; + cam->crop_bounds.top = 0; + cam->crop_bounds.height = 480; + cam->crop_current = cam->crop_defrect = cam->crop_bounds; + ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, + cam->crop_current.height, cam->csi); + ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, + cam->crop_current.top, cam->csi); + cam->streamparm.parm.capture.capturemode = 0; + + cam->standard.index = 0; + cam->standard.id = V4L2_STD_UNKNOWN; + cam->standard.frameperiod.denominator = 30; + cam->standard.frameperiod.numerator = 1; + cam->standard.framelines = 480; + cam->standard_autodetect = true; + cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod; + cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + cam->overlay_on = false; + cam->capture_on = false; + cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY; + + cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2; + cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2; + cam->v2f.fmt.pix.width = 288; + cam->v2f.fmt.pix.height = 352; + cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + cam->win.w.width = 160; + cam->win.w.height = 160; + cam->win.w.left = 0; + cam->win.w.top = 0; + + cam->ipu_id = ipu_id; + cam->csi = csi_id; + cam->mclk_source = mclk_source; + cam->mclk_on[cam->mclk_source] = false; + + cam->enc_callback = camera_callback; + init_waitqueue_head(&cam->power_queue); + spin_lock_init(&cam->queue_int_lock); + spin_lock_init(&cam->dqueue_int_lock); + + cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); + cam->self->module = THIS_MODULE; + sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi); + cam->self->type = v4l2_int_type_master; + cam->self->u.master = &mxc_v4l2_master; + + return 0; +} + +static ssize_t show_streaming(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct video_device *video_dev = container_of(dev, + struct video_device, dev); + cam_data *cam = video_get_drvdata(video_dev); + + if (cam->capture_on) + return sprintf(buf, "stream on\n"); + else + return sprintf(buf, "stream off\n"); +} +static DEVICE_ATTR(fsl_v4l2_capture_property, S_IRUGO, show_streaming, NULL); + +static ssize_t show_overlay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct video_device *video_dev = container_of(dev, + struct video_device, dev); + cam_data *cam = video_get_drvdata(video_dev); + + if (cam->overlay_on) + return sprintf(buf, "overlay on\n"); + else + return sprintf(buf, "overlay off\n"); +} +static DEVICE_ATTR(fsl_v4l2_overlay_property, S_IRUGO, show_overlay, NULL); + +static ssize_t show_csi(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct video_device *video_dev = container_of(dev, + struct video_device, dev); + cam_data *cam = video_get_drvdata(video_dev); + + return sprintf(buf, "ipu%d_csi%d\n", cam->ipu_id, cam->csi); +} +static DEVICE_ATTR(fsl_csi_property, S_IRUGO, show_csi, NULL); + +/*! + * This function is called to probe the devices if registered. + * + * @param pdev the device structure used to give information on which device + * to probe + * + * @return The function returns 0 on success and -1 on failure. + */ +static int mxc_v4l2_probe(struct platform_device *pdev) +{ + /* Create cam and initialize it. */ + cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL); + if (cam == NULL) { + pr_err("ERROR: v4l2 capture: failed to register camera\n"); + return -1; + } + + init_camera_struct(cam, pdev); + pdev->dev.release = camera_platform_release; + + /* Set up the v4l2 device and register it*/ + cam->self->priv = cam; + v4l2_int_device_register(cam->self); + + /* register v4l video device */ + if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr) + == -1) { + kfree(cam); + cam = NULL; + pr_err("ERROR: v4l2 capture: video_register_device failed\n"); + return -1; + } + pr_debug(" Video device registered: %s #%d\n", + cam->video_dev->name, cam->video_dev->minor); + + if (device_create_file(&cam->video_dev->dev, + &dev_attr_fsl_v4l2_capture_property)) + dev_err(&pdev->dev, "Error on creating sysfs file" + " for capture\n"); + + if (device_create_file(&cam->video_dev->dev, + &dev_attr_fsl_v4l2_overlay_property)) + dev_err(&pdev->dev, "Error on creating sysfs file" + " for overlay\n"); + + if (device_create_file(&cam->video_dev->dev, + &dev_attr_fsl_csi_property)) + dev_err(&pdev->dev, "Error on creating sysfs file" + " for csi number\n"); + + return 0; +} + +/*! + * This function is called to remove the devices when device unregistered. + * + * @param pdev the device structure used to give information on which device + * to remove + * + * @return The function returns 0 on success and -1 on failure. + */ +static int mxc_v4l2_remove(struct platform_device *pdev) +{ + cam_data *cam = (cam_data *)platform_get_drvdata(pdev); + if (cam->open_count) { + pr_err("ERROR: v4l2 capture:camera open " + "-- setting ops to NULL\n"); + return -EBUSY; + } else { + device_remove_file(&cam->video_dev->dev, + &dev_attr_fsl_v4l2_capture_property); + device_remove_file(&cam->video_dev->dev, + &dev_attr_fsl_v4l2_overlay_property); + device_remove_file(&cam->video_dev->dev, + &dev_attr_fsl_csi_property); + + pr_info("V4L2 freeing image input device\n"); + v4l2_int_device_unregister(cam->self); + video_unregister_device(cam->video_dev); + + mxc_free_frame_buf(cam); + kfree(cam); + } + + pr_info("V4L2 unregistering video\n"); + return 0; +} + +/*! + * This function is called to put the sensor in a low power state. + * Refer to the document driver-model/driver.txt in the kernel source tree + * for more information. + * + * @param pdev the device structure used to give information on which I2C + * to suspend + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure. + */ +static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state) +{ + cam_data *cam = platform_get_drvdata(pdev); + + pr_debug("In MVC:mxc_v4l2_suspend\n"); + + if (cam == NULL) + return -1; + + down(&cam->busy_lock); + + cam->low_power = true; + + if (cam->overlay_on == true) + stop_preview(cam); + if ((cam->capture_on == true) && cam->enc_disable) + cam->enc_disable(cam); + + if (cam->sensor && cam->open_count) { + if (cam->mclk_on[cam->mclk_source]) { + ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, + cam->mclk_source, + false, false); + cam->mclk_on[cam->mclk_source] = false; + } + vidioc_int_s_power(cam->sensor, 0); + } + + up(&cam->busy_lock); + + return 0; +} + +/*! + * This function is called to bring the sensor back from a low power state. + * Refer to the document driver-model/driver.txt in the kernel source tree + * for more information. + * + * @param pdev the device structure + * + * @return The function returns 0 on success and -1 on failure + */ +static int mxc_v4l2_resume(struct platform_device *pdev) +{ + cam_data *cam = platform_get_drvdata(pdev); + + pr_debug("In MVC:mxc_v4l2_resume\n"); + + if (cam == NULL) + return -1; + + down(&cam->busy_lock); + + cam->low_power = false; + wake_up_interruptible(&cam->power_queue); + + if (cam->sensor && cam->open_count) { + vidioc_int_s_power(cam->sensor, 1); + + if (!cam->mclk_on[cam->mclk_source]) { + ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, + cam->mclk_source, + true, true); + cam->mclk_on[cam->mclk_source] = true; + } + } + + if (cam->overlay_on == true) + start_preview(cam); + if (cam->capture_on == true) + mxc_streamon(cam); + + up(&cam->busy_lock); + + return 0; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxc_v4l2_driver = { + .driver = { + .name = "mxc_v4l2_capture", + .owner = THIS_MODULE, + .of_match_table = mxc_v4l2_dt_ids, + }, + .id_table = imx_v4l2_devtype, + .probe = mxc_v4l2_probe, + .remove = mxc_v4l2_remove, + .suspend = mxc_v4l2_suspend, + .resume = mxc_v4l2_resume, + .shutdown = NULL, +}; + +/*! + * Initializes the camera driver. + */ +static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) +{ + cam_data *cam = slave->u.slave->master->priv; + struct v4l2_format cam_fmt; + int i; + struct sensor_data *sdata = slave->priv; + + pr_debug("In MVC: mxc_v4l2_master_attach\n"); + pr_debug(" slave.name = %s\n", slave->name); + pr_debug(" master.name = %s\n", slave->u.slave->master->name); + + if (slave == NULL) { + pr_err("ERROR: v4l2 capture: slave parameter not valid.\n"); + return -1; + } + + if (sdata->csi != cam->csi) { + pr_debug("%s: csi doesn't match\n", __func__); + return -1; + } + + cam->sensor = slave; + + if (cam->sensor_index < MXC_SENSOR_NUM) { + cam->all_sensors[cam->sensor_index] = slave; + cam->sensor_index++; + } else { + pr_err("ERROR: v4l2 capture: slave number exceeds the maximum.\n"); + return -1; + } + + for (i = 0; i < cam->sensor_index; i++) { + vidioc_int_dev_exit(cam->all_sensors[i]); + vidioc_int_s_power(cam->all_sensors[i], 0); + } + + cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); + + /* Used to detect TV in (type 1) vs. camera (type 0)*/ + cam->device_type = cam_fmt.fmt.pix.priv; + + /* Set the input size to the ipu for this device */ + cam->crop_bounds.top = cam->crop_bounds.left = 0; + cam->crop_bounds.width = cam_fmt.fmt.pix.width; + cam->crop_bounds.height = cam_fmt.fmt.pix.height; + + /* This also is the max crop size for this device. */ + cam->crop_defrect.top = cam->crop_defrect.left = 0; + cam->crop_defrect.width = cam_fmt.fmt.pix.width; + cam->crop_defrect.height = cam_fmt.fmt.pix.height; + + /* At this point, this is also the current image size. */ + cam->crop_current.top = cam->crop_current.left = 0; + cam->crop_current.width = cam_fmt.fmt.pix.width; + cam->crop_current.height = cam_fmt.fmt.pix.height; + + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", + __func__, + cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", + __func__, + cam->crop_bounds.width, cam->crop_bounds.height); + pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", + __func__, + cam->crop_defrect.width, cam->crop_defrect.height); + pr_debug("End of %s: crop_current widthxheight %d x %d\n", + __func__, + cam->crop_current.width, cam->crop_current.height); + + return 0; +} + +/*! + * Disconnects the camera driver. + */ +static void mxc_v4l2_master_detach(struct v4l2_int_device *slave) +{ + unsigned int i; + cam_data *cam = slave->u.slave->master->priv; + + pr_debug("In MVC:mxc_v4l2_master_detach\n"); + + if (cam->sensor_index > 1) { + for (i = 0; i < cam->sensor_index; i++) { + if (cam->all_sensors[i] != slave) + continue; + /* Move all the sensors behind this + * sensor one step forward + */ + for (; i <= MXC_SENSOR_NUM - 2; i++) + cam->all_sensors[i] = cam->all_sensors[i+1]; + break; + } + /* Point current sensor to the last one */ + cam->sensor = cam->all_sensors[cam->sensor_index - 2]; + } else + cam->sensor = NULL; + + cam->sensor_index--; + vidioc_int_dev_exit(slave); +} + +/*! + * Entry point for the V4L2 + * + * @return Error code indicating success or failure + */ +static __init int camera_init(void) +{ + u8 err = 0; + + pr_debug("In MVC:camera_init\n"); + + /* Register the device driver structure. */ + err = platform_driver_register(&mxc_v4l2_driver); + if (err != 0) { + pr_err("ERROR: v4l2 capture:camera_init: " + "platform_driver_register failed.\n"); + return err; + } + + return err; +} + +/*! + * Exit and cleanup for the V4L2 + */ +static void __exit camera_exit(void) +{ + pr_debug("In MVC: camera_exit\n"); + + platform_driver_unregister(&mxc_v4l2_driver); +} + +module_init(camera_init); +module_exit(camera_exit); + +module_param(video_nr, int, 0444); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h --- linux-3.10.30/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,260 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup MXC_V4L2_CAPTURE MXC V4L2 Video Capture Driver + */ +/*! + * @file mxc_v4l2_capture.h + * + * @brief mxc V4L2 capture device API Header file + * + * It include all the defines for frame operations, also three structure defines + * use case ops structure, common v4l2 driver structure and frame structure. + * + * @ingroup MXC_V4L2_CAPTURE + */ +#ifndef __MXC_V4L2_CAPTURE_H__ +#define __MXC_V4L2_CAPTURE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define FRAME_NUM 10 +#define MXC_SENSOR_NUM 2 + +enum imx_v4l2_devtype { + IMX5_V4L2, + IMX6_V4L2, +}; + +/*! + * v4l2 frame structure. + */ +struct mxc_v4l_frame { + u32 paddress; + void *vaddress; + int count; + int width; + int height; + + struct v4l2_buffer buffer; + struct list_head queue; + int index; + union { + int ipu_buf_num; + int csi_buf_num; + }; +}; + +/* Only for old version. Will go away soon. */ +typedef struct { + u8 clk_mode; + u8 ext_vsync; + u8 Vsync_pol; + u8 Hsync_pol; + u8 pixclk_pol; + u8 data_pol; + u8 data_width; + u8 pack_tight; + u8 force_eof; + u8 data_en_pol; + u16 width; + u16 height; + u32 pixel_fmt; + u32 mclk; + u16 active_width; + u16 active_height; +} sensor_interface; + +/* Sensor control function */ +/* Only for old version. Will go away soon. */ +struct camera_sensor { + void (*set_color) (int bright, int saturation, int red, int green, + int blue); + void (*get_color) (int *bright, int *saturation, int *red, int *green, + int *blue); + void (*set_ae_mode) (int ae_mode); + void (*get_ae_mode) (int *ae_mode); + sensor_interface *(*config) (int *frame_rate, int high_quality); + sensor_interface *(*reset) (void); + void (*get_std) (v4l2_std_id *std); + void (*set_std) (v4l2_std_id std); + unsigned int csi; +}; + +/*! + * common v4l2 driver structure. + */ +typedef struct _cam_data { + struct video_device *video_dev; + int device_type; + + /* semaphore guard against SMP multithreading */ + struct semaphore busy_lock; + + int open_count; + + /* params lock for this camera */ + struct semaphore param_lock; + + /* Encoder */ + struct list_head ready_q; + struct list_head done_q; + struct list_head working_q; + int ping_pong_csi; + spinlock_t queue_int_lock; + spinlock_t dqueue_int_lock; + struct mxc_v4l_frame frame[FRAME_NUM]; + struct mxc_v4l_frame dummy_frame; + wait_queue_head_t enc_queue; + int enc_counter; + dma_addr_t rot_enc_bufs[2]; + void *rot_enc_bufs_vaddr[2]; + int rot_enc_buf_size[2]; + enum v4l2_buf_type type; + + /* still image capture */ + wait_queue_head_t still_queue; + int still_counter; + dma_addr_t still_buf[2]; + void *still_buf_vaddr; + + /* overlay */ + struct v4l2_window win; + struct v4l2_framebuffer v4l2_fb; + dma_addr_t vf_bufs[2]; + void *vf_bufs_vaddr[2]; + int vf_bufs_size[2]; + dma_addr_t rot_vf_bufs[2]; + void *rot_vf_bufs_vaddr[2]; + int rot_vf_buf_size[2]; + bool overlay_active; + int output; + struct fb_info *overlay_fb; + int fb_origin_std; + struct work_struct csi_work_struct; + + /* v4l2 format */ + struct v4l2_format v2f; + int rotation; /* for IPUv1 and IPUv3, this means encoder rotation */ + int vf_rotation; /* viewfinder rotation only for IPUv1 and IPUv3 */ + struct v4l2_mxc_offset offset; + + /* V4l2 control bit */ + int bright; + int hue; + int contrast; + int saturation; + int red; + int green; + int blue; + int ae_mode; + + /* standard */ + struct v4l2_streamparm streamparm; + struct v4l2_standard standard; + bool standard_autodetect; + + /* crop */ + struct v4l2_rect crop_bounds; + struct v4l2_rect crop_defrect; + struct v4l2_rect crop_current; + + int (*enc_update_eba) (struct ipu_soc *ipu, dma_addr_t eba, + int *bufferNum); + int (*enc_enable) (void *private); + int (*enc_disable) (void *private); + int (*enc_enable_csi) (void *private); + int (*enc_disable_csi) (void *private); + void (*enc_callback) (u32 mask, void *dev); + int (*vf_start_adc) (void *private); + int (*vf_stop_adc) (void *private); + int (*vf_start_sdc) (void *private); + int (*vf_stop_sdc) (void *private); + int (*vf_enable_csi) (void *private); + int (*vf_disable_csi) (void *private); + int (*csi_start) (void *private); + int (*csi_stop) (void *private); + + /* misc status flag */ + bool overlay_on; + bool capture_on; + int overlay_pid; + int capture_pid; + bool low_power; + wait_queue_head_t power_queue; + unsigned int ipu_id; + unsigned int csi; + u8 mclk_source; + bool mclk_on[2]; /* two mclk sources at most now */ + int current_input; + + int local_buf_num; + + /* camera sensor interface */ + struct camera_sensor *cam_sensor; /* old version */ + struct v4l2_int_device *all_sensors[MXC_SENSOR_NUM]; + struct v4l2_int_device *sensor; + struct v4l2_int_device *self; + int sensor_index; + void *ipu; + enum imx_v4l2_devtype devtype; + + /* v4l2 buf elements related to PxP DMA */ + struct completion pxp_tx_cmpl; + struct pxp_channel *pxp_chan; + struct pxp_config_data pxp_conf; + struct dma_async_tx_descriptor *txd; + dma_cookie_t cookie; + struct scatterlist sg[2]; +} cam_data; + +struct sensor_data { + const struct ov5642_platform_data *platform_data; + struct v4l2_int_device *v4l2_int_device; + struct i2c_client *i2c_client; + struct v4l2_pix_format pix; + struct v4l2_captureparm streamcap; + bool on; + + /* control settings */ + int brightness; + int hue; + int contrast; + int saturation; + int red; + int green; + int blue; + int ae_mode; + + u32 mclk; + u8 mclk_source; + struct clk *sensor_clk; + int csi; + + void (*io_init)(void); +}; + +void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi); +#endif /* __MXC_V4L2_CAPTURE_H__ */ diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ov5640.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ov5640.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ov5640.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ov5640.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,1951 @@ +/* + * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" + +#define OV5640_VOLTAGE_ANALOG 2800000 +#define OV5640_VOLTAGE_DIGITAL_CORE 1500000 +#define OV5640_VOLTAGE_DIGITAL_IO 1800000 + +#define MIN_FPS 15 +#define MAX_FPS 30 +#define DEFAULT_FPS 30 + +#define OV5640_XCLK_MIN 6000000 +#define OV5640_XCLK_MAX 24000000 + +#define OV5640_CHIP_ID_HIGH_BYTE 0x300A +#define OV5640_CHIP_ID_LOW_BYTE 0x300B + +enum ov5640_mode { + ov5640_mode_MIN = 0, + ov5640_mode_VGA_640_480 = 0, + ov5640_mode_QVGA_320_240 = 1, + ov5640_mode_NTSC_720_480 = 2, + ov5640_mode_PAL_720_576 = 3, + ov5640_mode_720P_1280_720 = 4, + ov5640_mode_1080P_1920_1080 = 5, + ov5640_mode_QSXGA_2592_1944 = 6, + ov5640_mode_QCIF_176_144 = 7, + ov5640_mode_XGA_1024_768 = 8, + ov5640_mode_MAX = 8 +}; + +enum ov5640_frame_rate { + ov5640_15_fps, + ov5640_30_fps +}; + +static int ov5640_framerates[] = { + [ov5640_15_fps] = 15, + [ov5640_30_fps] = 30, +}; + +struct reg_value { + u16 u16RegAddr; + u8 u8Val; + u8 u8Mask; + u32 u32Delay_ms; +}; + +struct ov5640_mode_info { + enum ov5640_mode mode; + u32 width; + u32 height; + struct reg_value *init_data_ptr; + u32 init_data_size; +}; + +/*! + * Maintains the information on the current state of the sesor. + */ +static struct sensor_data ov5640_data; +static int pwn_gpio, rst_gpio; +static int prev_sysclk; +static int AE_Target = 52, night_mode; +static int prev_HTS; +static int AE_high, AE_low; + +static struct reg_value ov5640_global_init_setting[] = { + {0x3008, 0x42, 0, 0}, + {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0}, + {0x3034, 0x1a, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, + {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, + {0x3633, 0x12, 0, 0}, {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, + {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, + {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, + {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, + {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, + {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, + {0x3a13, 0x43, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, + {0x3635, 0x13, 0, 0}, {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, + {0x3622, 0x01, 0, 0}, {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, + {0x3c05, 0x98, 0, 0}, {0x3c06, 0x00, 0, 0}, {0x3c07, 0x07, 0, 0}, + {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, + {0x3c0b, 0x40, 0, 0}, {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, + {0x3812, 0x00, 0, 0}, {0x3708, 0x64, 0, 0}, {0x4001, 0x02, 0, 0}, + {0x4005, 0x1a, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, + {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x440e, 0x00, 0, 0}, {0x5000, 0xa7, 0, 0}, + {0x3008, 0x02, 0, 0}, +}; + +static struct reg_value ov5640_init_setting_30fps_VGA[] = { + {0x3008, 0x42, 0, 0}, + {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0}, + {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0}, + {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0}, + {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, + {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, + {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, + {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, + {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, + {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, + {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0}, + {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0}, + {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0}, + {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0}, + {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5000, 0xa7, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0xf2, 0, 0}, + {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, {0x5187, 0x09, 0, 0}, + {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0}, {0x518a, 0x54, 0, 0}, + {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x50, 0, 0}, + {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x6c, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x09, 0, 0}, + {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0}, {0x5381, 0x1e, 0, 0}, + {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0}, {0x5384, 0x0a, 0, 0}, + {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0}, {0x5387, 0x7c, 0, 0}, + {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0}, {0x538a, 0x01, 0, 0}, + {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0}, {0x5301, 0x30, 0, 0}, + {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0}, {0x5304, 0x08, 0, 0}, + {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0}, {0x5307, 0x16, 0, 0}, + {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0}, {0x530b, 0x04, 0, 0}, + {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0}, {0x5481, 0x08, 0, 0}, + {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0}, {0x5484, 0x51, 0, 0}, + {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0}, {0x5487, 0x7d, 0, 0}, + {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0}, {0x548a, 0x9a, 0, 0}, + {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0}, {0x548d, 0xcd, 0, 0}, + {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0}, {0x5490, 0x1d, 0, 0}, + {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x10, 0, 0}, + {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0}, {0x558b, 0xf8, 0, 0}, + {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0}, {0x5802, 0x0f, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0}, {0x5805, 0x26, 0, 0}, + {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0}, {0x5808, 0x05, 0, 0}, + {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0}, {0x580b, 0x0d, 0, 0}, + {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0}, {0x580e, 0x00, 0, 0}, + {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0}, {0x5811, 0x09, 0, 0}, + {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x00, 0, 0}, + {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0}, {0x5817, 0x08, 0, 0}, + {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0}, {0x581a, 0x05, 0, 0}, + {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0}, {0x581d, 0x0e, 0, 0}, + {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0}, {0x5820, 0x11, 0, 0}, + {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0}, {0x5823, 0x28, 0, 0}, + {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0}, {0x5829, 0x26, 0, 0}, + {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0}, {0x582c, 0x24, 0, 0}, + {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0}, {0x582f, 0x22, 0, 0}, + {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0}, {0x5832, 0x24, 0, 0}, + {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0}, {0x5835, 0x22, 0, 0}, + {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0}, {0x5838, 0x44, 0, 0}, + {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0}, {0x583b, 0x28, 0, 0}, + {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0}, {0x5025, 0x00, 0, 0}, + {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0}, + {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0}, + {0x3008, 0x02, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_VGA_640_480[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, + {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3503, 0x00, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_VGA_640_480[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, + {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, {0x3503, 0x00, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, + {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, + {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, + {0x3807, 0xd4, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, + {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, + {0x3807, 0xd4, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, + {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_PAL_720_576[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x60, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x09, 0, 0}, {0x3805, 0x7e, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, + {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_PAL_720_576[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x60, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x09, 0, 0}, {0x3805, 0x7e, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, + {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_720P_1280_720[] = { + {0x3035, 0x21, 0, 0}, {0x3036, 0x69, 0, 0}, {0x3c07, 0x07, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3709, 0x52, 0, 0}, + {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe0, 0, 0}, + {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe0, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, + {0x4837, 0x16, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, + {0x3503, 0x00, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_720P_1280_720[] = { + {0x3035, 0x41, 0, 0}, {0x3036, 0x69, 0, 0}, {0x3c07, 0x07, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3709, 0x52, 0, 0}, + {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe0, 0, 0}, + {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe0, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, + {0x4837, 0x16, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, + {0x3503, 0x00, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, + {0x380a, 0x00, 0, 0}, {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, + {0x380a, 0x00, 0, 0}, {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, + {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { + {0x3c07, 0x08, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9b, 0, 0}, {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, + {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, {0x380c, 0x07, 0, 0}, + {0x380d, 0x68, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, + {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x0b, 0, 0}, + {0x3a03, 0x88, 0, 0}, {0x3a14, 0x0b, 0, 0}, {0x3a15, 0x88, 0, 0}, + {0x4004, 0x02, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, + {0x460c, 0x20, 0, 0}, {0x4837, 0x22, 0, 0}, {0x3824, 0x01, 0, 0}, + {0x5001, 0xa3, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x46, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + + +static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { + {0x3c07, 0x07, 0, 0}, {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, + {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xee, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x05, 0, 0}, + {0x3807, 0xc3, 0, 0}, {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0}, + {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, {0x380c, 0x0b, 0, 0}, + {0x380d, 0x1c, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, + {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0}, + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0}, + {0x3a03, 0xae, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xae, 0, 0}, + {0x4004, 0x06, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x02, 0, 0}, {0x4407, 0x0c, 0, 0}, {0x460b, 0x37, 0, 0}, + {0x460c, 0x20, 0, 0}, {0x4837, 0x2c, 0, 0}, {0x3824, 0x01, 0, 0}, + {0x5001, 0x83, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { + {0x3c07, 0x07, 0, 0}, {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, + {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, + {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, + {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, + {0x3807, 0x9f, 0, 0}, {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, + {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, + {0x380d, 0x1c, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, + {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0}, + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0}, + {0x3a03, 0xae, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xae, 0, 0}, + {0x4004, 0x06, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x4713, 0x02, 0, 0}, {0x4407, 0x0c, 0, 0}, {0x460b, 0x37, 0, 0}, + {0x460c, 0x20, 0, 0}, {0x4837, 0x2c, 0, 0}, {0x3824, 0x01, 0, 0}, + {0x5001, 0x83, 0, 0}, {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x69, 0, 0}, {0x3037, 0x13, 0, 0}, +}; + +static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = { + { + {ov5640_mode_VGA_640_480, 640, 480, + ov5640_setting_15fps_VGA_640_480, + ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, + {ov5640_mode_QVGA_320_240, 320, 240, + ov5640_setting_15fps_QVGA_320_240, + ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, + {ov5640_mode_NTSC_720_480, 720, 480, + ov5640_setting_15fps_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, + {ov5640_mode_PAL_720_576, 720, 576, + ov5640_setting_15fps_PAL_720_576, + ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, + {ov5640_mode_720P_1280_720, 1280, 720, + ov5640_setting_15fps_720P_1280_720, + ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, + {ov5640_mode_1080P_1920_1080, 1920, 1080, + ov5640_setting_15fps_1080P_1920_1080, + ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, + {ov5640_mode_QSXGA_2592_1944, 2592, 1944, + ov5640_setting_15fps_QSXGA_2592_1944, + ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, + {ov5640_mode_QCIF_176_144, 176, 144, + ov5640_setting_15fps_QCIF_176_144, + ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, + {ov5640_mode_XGA_1024_768, 1024, 768, + ov5640_setting_15fps_XGA_1024_768, + ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, + }, + { + {ov5640_mode_VGA_640_480, 640, 480, + ov5640_setting_30fps_VGA_640_480, + ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, + {ov5640_mode_QVGA_320_240, 320, 240, + ov5640_setting_30fps_QVGA_320_240, + ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, + {ov5640_mode_NTSC_720_480, 720, 480, + ov5640_setting_30fps_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, + {ov5640_mode_PAL_720_576, 720, 576, + ov5640_setting_30fps_PAL_720_576, + ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, + {ov5640_mode_720P_1280_720, 1280, 720, + ov5640_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, + {ov5640_mode_1080P_1920_1080, 0, 0, NULL, 0}, + {ov5640_mode_QSXGA_2592_1944, 0, 0, NULL, 0}, + {ov5640_mode_QCIF_176_144, 176, 144, + ov5640_setting_30fps_QCIF_176_144, + ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, + {ov5640_mode_XGA_1024_768, 1024, 768, + ov5640_setting_30fps_XGA_1024_768, + ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, + }, +}; + +static struct regulator *io_regulator; +static struct regulator *core_regulator; +static struct regulator *analog_regulator; + +static int ov5640_probe(struct i2c_client *adapter, + const struct i2c_device_id *device_id); +static int ov5640_remove(struct i2c_client *client); + +static s32 ov5640_read_reg(u16 reg, u8 *val); +static s32 ov5640_write_reg(u16 reg, u8 val); + +static const struct i2c_device_id ov5640_id[] = { + {"ov5640", 0}, + {"ov564x", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ov5640_id); + +static struct i2c_driver ov5640_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ov5640", + }, + .probe = ov5640_probe, + .remove = ov5640_remove, + .id_table = ov5640_id, +}; + +static inline void ov5640_power_down(int enable) +{ + gpio_set_value(pwn_gpio, enable); + + msleep(2); +} + +static inline void ov5640_reset(void) +{ + /* camera reset */ + gpio_set_value(rst_gpio, 1); + + /* camera power down */ + gpio_set_value(pwn_gpio, 1); + msleep(5); + gpio_set_value(pwn_gpio, 0); + msleep(5); + gpio_set_value(rst_gpio, 0); + msleep(1); + gpio_set_value(rst_gpio, 1); + msleep(5); + gpio_set_value(pwn_gpio, 1); +} + +static int ov5640_regulator_enable(struct device *dev) +{ + int ret = 0; + + io_regulator = devm_regulator_get(dev, "DOVDD"); + if (!IS_ERR(io_regulator)) { + regulator_set_voltage(io_regulator, + OV5640_VOLTAGE_DIGITAL_IO, + OV5640_VOLTAGE_DIGITAL_IO); + ret = regulator_enable(io_regulator); + if (ret) { + dev_err(dev, "set io voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set io voltage ok\n"); + } + } else { + io_regulator = NULL; + dev_warn(dev, "cannot get io voltage\n"); + } + + core_regulator = devm_regulator_get(dev, "DVDD"); + if (!IS_ERR(core_regulator)) { + regulator_set_voltage(core_regulator, + OV5640_VOLTAGE_DIGITAL_CORE, + OV5640_VOLTAGE_DIGITAL_CORE); + ret = regulator_enable(core_regulator); + if (ret) { + dev_err(dev, "set core voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set core voltage ok\n"); + } + } else { + core_regulator = NULL; + dev_warn(dev, "cannot get core voltage\n"); + } + + analog_regulator = devm_regulator_get(dev, "AVDD"); + if (!IS_ERR(analog_regulator)) { + regulator_set_voltage(analog_regulator, + OV5640_VOLTAGE_ANALOG, + OV5640_VOLTAGE_ANALOG); + ret = regulator_enable(analog_regulator); + if (ret) { + dev_err(dev, "set analog voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set analog voltage ok\n"); + } + } else { + analog_regulator = NULL; + dev_warn(dev, "cannot get analog voltage\n"); + } + + return ret; +} + +static s32 ov5640_write_reg(u16 reg, u8 val) +{ + u8 au8Buf[3] = {0}; + + au8Buf[0] = reg >> 8; + au8Buf[1] = reg & 0xff; + au8Buf[2] = val; + + if (i2c_master_send(ov5640_data.i2c_client, au8Buf, 3) < 0) { + pr_err("%s:write reg error:reg=%x,val=%x\n", + __func__, reg, val); + return -1; + } + + return 0; +} + +static s32 ov5640_read_reg(u16 reg, u8 *val) +{ + u8 au8RegBuf[2] = {0}; + u8 u8RdVal = 0; + + au8RegBuf[0] = reg >> 8; + au8RegBuf[1] = reg & 0xff; + + if (2 != i2c_master_send(ov5640_data.i2c_client, au8RegBuf, 2)) { + pr_err("%s:write reg error:reg=%x\n", + __func__, reg); + return -1; + } + + if (1 != i2c_master_recv(ov5640_data.i2c_client, &u8RdVal, 1)) { + pr_err("%s:read reg error:reg=%x,val=%x\n", + __func__, reg, u8RdVal); + return -1; + } + + *val = u8RdVal; + + return u8RdVal; +} + +static void ov5640_soft_reset(void) +{ + /* sysclk from pad */ + ov5640_write_reg(0x3103, 0x11); + + /* software reset */ + ov5640_write_reg(0x3008, 0x82); + + /* delay at least 5ms */ + msleep(10); +} + +/* set sensor driver capability + * 0x302c[7:6] - strength + 00 - 1x + 01 - 2x + 10 - 3x + 11 - 4x + */ +static int ov5640_driver_capability(int strength) +{ + u8 temp = 0; + + if (strength > 4 || strength < 1) { + pr_err("The valid driver capability of ov5640 is 1x~4x\n"); + return -EINVAL; + } + + ov5640_read_reg(0x302c, &temp); + + temp &= ~0xc0; /* clear [7:6] */ + temp |= ((strength - 1) << 6); /* set [7:6] */ + + ov5640_write_reg(0x302c, temp); + + return 0; +} + +/* calculate sysclk */ +static int ov5640_get_sysclk(void) +{ + int xvclk = ov5640_data.mclk / 10000; + int sysclk; + int temp1, temp2; + int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv, Bit_div2x, sclk_rdiv; + int sclk_rdiv_map[] = {1, 2, 4, 8}; + u8 regval = 0; + + temp1 = ov5640_read_reg(0x3034, ®val); + temp2 = temp1 & 0x0f; + if (temp2 == 8 || temp2 == 10) { + Bit_div2x = temp2 / 2; + } else { + pr_err("ov5640: unsupported bit mode %d\n", temp2); + return -1; + } + + temp1 = ov5640_read_reg(0x3035, ®val); + SysDiv = temp1 >> 4; + if (SysDiv == 0) + SysDiv = 16; + + temp1 = ov5640_read_reg(0x3036, ®val); + Multiplier = temp1; + temp1 = ov5640_read_reg(0x3037, ®val); + PreDiv = temp1 & 0x0f; + Pll_rdiv = ((temp1 >> 4) & 0x01) + 1; + + temp1 = ov5640_read_reg(0x3108, ®val); + temp2 = temp1 & 0x03; + + sclk_rdiv = sclk_rdiv_map[temp2]; + VCO = xvclk * Multiplier / PreDiv; + sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv; + + return sysclk; +} + +/* read HTS from register settings */ +static int ov5640_get_HTS(void) +{ + int HTS; + u8 temp = 0; + + HTS = ov5640_read_reg(0x380c, &temp); + HTS = (HTS<<8) + ov5640_read_reg(0x380d, &temp); + return HTS; +} + +/* read VTS from register settings */ +static int ov5640_get_VTS(void) +{ + int VTS; + u8 temp = 0; + + VTS = ov5640_read_reg(0x380e, &temp); + VTS = (VTS<<8) + ov5640_read_reg(0x380f, &temp); + + return VTS; +} + +/* write VTS to registers */ +static int ov5640_set_VTS(int VTS) +{ + int temp; + + temp = VTS & 0xff; + ov5640_write_reg(0x380f, temp); + + temp = VTS>>8; + ov5640_write_reg(0x380e, temp); + return 0; +} + +/* read shutter, in number of line period */ +static int ov5640_get_shutter(void) +{ + int shutter; + u8 regval; + + shutter = (ov5640_read_reg(0x03500, ®val) & 0x0f); + + shutter = (shutter<<8) + ov5640_read_reg(0x3501, ®val); + shutter = (shutter<<4) + (ov5640_read_reg(0x3502, ®val)>>4); + + return shutter; +} + +/* write shutter, in number of line period */ +static int ov5640_set_shutter(int shutter) +{ + int temp; + + shutter = shutter & 0xffff; + temp = shutter & 0x0f; + temp = temp<<4; + ov5640_write_reg(0x3502, temp); + + temp = shutter & 0xfff; + temp = temp>>4; + ov5640_write_reg(0x3501, temp); + + temp = shutter>>12; + ov5640_write_reg(0x3500, temp); + + return 0; +} + +/* read gain, 16 = 1x */ +static int ov5640_get_gain16(void) +{ + int gain16; + u8 regval; + + gain16 = ov5640_read_reg(0x350a, ®val) & 0x03; + gain16 = (gain16<<8) + ov5640_read_reg(0x350b, ®val); + + return gain16; +} + +/* write gain, 16 = 1x */ +static int ov5640_set_gain16(int gain16) +{ + int temp; + + gain16 = gain16 & 0x3ff; + temp = gain16 & 0xff; + + ov5640_write_reg(0x350b, temp); + temp = gain16>>8; + + ov5640_write_reg(0x350a, temp); + return 0; +} + +/* get banding filter value */ +static int ov5640_get_light_freq(void) +{ + int temp, temp1, light_frequency; + u8 regval; + + temp = ov5640_read_reg(0x3c01, ®val); + if (temp & 0x80) { + /* manual */ + temp1 = ov5640_read_reg(0x3c00, ®val); + if (temp1 & 0x04) { + /* 50Hz */ + light_frequency = 50; + } else { + /* 60Hz */ + light_frequency = 60; + } + } else { + /* auto */ + temp1 = ov5640_read_reg(0x3c0c, ®val); + if (temp1 & 0x01) { + /* 50Hz */ + light_frequency = 50; + } else { + /* 60Hz */ + light_frequency = 60; + } + } + + return light_frequency; +} + +static void ov5640_set_bandingfilter(void) +{ + int prev_VTS; + int band_step60, max_band60, band_step50, max_band50; + + /* read preview PCLK */ + prev_sysclk = ov5640_get_sysclk(); + + /* read preview HTS */ + prev_HTS = ov5640_get_HTS(); + + /* read preview VTS */ + prev_VTS = ov5640_get_VTS(); + + /* calculate banding filter */ + /* 60Hz */ + band_step60 = prev_sysclk * 100/prev_HTS * 100/120; + ov5640_write_reg(0x3a0a, (band_step60 >> 8)); + ov5640_write_reg(0x3a0b, (band_step60 & 0xff)); + + max_band60 = (int)((prev_VTS-4)/band_step60); + ov5640_write_reg(0x3a0d, max_band60); + + /* 50Hz */ + band_step50 = prev_sysclk * 100/prev_HTS; + ov5640_write_reg(0x3a08, (band_step50 >> 8)); + ov5640_write_reg(0x3a09, (band_step50 & 0xff)); + + max_band50 = (int)((prev_VTS-4)/band_step50); + ov5640_write_reg(0x3a0e, max_band50); +} + +/* stable in high */ +static int ov5640_set_AE_target(int target) +{ + int fast_high, fast_low; + + AE_low = target * 23 / 25; /* 0.92 */ + AE_high = target * 27 / 25; /* 1.08 */ + fast_high = AE_high << 1; + + if (fast_high > 255) + fast_high = 255; + fast_low = AE_low >> 1; + + ov5640_write_reg(0x3a0f, AE_high); + ov5640_write_reg(0x3a10, AE_low); + ov5640_write_reg(0x3a1b, AE_high); + ov5640_write_reg(0x3a1e, AE_low); + ov5640_write_reg(0x3a11, fast_high); + ov5640_write_reg(0x3a1f, fast_low); + + return 0; +} + +/* enable = 0 to turn off night mode + enable = 1 to turn on night mode */ +static int ov5640_set_night_mode(int enable) +{ + u8 mode; + + ov5640_read_reg(0x3a00, &mode); + + if (enable) { + /* night mode on */ + mode |= 0x04; + ov5640_write_reg(0x3a00, mode); + } else { + /* night mode off */ + mode &= 0xfb; + ov5640_write_reg(0x3a00, mode); + } + + return 0; +} + +/* enable = 0 to turn off AEC/AGC + enable = 1 to turn on AEC/AGC */ +void ov5640_turn_on_AE_AG(int enable) +{ + u8 ae_ag_ctrl; + + ov5640_read_reg(0x3503, &ae_ag_ctrl); + if (enable) { + /* turn on auto AE/AG */ + ae_ag_ctrl = ae_ag_ctrl & ~(0x03); + } else { + /* turn off AE/AG */ + ae_ag_ctrl = ae_ag_ctrl | 0x03; + } + ov5640_write_reg(0x3503, ae_ag_ctrl); +} + +/* download ov5640 settings to sensor through i2c */ +static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize) +{ + register u32 Delay_ms = 0; + register u16 RegAddr = 0; + register u8 Mask = 0; + register u8 Val = 0; + u8 RegVal = 0; + int i, retval = 0; + + for (i = 0; i < ArySize; ++i, ++pModeSetting) { + Delay_ms = pModeSetting->u32Delay_ms; + RegAddr = pModeSetting->u16RegAddr; + Val = pModeSetting->u8Val; + Mask = pModeSetting->u8Mask; + + if (Mask) { + retval = ov5640_read_reg(RegAddr, &RegVal); + if (retval < 0) + goto err; + + RegVal &= ~(u8)Mask; + Val &= Mask; + Val |= RegVal; + } + + retval = ov5640_write_reg(RegAddr, Val); + if (retval < 0) + goto err; + + if (Delay_ms) + msleep(Delay_ms); + } +err: + return retval; +} + +static int ov5640_init_mode(void) +{ + struct reg_value *pModeSetting = NULL; + int ArySize = 0, retval = 0; + + ov5640_soft_reset(); + + pModeSetting = ov5640_global_init_setting; + ArySize = ARRAY_SIZE(ov5640_global_init_setting); + retval = ov5640_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + + pModeSetting = ov5640_init_setting_30fps_VGA; + ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA); + retval = ov5640_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + + /* change driver capability to 2x according to validation board. + * if the image is not stable, please increase the driver strength. + */ + ov5640_driver_capability(2); + ov5640_set_bandingfilter(); + ov5640_set_AE_target(AE_Target); + ov5640_set_night_mode(night_mode); + + /* skip 9 vysnc: start capture at 10th vsync */ + msleep(300); + + /* turn off night mode */ + night_mode = 0; + ov5640_data.pix.width = 640; + ov5640_data.pix.height = 480; +err: + return retval; +} + +/* change to or back to subsampling mode set the mode directly + * image size below 1280 * 960 is subsampling mode */ +static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate, + enum ov5640_mode mode) +{ + struct reg_value *pModeSetting = NULL; + s32 ArySize = 0; + int retval = 0; + + if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) { + pr_err("Wrong ov5640 mode detected!\n"); + return -1; + } + + pModeSetting = ov5640_mode_info_data[frame_rate][mode].init_data_ptr; + ArySize = + ov5640_mode_info_data[frame_rate][mode].init_data_size; + + ov5640_data.pix.width = ov5640_mode_info_data[frame_rate][mode].width; + ov5640_data.pix.height = ov5640_mode_info_data[frame_rate][mode].height; + + if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 || + pModeSetting == NULL || ArySize == 0) + return -EINVAL; + + /* set ov5640 to subsampling mode */ + retval = ov5640_download_firmware(pModeSetting, ArySize); + + /* turn on AE AG for subsampling mode, in case the firmware didn't */ + ov5640_turn_on_AE_AG(1); + + /* calculate banding filter */ + ov5640_set_bandingfilter(); + + /* set AE target */ + ov5640_set_AE_target(AE_Target); + + /* update night mode setting */ + ov5640_set_night_mode(night_mode); + + /* skip 9 vysnc: start capture at 10th vsync */ + if (mode == ov5640_mode_XGA_1024_768 && frame_rate == ov5640_30_fps) { + pr_warning("ov5640: actual frame rate of XGA is 22.5fps\n"); + /* 1/22.5 * 9*/ + msleep(400); + return retval; + } + + if (frame_rate == ov5640_15_fps) { + /* 1/15 * 9*/ + msleep(600); + } else if (frame_rate == ov5640_30_fps) { + /* 1/30 * 9*/ + msleep(300); + } + + return retval; +} + +/* change to scaling mode go through exposure calucation + * image size above 1280 * 960 is scaling mode */ +static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate, + enum ov5640_mode mode) +{ + int prev_shutter, prev_gain16, average; + int cap_shutter, cap_gain16; + int cap_sysclk, cap_HTS, cap_VTS; + int light_freq, cap_bandfilt, cap_maxband; + long cap_gain16_shutter; + u8 temp; + struct reg_value *pModeSetting = NULL; + s32 ArySize = 0; + int retval = 0; + + /* check if the input mode and frame rate is valid */ + pModeSetting = + ov5640_mode_info_data[frame_rate][mode].init_data_ptr; + ArySize = + ov5640_mode_info_data[frame_rate][mode].init_data_size; + + ov5640_data.pix.width = + ov5640_mode_info_data[frame_rate][mode].width; + ov5640_data.pix.height = + ov5640_mode_info_data[frame_rate][mode].height; + + if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 || + pModeSetting == NULL || ArySize == 0) + return -EINVAL; + + /* read preview shutter */ + prev_shutter = ov5640_get_shutter(); + + /* read preview gain */ + prev_gain16 = ov5640_get_gain16(); + + /* get average */ + average = ov5640_read_reg(0x56a1, &temp); + + /* turn off night mode for capture */ + ov5640_set_night_mode(0); + + /* turn off overlay */ + ov5640_write_reg(0x3022, 0x06); + + /* Write capture setting */ + retval = ov5640_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + + /* turn off AE AG when capture image. */ + ov5640_turn_on_AE_AG(0); + + /* read capture VTS */ + cap_VTS = ov5640_get_VTS(); + cap_HTS = ov5640_get_HTS(); + cap_sysclk = ov5640_get_sysclk(); + + /* calculate capture banding filter */ + light_freq = ov5640_get_light_freq(); + if (light_freq == 60) { + /* 60Hz */ + cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120; + } else { + /* 50Hz */ + cap_bandfilt = cap_sysclk * 100 / cap_HTS; + } + cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt); + /* calculate capture shutter/gain16 */ + if (average > AE_low && average < AE_high) { + /* in stable range */ + cap_gain16_shutter = + prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk * + prev_HTS/cap_HTS * AE_Target / average; + } else { + cap_gain16_shutter = + prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk * + prev_HTS/cap_HTS; + } + + /* gain to shutter */ + if (cap_gain16_shutter < (cap_bandfilt * 16)) { + /* shutter < 1/100 */ + cap_shutter = cap_gain16_shutter/16; + if (cap_shutter < 1) + cap_shutter = 1; + cap_gain16 = cap_gain16_shutter/cap_shutter; + if (cap_gain16 < 16) + cap_gain16 = 16; + } else { + if (cap_gain16_shutter > (cap_bandfilt*cap_maxband*16)) { + /* exposure reach max */ + cap_shutter = cap_bandfilt*cap_maxband; + cap_gain16 = cap_gain16_shutter / cap_shutter; + } else { + /* 1/100 < cap_shutter =< max, cap_shutter = n/100 */ + cap_shutter = + ((int)(cap_gain16_shutter/16/cap_bandfilt)) + * cap_bandfilt; + cap_gain16 = cap_gain16_shutter / cap_shutter; + } + } + + /* write capture gain */ + ov5640_set_gain16(cap_gain16); + + /* write capture shutter */ + if (cap_shutter > (cap_VTS - 4)) { + cap_VTS = cap_shutter + 4; + ov5640_set_VTS(cap_VTS); + } + + ov5640_set_shutter(cap_shutter); + + /* skip 2 vysnc: start capture at 3rd vsync + * frame rate of QSXGA and 1080P is 7.5fps: 1/7.5 * 2 + */ + pr_warning("ov5640: the actual frame rate of %s is 7.5fps\n", + mode == ov5640_mode_1080P_1920_1080 ? "1080P" : "QSXGA"); + msleep(267); +err: + return retval; +} + +static int ov5640_change_mode(enum ov5640_frame_rate frame_rate, + enum ov5640_mode mode) +{ + int retval = 0; + + if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) { + pr_err("Wrong ov5640 mode detected!\n"); + return -1; + } + + if (mode == ov5640_mode_1080P_1920_1080 || + mode == ov5640_mode_QSXGA_2592_1944) { + /* change to scaling mode go through exposure calucation + * image size above 1280 * 960 is scaling mode */ + retval = ov5640_change_mode_exposure_calc(frame_rate, mode); + } else { + /* change back to subsampling modem download firmware directly + * image size below 1280 * 960 is subsampling mode */ + retval = ov5640_change_mode_direct(frame_rate, mode); + } + + return retval; +} + +/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ + +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + if (s == NULL) { + pr_err(" ERROR!! no slave device set!\n"); + return -1; + } + + memset(p, 0, sizeof(*p)); + p->u.bt656.clock_curr = ov5640_data.mclk; + pr_debug(" clock_curr=mclk=%d\n", ov5640_data.mclk); + p->if_type = V4L2_IF_TYPE_BT656; + p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; + p->u.bt656.clock_min = OV5640_XCLK_MIN; + p->u.bt656.clock_max = OV5640_XCLK_MAX; + p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ + + return 0; +} + +/*! + * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl + * @s: pointer to standard V4L2 device structure + * @on: indicates power mode (on or off) + * + * Turns the power on or off, depending on the value of on and returns the + * appropriate error code. + */ +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct sensor_data *sensor = s->priv; + + if (on && !sensor->on) { + if (io_regulator) + if (regulator_enable(io_regulator) != 0) + return -EIO; + if (core_regulator) + if (regulator_enable(core_regulator) != 0) + return -EIO; + if (analog_regulator) + if (regulator_enable(analog_regulator) != 0) + return -EIO; + /* Make sure power on */ + ov5640_power_down(0); + } else if (!on && sensor->on) { + if (analog_regulator) + regulator_disable(analog_regulator); + if (core_regulator) + regulator_disable(core_regulator); + if (io_regulator) + regulator_disable(io_regulator); + + ov5640_power_down(1); +} + + sensor->on = on; + + return 0; +} + +/*! + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + int ret = 0; + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cparm->capability = sensor->streamcap.capability; + cparm->timeperframe = sensor->streamcap.timeperframe; + cparm->capturemode = sensor->streamcap.capturemode; + ret = 0; + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + u32 tgt_fps; /* target frames per secound */ + enum ov5640_frame_rate frame_rate; + int ret = 0; + + /* Make sure power on */ + ov5640_power_down(0); + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + /* Check that the new frame rate is allowed. */ + if ((timeperframe->numerator == 0) || + (timeperframe->denominator == 0)) { + timeperframe->denominator = DEFAULT_FPS; + timeperframe->numerator = 1; + } + + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps > MAX_FPS) { + timeperframe->denominator = MAX_FPS; + timeperframe->numerator = 1; + } else if (tgt_fps < MIN_FPS) { + timeperframe->denominator = MIN_FPS; + timeperframe->numerator = 1; + } + + /* Actual frame rate we use */ + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps == 15) + frame_rate = ov5640_15_fps; + else if (tgt_fps == 30) + frame_rate = ov5640_30_fps; + else { + pr_err(" The camera frame rate is not supported!\n"); + return -EINVAL; + } + + ret = ov5640_change_mode(frame_rate, + a->parm.capture.capturemode); + if (ret < 0) + return ret; + + sensor->streamcap.timeperframe = *timeperframe; + sensor->streamcap.capturemode = a->parm.capture.capturemode; + + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + pr_debug(" type is not " \ + "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n", + a->type); + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct sensor_data *sensor = s->priv; + + f->fmt.pix = sensor->pix; + + return 0; +} + +/*! + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int ret = 0; + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + vc->value = ov5640_data.brightness; + break; + case V4L2_CID_HUE: + vc->value = ov5640_data.hue; + break; + case V4L2_CID_CONTRAST: + vc->value = ov5640_data.contrast; + break; + case V4L2_CID_SATURATION: + vc->value = ov5640_data.saturation; + break; + case V4L2_CID_RED_BALANCE: + vc->value = ov5640_data.red; + break; + case V4L2_CID_BLUE_BALANCE: + vc->value = ov5640_data.blue; + break; + case V4L2_CID_EXPOSURE: + vc->value = ov5640_data.ae_mode; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/*! + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = 0; + + pr_debug("In ov5640:ioctl_s_ctrl %d\n", + vc->id); + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + break; + case V4L2_CID_CONTRAST: + break; + case V4L2_CID_SATURATION: + break; + case V4L2_CID_HUE: + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + break; + case V4L2_CID_DO_WHITE_BALANCE: + break; + case V4L2_CID_RED_BALANCE: + break; + case V4L2_CID_BLUE_BALANCE: + break; + case V4L2_CID_GAMMA: + break; + case V4L2_CID_EXPOSURE: + break; + case V4L2_CID_AUTOGAIN: + break; + case V4L2_CID_GAIN: + break; + case V4L2_CID_HFLIP: + break; + case V4L2_CID_VFLIP: + break; + default: + retval = -EPERM; + break; + } + + return retval; +} + +/*! + * ioctl_enum_framesizes - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMESIZES ioctl + * @s: pointer to standard V4L2 device structure + * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index > ov5640_mode_MAX) + return -EINVAL; + + fsize->pixel_format = ov5640_data.pix.pixelformat; + fsize->discrete.width = + max(ov5640_mode_info_data[0][fsize->index].width, + ov5640_mode_info_data[1][fsize->index].width); + fsize->discrete.height = + max(ov5640_mode_info_data[0][fsize->index].height, + ov5640_mode_info_data[1][fsize->index].height); + return 0; +} + +/*! + * ioctl_enum_frameintervals - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMEINTERVALS ioctl + * @s: pointer to standard V4L2 device structure + * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_frameintervals(struct v4l2_int_device *s, + struct v4l2_frmivalenum *fival) +{ + int i, j, count; + + if (fival->index < 0 || fival->index > ov5640_mode_MAX) + return -EINVAL; + + if (fival->width == 0 || fival->height == 0 || + fival->pixel_format == 0) { + pr_warning("Please assign pixelformat, width and height.\n"); + return -EINVAL; + } + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1; + + count = 0; + for (i = 0; i < ARRAY_SIZE(ov5640_mode_info_data); i++) { + for (j = 0; j < (ov5640_mode_MAX + 1); j++) { + if (fival->pixel_format == ov5640_data.pix.pixelformat + && fival->width == ov5640_mode_info_data[i][j].width + && fival->height == ov5640_mode_info_data[i][j].height + && ov5640_mode_info_data[i][j].init_data_ptr != NULL) { + count++; + } + if (fival->index == (count - 1)) { + fival->discrete.denominator = + ov5640_framerates[i]; + return 0; + } + } + } + + return -EINVAL; +} + +/*! + * ioctl_g_chip_ident - V4L2 sensor interface handler for + * VIDIOC_DBG_G_CHIP_IDENT ioctl + * @s: pointer to standard V4L2 device structure + * @id: pointer to int + * + * Return 0. + */ +static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) +{ + ((struct v4l2_dbg_chip_ident *)id)->match.type = + V4L2_CHIP_MATCH_I2C_DRIVER; + strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, "ov5640_camera"); + + return 0; +} + +/*! + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + + return 0; +} + +/*! + * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT + * @s: pointer to standard V4L2 device structure + * @fmt: pointer to standard V4L2 fmt description structure + * + * Return 0. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index > ov5640_mode_MAX) + return -EINVAL; + + fmt->pixelformat = ov5640_data.pix.pixelformat; + + return 0; +} + +/*! + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct sensor_data *sensor = s->priv; + u32 tgt_xclk; /* target xclk */ + u32 tgt_fps; /* target frames per secound */ + enum ov5640_frame_rate frame_rate; + int ret; + + ov5640_data.on = true; + + /* mclk */ + tgt_xclk = ov5640_data.mclk; + tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX); + tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN); + ov5640_data.mclk = tgt_xclk; + + pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000); + clk_set_rate(ov5640_data.sensor_clk, ov5640_data.mclk); + + /* Default camera frame rate is set in probe */ + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 15) + frame_rate = ov5640_15_fps; + else if (tgt_fps == 30) + frame_rate = ov5640_30_fps; + else + return -EINVAL; /* Only support 15fps or 30fps now. */ + + ret = ov5640_init_mode(); + return ret; +} + +/*! + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the device when slave detaches to the master. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/*! + * This structure defines all the ioctls for this module and links them to the + * enumeration. + */ +static struct v4l2_int_ioctl_desc ov5640_ioctl_desc[] = { + { vidioc_int_dev_init_num, + (v4l2_int_ioctl_func *)ioctl_dev_init }, + { vidioc_int_dev_exit_num, + ioctl_dev_exit}, + { vidioc_int_s_power_num, + (v4l2_int_ioctl_func *)ioctl_s_power }, + { vidioc_int_g_ifparm_num, + (v4l2_int_ioctl_func *)ioctl_g_ifparm }, + { vidioc_int_init_num, + (v4l2_int_ioctl_func *)ioctl_init }, + { vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, + { vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, + { vidioc_int_g_parm_num, + (v4l2_int_ioctl_func *)ioctl_g_parm }, + { vidioc_int_s_parm_num, + (v4l2_int_ioctl_func *)ioctl_s_parm }, + { vidioc_int_g_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_g_ctrl }, + { vidioc_int_s_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_s_ctrl }, + { vidioc_int_enum_framesizes_num, + (v4l2_int_ioctl_func *)ioctl_enum_framesizes }, + { vidioc_int_enum_frameintervals_num, + (v4l2_int_ioctl_func *)ioctl_enum_frameintervals }, + { vidioc_int_g_chip_ident_num, + (v4l2_int_ioctl_func *)ioctl_g_chip_ident }, +}; + +static struct v4l2_int_slave ov5640_slave = { + .ioctls = ov5640_ioctl_desc, + .num_ioctls = ARRAY_SIZE(ov5640_ioctl_desc), +}; + +static struct v4l2_int_device ov5640_int_device = { + .module = THIS_MODULE, + .name = "ov5640", + .type = v4l2_int_type_slave, + .u = { + .slave = &ov5640_slave, + }, +}; + +/*! + * ov5640 I2C probe function + * + * @param adapter struct i2c_adapter * + * @return Error code indicating success or failure + */ +static int ov5640_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pinctrl *pinctrl; + struct device *dev = &client->dev; + int retval; + u8 chip_id_high, chip_id_low; + + /* ov5640 pinctrl */ + pinctrl = devm_pinctrl_get_select_default(dev); + if (IS_ERR(pinctrl)) { + dev_err(dev, "setup pinctrl failed\n"); + return PTR_ERR(pinctrl); + } + + /* request power down pin */ + pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); + if (!gpio_is_valid(pwn_gpio)) { + dev_err(dev, "no sensor pwdn pin available\n"); + return -ENODEV; + } + retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, + "ov5640_pwdn"); + if (retval < 0) + return retval; + + /* request reset pin */ + rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); + if (!gpio_is_valid(rst_gpio)) { + dev_err(dev, "no sensor reset pin available\n"); + return -EINVAL; + } + retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, + "ov5640_reset"); + if (retval < 0) + return retval; + + /* Set initial values for the sensor struct. */ + memset(&ov5640_data, 0, sizeof(ov5640_data)); + ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); + if (IS_ERR(ov5640_data.sensor_clk)) { + dev_err(dev, "get mclk failed\n"); + return PTR_ERR(ov5640_data.sensor_clk); + } + + retval = of_property_read_u32(dev->of_node, "mclk", + &ov5640_data.mclk); + if (retval) { + dev_err(dev, "mclk frequency is invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "mclk_source", + (u32 *) &(ov5640_data.mclk_source)); + if (retval) { + dev_err(dev, "mclk_source invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "csi_id", + &(ov5640_data.csi)); + if (retval) { + dev_err(dev, "csi_id invalid\n"); + return retval; + } + + clk_prepare_enable(ov5640_data.sensor_clk); + + ov5640_data.io_init = ov5640_reset; + ov5640_data.i2c_client = client; + ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV; + ov5640_data.pix.width = 640; + ov5640_data.pix.height = 480; + ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | + V4L2_CAP_TIMEPERFRAME; + ov5640_data.streamcap.capturemode = 0; + ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS; + ov5640_data.streamcap.timeperframe.numerator = 1; + + ov5640_regulator_enable(&client->dev); + + ov5640_reset(); + + ov5640_power_down(0); + + retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high); + if (retval < 0 || chip_id_high != 0x56) { + clk_disable_unprepare(ov5640_data.sensor_clk); + pr_warning("camera ov5640 is not found\n"); + return -ENODEV; + } + retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low); + if (retval < 0 || chip_id_low != 0x40) { + clk_disable_unprepare(ov5640_data.sensor_clk); + pr_warning("camera ov5640 is not found\n"); + return -ENODEV; + } + + ov5640_power_down(1); + + clk_disable_unprepare(ov5640_data.sensor_clk); + + ov5640_int_device.priv = &ov5640_data; + retval = v4l2_int_device_register(&ov5640_int_device); + + pr_info("camera ov5640 is found\n"); + return retval; +} + +/*! + * ov5640 I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int ov5640_remove(struct i2c_client *client) +{ + v4l2_int_device_unregister(&ov5640_int_device); + + if (analog_regulator) + regulator_disable(analog_regulator); + + if (core_regulator) + regulator_disable(core_regulator); + + if (io_regulator) + regulator_disable(io_regulator); + + return 0; +} + +module_i2c_driver(ov5640_i2c_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("OV5640 Camera Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("CSI"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ov5640_mipi.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ov5640_mipi.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ov5640_mipi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ov5640_mipi.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,2104 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" + +#define OV5640_VOLTAGE_ANALOG 2800000 +#define OV5640_VOLTAGE_DIGITAL_CORE 1500000 +#define OV5640_VOLTAGE_DIGITAL_IO 1800000 + +#define MIN_FPS 15 +#define MAX_FPS 30 +#define DEFAULT_FPS 30 + +#define OV5640_XCLK_MIN 6000000 +#define OV5640_XCLK_MAX 24000000 + +#define OV5640_CHIP_ID_HIGH_BYTE 0x300A +#define OV5640_CHIP_ID_LOW_BYTE 0x300B + +enum ov5640_mode { + ov5640_mode_MIN = 0, + ov5640_mode_VGA_640_480 = 0, + ov5640_mode_QVGA_320_240 = 1, + ov5640_mode_NTSC_720_480 = 2, + ov5640_mode_PAL_720_576 = 3, + ov5640_mode_720P_1280_720 = 4, + ov5640_mode_1080P_1920_1080 = 5, + ov5640_mode_QSXGA_2592_1944 = 6, + ov5640_mode_QCIF_176_144 = 7, + ov5640_mode_XGA_1024_768 = 8, + ov5640_mode_MAX = 8, + ov5640_mode_INIT = 0xff, /*only for sensor init*/ +}; + +enum ov5640_frame_rate { + ov5640_15_fps, + ov5640_30_fps +}; + +/* image size under 1280 * 960 are SUBSAMPLING + * image size upper 1280 * 960 are SCALING + */ +enum ov5640_downsize_mode { + SUBSAMPLING, + SCALING, +}; + +struct reg_value { + u16 u16RegAddr; + u8 u8Val; + u8 u8Mask; + u32 u32Delay_ms; +}; + +struct ov5640_mode_info { + enum ov5640_mode mode; + enum ov5640_downsize_mode dn_mode; + u32 width; + u32 height; + struct reg_value *init_data_ptr; + u32 init_data_size; +}; + +/*! + * Maintains the information on the current state of the sesor. + */ +static struct sensor_data ov5640_data; +static int pwn_gpio, rst_gpio; + +static struct reg_value ov5640_init_setting_30fps_VGA[] = { + + {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, + {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, + {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, + {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0}, + {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, + {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, + {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, + {0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, + {0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, + {0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, + {0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0}, + {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0}, + {0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0}, + {0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0}, + {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, + {0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, + {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, + {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, + {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0}, + {0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0}, + {0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0}, + {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, + {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, + {0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, + {0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0}, + {0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0}, + {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0}, + {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0}, + {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0}, + {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0}, + {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0}, + {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0}, + {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0}, + {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0}, + {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0}, + {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0}, + {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0}, + {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0}, + {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0}, + {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0}, + {0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0}, + {0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0}, + {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0}, + {0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0}, + {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0}, + {0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0}, + {0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0}, + {0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0}, + {0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0}, + {0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0}, + {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0}, + {0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0}, + {0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0}, + {0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0}, + {0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0}, + {0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0}, + {0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0}, + {0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0}, + {0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0}, + {0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0}, + {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, + {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, + {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300}, +}; + +static struct reg_value ov5640_setting_30fps_VGA_640_480[] = { + + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_VGA_640_480[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { + + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, + {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, + {0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0}, + {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { + {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; +static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { + {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_PAL_720_576[] = { + {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_PAL_720_576[] = { + {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_720P_1280_720[] = { + {0x3008, 0x42, 0, 0}, + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, + {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, + {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, + {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, + {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0}, + {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_720P_1280_720[] = { + {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, + {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, + {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, + {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, + {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, +}; + +static struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { + {0x3008, 0x42, 0, 0}, + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0}, + {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, + {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, + {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, + {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, + {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, + {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, + {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, + {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, + {0x3503, 0, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { + {0x3008, 0x42, 0, 0}, + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0}, + {0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, + {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, + {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, + {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, + {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, + {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, + {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, + {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, + {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, +}; + +static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { + {0x4202, 0x0f, 0, 0}, /* stream off the sensor */ + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, /*disable flip*/ + {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, + {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, + {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, + {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, + {0x4202, 0x00, 0, 0}, /* stream on the sensor */ +}; + +static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = { + { + {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480, + ov5640_setting_15fps_VGA_640_480, + ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, + {ov5640_mode_QVGA_320_240, SUBSAMPLING, 320, 240, + ov5640_setting_15fps_QVGA_320_240, + ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, + {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480, + ov5640_setting_15fps_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, + {ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576, + ov5640_setting_15fps_PAL_720_576, + ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, + {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720, + ov5640_setting_15fps_720P_1280_720, + ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, + {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080, + ov5640_setting_15fps_1080P_1920_1080, + ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, + {ov5640_mode_QSXGA_2592_1944, SCALING, 2592, 1944, + ov5640_setting_15fps_QSXGA_2592_1944, + ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, + {ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144, + ov5640_setting_15fps_QCIF_176_144, + ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, + {ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, + ov5640_setting_15fps_XGA_1024_768, + ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, + }, + { + {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480, + ov5640_setting_30fps_VGA_640_480, + ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, + {ov5640_mode_QVGA_320_240, SUBSAMPLING, 320, 240, + ov5640_setting_30fps_QVGA_320_240, + ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, + {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480, + ov5640_setting_30fps_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, + {ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576, + ov5640_setting_30fps_PAL_720_576, + ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, + {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720, + ov5640_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, + {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080, + ov5640_setting_30fps_1080P_1920_1080, + ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, + {ov5640_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0}, + {ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144, + ov5640_setting_30fps_QCIF_176_144, + ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, + {ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, + ov5640_setting_30fps_XGA_1024_768, + ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, + }, +}; + +static struct regulator *io_regulator; +static struct regulator *core_regulator; +static struct regulator *analog_regulator; +static struct regulator *gpo_regulator; + +static int ov5640_probe(struct i2c_client *adapter, + const struct i2c_device_id *device_id); +static int ov5640_remove(struct i2c_client *client); + +static s32 ov5640_read_reg(u16 reg, u8 *val); +static s32 ov5640_write_reg(u16 reg, u8 val); + +static const struct i2c_device_id ov5640_id[] = { + {"ov5640_mipi", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ov5640_id); + +static struct i2c_driver ov5640_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ov5640_mipi", + }, + .probe = ov5640_probe, + .remove = ov5640_remove, + .id_table = ov5640_id, +}; + +static void ov5640_standby(s32 enable) +{ + if (enable) + gpio_set_value(pwn_gpio, 1); + else + gpio_set_value(pwn_gpio, 0); + + msleep(2); +} + +static void ov5640_reset(void) +{ + /* camera reset */ + gpio_set_value(rst_gpio, 1); + + /* camera power dowmn */ + gpio_set_value(pwn_gpio, 1); + msleep(5); + + gpio_set_value(pwn_gpio, 0); + msleep(5); + + gpio_set_value(rst_gpio, 0); + msleep(1); + + gpio_set_value(rst_gpio, 1); + msleep(5); + + gpio_set_value(pwn_gpio, 1); +} + +static int ov5640_power_on(struct device *dev) +{ + int ret = 0; + + io_regulator = devm_regulator_get(dev, "DOVDD"); + if (!IS_ERR(io_regulator)) { + regulator_set_voltage(io_regulator, + OV5640_VOLTAGE_DIGITAL_IO, + OV5640_VOLTAGE_DIGITAL_IO); + ret = regulator_enable(io_regulator); + if (ret) { + pr_err("%s:io set voltage error\n", __func__); + return ret; + } else { + dev_dbg(dev, + "%s:io set voltage ok\n", __func__); + } + } else { + pr_err("%s: cannot get io voltage error\n", __func__); + io_regulator = NULL; + } + + core_regulator = devm_regulator_get(dev, "DVDD"); + if (!IS_ERR(core_regulator)) { + regulator_set_voltage(core_regulator, + OV5640_VOLTAGE_DIGITAL_CORE, + OV5640_VOLTAGE_DIGITAL_CORE); + ret = regulator_enable(core_regulator); + if (ret) { + pr_err("%s:core set voltage error\n", __func__); + return ret; + } else { + dev_dbg(dev, + "%s:core set voltage ok\n", __func__); + } + } else { + core_regulator = NULL; + pr_err("%s: cannot get core voltage error\n", __func__); + } + + analog_regulator = devm_regulator_get(dev, "AVDD"); + if (!IS_ERR(analog_regulator)) { + regulator_set_voltage(analog_regulator, + OV5640_VOLTAGE_ANALOG, + OV5640_VOLTAGE_ANALOG); + ret = regulator_enable(analog_regulator); + if (ret) { + pr_err("%s:analog set voltage error\n", + __func__); + return ret; + } else { + dev_dbg(dev, + "%s:analog set voltage ok\n", __func__); + } + } else { + analog_regulator = NULL; + pr_err("%s: cannot get analog voltage error\n", __func__); + } + + return ret; +} + +static s32 ov5640_write_reg(u16 reg, u8 val) +{ + u8 au8Buf[3] = {0}; + + au8Buf[0] = reg >> 8; + au8Buf[1] = reg & 0xff; + au8Buf[2] = val; + + if (i2c_master_send(ov5640_data.i2c_client, au8Buf, 3) < 0) { + pr_err("%s:write reg error:reg=%x,val=%x\n", + __func__, reg, val); + return -1; + } + + return 0; +} + +static s32 ov5640_read_reg(u16 reg, u8 *val) +{ + u8 au8RegBuf[2] = {0}; + u8 u8RdVal = 0; + + au8RegBuf[0] = reg >> 8; + au8RegBuf[1] = reg & 0xff; + + if (2 != i2c_master_send(ov5640_data.i2c_client, au8RegBuf, 2)) { + pr_err("%s:write reg error:reg=%x\n", + __func__, reg); + return -1; + } + + if (1 != i2c_master_recv(ov5640_data.i2c_client, &u8RdVal, 1)) { + pr_err("%s:read reg error:reg=%x,val=%x\n", + __func__, reg, u8RdVal); + return -1; + } + + *val = u8RdVal; + + return u8RdVal; +} + +static int prev_sysclk, prev_HTS; +static int AE_low, AE_high, AE_Target = 52; + +void OV5640_stream_on(void) +{ + ov5640_write_reg(0x4202, 0x00); +} + +void OV5640_stream_off(void) +{ + ov5640_write_reg(0x4202, 0x0f); +} + + +int OV5640_get_sysclk(void) +{ + /* calculate sysclk */ + int xvclk = ov5640_data.mclk / 10000; + int temp1, temp2; + int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv; + int Bit_div2x = 1, sclk_rdiv, sysclk; + u8 temp; + + int sclk_rdiv_map[] = {1, 2, 4, 8}; + + temp1 = ov5640_read_reg(0x3034, &temp); + temp2 = temp1 & 0x0f; + if (temp2 == 8 || temp2 == 10) + Bit_div2x = temp2 / 2; + + temp1 = ov5640_read_reg(0x3035, &temp); + SysDiv = temp1>>4; + if (SysDiv == 0) + SysDiv = 16; + + temp1 = ov5640_read_reg(0x3036, &temp); + Multiplier = temp1; + + temp1 = ov5640_read_reg(0x3037, &temp); + PreDiv = temp1 & 0x0f; + Pll_rdiv = ((temp1 >> 4) & 0x01) + 1; + + temp1 = ov5640_read_reg(0x3108, &temp); + temp2 = temp1 & 0x03; + sclk_rdiv = sclk_rdiv_map[temp2]; + + VCO = xvclk * Multiplier / PreDiv; + + sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv; + + return sysclk; +} + +void OV5640_set_night_mode(void) +{ + /* read HTS from register settings */ + u8 mode; + + ov5640_read_reg(0x3a00, &mode); + mode &= 0xfb; + ov5640_write_reg(0x3a00, mode); +} + +int OV5640_get_HTS(void) +{ + /* read HTS from register settings */ + int HTS; + u8 temp; + + HTS = ov5640_read_reg(0x380c, &temp); + HTS = (HTS<<8) + ov5640_read_reg(0x380d, &temp); + + return HTS; +} + +int OV5640_get_VTS(void) +{ + /* read VTS from register settings */ + int VTS; + u8 temp; + + /* total vertical size[15:8] high byte */ + VTS = ov5640_read_reg(0x380e, &temp); + + VTS = (VTS<<8) + ov5640_read_reg(0x380f, &temp); + + return VTS; +} + +int OV5640_set_VTS(int VTS) +{ + /* write VTS to registers */ + int temp; + + temp = VTS & 0xff; + ov5640_write_reg(0x380f, temp); + + temp = VTS>>8; + ov5640_write_reg(0x380e, temp); + + return 0; +} + +int OV5640_get_shutter(void) +{ + /* read shutter, in number of line period */ + int shutter; + u8 temp; + + shutter = (ov5640_read_reg(0x03500, &temp) & 0x0f); + shutter = (shutter<<8) + ov5640_read_reg(0x3501, &temp); + shutter = (shutter<<4) + (ov5640_read_reg(0x3502, &temp)>>4); + + return shutter; +} + +int OV5640_set_shutter(int shutter) +{ + /* write shutter, in number of line period */ + int temp; + + shutter = shutter & 0xffff; + + temp = shutter & 0x0f; + temp = temp<<4; + ov5640_write_reg(0x3502, temp); + + temp = shutter & 0xfff; + temp = temp>>4; + ov5640_write_reg(0x3501, temp); + + temp = shutter>>12; + ov5640_write_reg(0x3500, temp); + + return 0; +} + +int OV5640_get_gain16(void) +{ + /* read gain, 16 = 1x */ + int gain16; + u8 temp; + + gain16 = ov5640_read_reg(0x350a, &temp) & 0x03; + gain16 = (gain16<<8) + ov5640_read_reg(0x350b, &temp); + + return gain16; +} + +int OV5640_set_gain16(int gain16) +{ + /* write gain, 16 = 1x */ + u8 temp; + gain16 = gain16 & 0x3ff; + + temp = gain16 & 0xff; + ov5640_write_reg(0x350b, temp); + + temp = gain16>>8; + ov5640_write_reg(0x350a, temp); + + return 0; +} + +int OV5640_get_light_freq(void) +{ + /* get banding filter value */ + int temp, temp1, light_freq = 0; + u8 tmp; + + temp = ov5640_read_reg(0x3c01, &tmp); + + if (temp & 0x80) { + /* manual */ + temp1 = ov5640_read_reg(0x3c00, &tmp); + if (temp1 & 0x04) { + /* 50Hz */ + light_freq = 50; + } else { + /* 60Hz */ + light_freq = 60; + } + } else { + /* auto */ + temp1 = ov5640_read_reg(0x3c0c, &tmp); + if (temp1 & 0x01) { + /* 50Hz */ + light_freq = 50; + } else { + /* 60Hz */ + } + } + return light_freq; +} + +void OV5640_set_bandingfilter(void) +{ + int prev_VTS; + int band_step60, max_band60, band_step50, max_band50; + + /* read preview PCLK */ + prev_sysclk = OV5640_get_sysclk(); + /* read preview HTS */ + prev_HTS = OV5640_get_HTS(); + + /* read preview VTS */ + prev_VTS = OV5640_get_VTS(); + + /* calculate banding filter */ + /* 60Hz */ + band_step60 = prev_sysclk * 100/prev_HTS * 100/120; + ov5640_write_reg(0x3a0a, (band_step60 >> 8)); + ov5640_write_reg(0x3a0b, (band_step60 & 0xff)); + + max_band60 = (int)((prev_VTS-4)/band_step60); + ov5640_write_reg(0x3a0d, max_band60); + + /* 50Hz */ + band_step50 = prev_sysclk * 100/prev_HTS; + ov5640_write_reg(0x3a08, (band_step50 >> 8)); + ov5640_write_reg(0x3a09, (band_step50 & 0xff)); + + max_band50 = (int)((prev_VTS-4)/band_step50); + ov5640_write_reg(0x3a0e, max_band50); +} + +int OV5640_set_AE_target(int target) +{ + /* stable in high */ + int fast_high, fast_low; + AE_low = target * 23 / 25; /* 0.92 */ + AE_high = target * 27 / 25; /* 1.08 */ + + fast_high = AE_high<<1; + if (fast_high > 255) + fast_high = 255; + + fast_low = AE_low >> 1; + + ov5640_write_reg(0x3a0f, AE_high); + ov5640_write_reg(0x3a10, AE_low); + ov5640_write_reg(0x3a1b, AE_high); + ov5640_write_reg(0x3a1e, AE_low); + ov5640_write_reg(0x3a11, fast_high); + ov5640_write_reg(0x3a1f, fast_low); + + return 0; +} + +void OV5640_turn_on_AE_AG(int enable) +{ + u8 ae_ag_ctrl; + + ov5640_read_reg(0x3503, &ae_ag_ctrl); + if (enable) { + /* turn on auto AE/AG */ + ae_ag_ctrl = ae_ag_ctrl & ~(0x03); + } else { + /* turn off AE/AG */ + ae_ag_ctrl = ae_ag_ctrl | 0x03; + } + ov5640_write_reg(0x3503, ae_ag_ctrl); +} + +bool binning_on(void) +{ + u8 temp; + ov5640_read_reg(0x3821, &temp); + temp &= 0xfe; + if (temp) + return true; + else + return false; +} + +static void ov5640_set_virtual_channel(int channel) +{ + u8 channel_id; + + ov5640_read_reg(0x4814, &channel_id); + channel_id &= ~(3 << 6); + ov5640_write_reg(0x4814, channel_id | (channel << 6)); +} + +/* download ov5640 settings to sensor through i2c */ +static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize) +{ + register u32 Delay_ms = 0; + register u16 RegAddr = 0; + register u8 Mask = 0; + register u8 Val = 0; + u8 RegVal = 0; + int i, retval = 0; + + for (i = 0; i < ArySize; ++i, ++pModeSetting) { + Delay_ms = pModeSetting->u32Delay_ms; + RegAddr = pModeSetting->u16RegAddr; + Val = pModeSetting->u8Val; + Mask = pModeSetting->u8Mask; + + if (Mask) { + retval = ov5640_read_reg(RegAddr, &RegVal); + if (retval < 0) + goto err; + + RegVal &= ~(u8)Mask; + Val &= Mask; + Val |= RegVal; + } + + retval = ov5640_write_reg(RegAddr, Val); + if (retval < 0) + goto err; + + if (Delay_ms) + msleep(Delay_ms); + } +err: + return retval; +} + +/* sensor changes between scaling and subsampling + * go through exposure calcualtion + */ +static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate, + enum ov5640_mode mode) +{ + struct reg_value *pModeSetting = NULL; + s32 ArySize = 0; + u8 average; + int prev_shutter, prev_gain16; + int cap_shutter, cap_gain16; + int cap_sysclk, cap_HTS, cap_VTS; + int light_freq, cap_bandfilt, cap_maxband; + long cap_gain16_shutter; + int retval = 0; + + /* check if the input mode and frame rate is valid */ + pModeSetting = + ov5640_mode_info_data[frame_rate][mode].init_data_ptr; + ArySize = + ov5640_mode_info_data[frame_rate][mode].init_data_size; + + ov5640_data.pix.width = + ov5640_mode_info_data[frame_rate][mode].width; + ov5640_data.pix.height = + ov5640_mode_info_data[frame_rate][mode].height; + + if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 || + pModeSetting == NULL || ArySize == 0) + return -EINVAL; + + /* auto focus */ + /* OV5640_auto_focus();//if no af function, just skip it */ + + /* turn off AE/AG */ + OV5640_turn_on_AE_AG(0); + + /* read preview shutter */ + prev_shutter = OV5640_get_shutter(); + if ((binning_on()) && (mode != ov5640_mode_720P_1280_720) + && (mode != ov5640_mode_1080P_1920_1080)) + prev_shutter *= 2; + + /* read preview gain */ + prev_gain16 = OV5640_get_gain16(); + + /* get average */ + ov5640_read_reg(0x56a1, &average); + + /* turn off night mode for capture */ + OV5640_set_night_mode(); + + /* turn off overlay */ + /* ov5640_write_reg(0x3022, 0x06);//if no af function, just skip it */ + + OV5640_stream_off(); + + /* Write capture setting */ + retval = ov5640_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + + /* read capture VTS */ + cap_VTS = OV5640_get_VTS(); + cap_HTS = OV5640_get_HTS(); + cap_sysclk = OV5640_get_sysclk(); + + /* calculate capture banding filter */ + light_freq = OV5640_get_light_freq(); + if (light_freq == 60) { + /* 60Hz */ + cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120; + } else { + /* 50Hz */ + cap_bandfilt = cap_sysclk * 100 / cap_HTS; + } + cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt); + + /* calculate capture shutter/gain16 */ + if (average > AE_low && average < AE_high) { + /* in stable range */ + cap_gain16_shutter = + prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk + * prev_HTS/cap_HTS * AE_Target / average; + } else { + cap_gain16_shutter = + prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk + * prev_HTS/cap_HTS; + } + + /* gain to shutter */ + if (cap_gain16_shutter < (cap_bandfilt * 16)) { + /* shutter < 1/100 */ + cap_shutter = cap_gain16_shutter/16; + if (cap_shutter < 1) + cap_shutter = 1; + + cap_gain16 = cap_gain16_shutter/cap_shutter; + if (cap_gain16 < 16) + cap_gain16 = 16; + } else { + if (cap_gain16_shutter > + (cap_bandfilt * cap_maxband * 16)) { + /* exposure reach max */ + cap_shutter = cap_bandfilt * cap_maxband; + cap_gain16 = cap_gain16_shutter / cap_shutter; + } else { + /* 1/100 < (cap_shutter = n/100) =< max */ + cap_shutter = + ((int) (cap_gain16_shutter/16 / cap_bandfilt)) + *cap_bandfilt; + cap_gain16 = cap_gain16_shutter / cap_shutter; + } + } + + /* write capture gain */ + OV5640_set_gain16(cap_gain16); + + /* write capture shutter */ + if (cap_shutter > (cap_VTS - 4)) { + cap_VTS = cap_shutter + 4; + OV5640_set_VTS(cap_VTS); + } + OV5640_set_shutter(cap_shutter); + + OV5640_stream_on(); + +err: + return retval; +} + +/* if sensor changes inside scaling or subsampling + * change mode directly + * */ +static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate, + enum ov5640_mode mode) +{ + struct reg_value *pModeSetting = NULL; + s32 ArySize = 0; + int retval = 0; + + /* check if the input mode and frame rate is valid */ + pModeSetting = + ov5640_mode_info_data[frame_rate][mode].init_data_ptr; + ArySize = + ov5640_mode_info_data[frame_rate][mode].init_data_size; + + ov5640_data.pix.width = + ov5640_mode_info_data[frame_rate][mode].width; + ov5640_data.pix.height = + ov5640_mode_info_data[frame_rate][mode].height; + + if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 || + pModeSetting == NULL || ArySize == 0) + return -EINVAL; + + /* turn off AE/AG */ + OV5640_turn_on_AE_AG(0); + + OV5640_stream_off(); + + /* Write capture setting */ + retval = ov5640_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + + OV5640_stream_on(); + + OV5640_turn_on_AE_AG(1); + +err: + return retval; +} + +static int ov5640_init_mode(enum ov5640_frame_rate frame_rate, + enum ov5640_mode mode, enum ov5640_mode orig_mode) +{ + struct reg_value *pModeSetting = NULL; + s32 ArySize = 0; + int retval = 0; + void *mipi_csi2_info; + u32 mipi_reg, msec_wait4stable = 0; + enum ov5640_downsize_mode dn_mode, orig_dn_mode; + + if ((mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) + && (mode != ov5640_mode_INIT)) { + pr_err("Wrong ov5640 mode detected!\n"); + return -1; + } + + mipi_csi2_info = mipi_csi2_get_info(); + + /* initial mipi dphy */ + if (!mipi_csi2_info) { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -1; + } + + if (!mipi_csi2_get_status(mipi_csi2_info)) + mipi_csi2_enable(mipi_csi2_info); + + if (!mipi_csi2_get_status(mipi_csi2_info)) { + pr_err("Can not enable mipi csi2 driver!\n"); + return -1; + } + + mipi_csi2_set_lanes(mipi_csi2_info); + + /*Only reset MIPI CSI2 HW at sensor initialize*/ + if (mode == ov5640_mode_INIT) + mipi_csi2_reset(mipi_csi2_info); + + if (ov5640_data.pix.pixelformat == V4L2_PIX_FMT_UYVY) + mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_YUV422); + else if (ov5640_data.pix.pixelformat == V4L2_PIX_FMT_RGB565) + mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_RGB565); + else + pr_err("currently this sensor format can not be supported!\n"); + + dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode; + orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode; + if (mode == ov5640_mode_INIT) { + pModeSetting = ov5640_init_setting_30fps_VGA; + ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA); + + ov5640_data.pix.width = 640; + ov5640_data.pix.height = 480; + retval = ov5640_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + + pModeSetting = ov5640_setting_30fps_VGA_640_480; + ArySize = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480); + retval = ov5640_download_firmware(pModeSetting, ArySize); + } else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) || + (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) { + /* change between subsampling and scaling + * go through exposure calucation */ + retval = ov5640_change_mode_exposure_calc(frame_rate, mode); + } else { + /* change inside subsampling or scaling + * download firmware directly */ + retval = ov5640_change_mode_direct(frame_rate, mode); + } + + if (retval < 0) + goto err; + + OV5640_set_AE_target(AE_Target); + OV5640_get_light_freq(); + OV5640_set_bandingfilter(); + ov5640_set_virtual_channel(ov5640_data.csi); + + /* add delay to wait for sensor stable */ + if (mode == ov5640_mode_QSXGA_2592_1944) { + /* dump the first two frames: 1/7.5*2 + * the frame rate of QSXGA is 7.5fps */ + msec_wait4stable = 267; + } else if (frame_rate == ov5640_15_fps) { + /* dump the first nine frames: 1/15*9 */ + msec_wait4stable = 600; + } else if (frame_rate == ov5640_30_fps) { + /* dump the first nine frames: 1/30*9 */ + msec_wait4stable = 300; + } + msleep(msec_wait4stable); + + if (mipi_csi2_info) { + unsigned int i; + + i = 0; + + /* wait for mipi sensor ready */ + mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); + while ((mipi_reg == 0x200) && (i < 10)) { + mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); + i++; + msleep(10); + } + + if (i >= 10) { + pr_err("mipi csi2 can not receive sensor clk!\n"); + return -1; + } + + i = 0; + + /* wait for mipi stable */ + mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); + while ((mipi_reg != 0x0) && (i < 10)) { + mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); + i++; + msleep(10); + } + + if (i >= 10) { + pr_err("mipi csi2 can not reveive data correctly!\n"); + return -1; + } + } +err: + return retval; +} + +/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ + +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + if (s == NULL) { + pr_err(" ERROR!! no slave device set!\n"); + return -1; + } + + memset(p, 0, sizeof(*p)); + p->u.bt656.clock_curr = ov5640_data.mclk; + pr_debug(" clock_curr=mclk=%d\n", ov5640_data.mclk); + p->if_type = V4L2_IF_TYPE_BT656; + p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; + p->u.bt656.clock_min = OV5640_XCLK_MIN; + p->u.bt656.clock_max = OV5640_XCLK_MAX; + p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ + + return 0; +} + +/*! + * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl + * @s: pointer to standard V4L2 device structure + * @on: indicates power mode (on or off) + * + * Turns the power on or off, depending on the value of on and returns the + * appropriate error code. + */ +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct sensor_data *sensor = s->priv; + + if (on && !sensor->on) { + if (io_regulator) + if (regulator_enable(io_regulator) != 0) + return -EIO; + if (core_regulator) + if (regulator_enable(core_regulator) != 0) + return -EIO; + if (gpo_regulator) + if (regulator_enable(gpo_regulator) != 0) + return -EIO; + if (analog_regulator) + if (regulator_enable(analog_regulator) != 0) + return -EIO; + /* Make sure power on */ + ov5640_standby(0); + } else if (!on && sensor->on) { + if (analog_regulator) + regulator_disable(analog_regulator); + if (core_regulator) + regulator_disable(core_regulator); + if (io_regulator) + regulator_disable(io_regulator); + if (gpo_regulator) + regulator_disable(gpo_regulator); + + ov5640_standby(1); + } + + sensor->on = on; + + return 0; +} + +/*! + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + int ret = 0; + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cparm->capability = sensor->streamcap.capability; + cparm->timeperframe = sensor->streamcap.timeperframe; + cparm->capturemode = sensor->streamcap.capturemode; + ret = 0; + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + u32 tgt_fps; /* target frames per secound */ + enum ov5640_frame_rate frame_rate; + enum ov5640_mode orig_mode; + int ret = 0; + + /* Make sure power on */ + ov5640_standby(0); + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + /* Check that the new frame rate is allowed. */ + if ((timeperframe->numerator == 0) || + (timeperframe->denominator == 0)) { + timeperframe->denominator = DEFAULT_FPS; + timeperframe->numerator = 1; + } + + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps > MAX_FPS) { + timeperframe->denominator = MAX_FPS; + timeperframe->numerator = 1; + } else if (tgt_fps < MIN_FPS) { + timeperframe->denominator = MIN_FPS; + timeperframe->numerator = 1; + } + + /* Actual frame rate we use */ + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps == 15) + frame_rate = ov5640_15_fps; + else if (tgt_fps == 30) + frame_rate = ov5640_30_fps; + else { + pr_err(" The camera frame rate is not supported!\n"); + return -EINVAL; + } + + orig_mode = sensor->streamcap.capturemode; + ret = ov5640_init_mode(frame_rate, + (u32)a->parm.capture.capturemode, orig_mode); + if (ret < 0) + return ret; + + sensor->streamcap.timeperframe = *timeperframe; + sensor->streamcap.capturemode = + (u32)a->parm.capture.capturemode; + + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + pr_debug(" type is not " \ + "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n", + a->type); + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct sensor_data *sensor = s->priv; + + f->fmt.pix = sensor->pix; + + return 0; +} + +/*! + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int ret = 0; + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + vc->value = ov5640_data.brightness; + break; + case V4L2_CID_HUE: + vc->value = ov5640_data.hue; + break; + case V4L2_CID_CONTRAST: + vc->value = ov5640_data.contrast; + break; + case V4L2_CID_SATURATION: + vc->value = ov5640_data.saturation; + break; + case V4L2_CID_RED_BALANCE: + vc->value = ov5640_data.red; + break; + case V4L2_CID_BLUE_BALANCE: + vc->value = ov5640_data.blue; + break; + case V4L2_CID_EXPOSURE: + vc->value = ov5640_data.ae_mode; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/*! + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = 0; + + pr_debug("In ov5640:ioctl_s_ctrl %d\n", + vc->id); + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + break; + case V4L2_CID_CONTRAST: + break; + case V4L2_CID_SATURATION: + break; + case V4L2_CID_HUE: + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + break; + case V4L2_CID_DO_WHITE_BALANCE: + break; + case V4L2_CID_RED_BALANCE: + break; + case V4L2_CID_BLUE_BALANCE: + break; + case V4L2_CID_GAMMA: + break; + case V4L2_CID_EXPOSURE: + break; + case V4L2_CID_AUTOGAIN: + break; + case V4L2_CID_GAIN: + break; + case V4L2_CID_HFLIP: + break; + case V4L2_CID_VFLIP: + break; + default: + retval = -EPERM; + break; + } + + return retval; +} + +/*! + * ioctl_enum_framesizes - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMESIZES ioctl + * @s: pointer to standard V4L2 device structure + * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index > ov5640_mode_MAX) + return -EINVAL; + + fsize->pixel_format = ov5640_data.pix.pixelformat; + fsize->discrete.width = + max(ov5640_mode_info_data[0][fsize->index].width, + ov5640_mode_info_data[1][fsize->index].width); + fsize->discrete.height = + max(ov5640_mode_info_data[0][fsize->index].height, + ov5640_mode_info_data[1][fsize->index].height); + return 0; +} + +/*! + * ioctl_g_chip_ident - V4L2 sensor interface handler for + * VIDIOC_DBG_G_CHIP_IDENT ioctl + * @s: pointer to standard V4L2 device structure + * @id: pointer to int + * + * Return 0. + */ +static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) +{ + ((struct v4l2_dbg_chip_ident *)id)->match.type = + V4L2_CHIP_MATCH_I2C_DRIVER; + strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, + "ov5640_mipi_camera"); + + return 0; +} + +/*! + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + + return 0; +} + +/*! + * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT + * @s: pointer to standard V4L2 device structure + * @fmt: pointer to standard V4L2 fmt description structure + * + * Return 0. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index > ov5640_mode_MAX) + return -EINVAL; + + fmt->pixelformat = ov5640_data.pix.pixelformat; + + return 0; +} + +/*! + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct sensor_data *sensor = s->priv; + u32 tgt_xclk; /* target xclk */ + u32 tgt_fps; /* target frames per secound */ + int ret; + enum ov5640_frame_rate frame_rate; + void *mipi_csi2_info; + + ov5640_data.on = true; + + /* mclk */ + tgt_xclk = ov5640_data.mclk; + tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX); + tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN); + ov5640_data.mclk = tgt_xclk; + + pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000); + + /* Default camera frame rate is set in probe */ + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 15) + frame_rate = ov5640_15_fps; + else if (tgt_fps == 30) + frame_rate = ov5640_30_fps; + else + return -EINVAL; /* Only support 15fps or 30fps now. */ + + mipi_csi2_info = mipi_csi2_get_info(); + + /* enable mipi csi2 */ + if (mipi_csi2_info) + mipi_csi2_enable(mipi_csi2_info); + else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } + + ret = ov5640_init_mode(frame_rate, ov5640_mode_INIT, ov5640_mode_INIT); + + return ret; +} + +/*! + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the device when slave detaches to the master. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + void *mipi_csi2_info; + + mipi_csi2_info = mipi_csi2_get_info(); + + /* disable mipi csi2 */ + if (mipi_csi2_info) + if (mipi_csi2_get_status(mipi_csi2_info)) + mipi_csi2_disable(mipi_csi2_info); + + return 0; +} + +/*! + * This structure defines all the ioctls for this module and links them to the + * enumeration. + */ +static struct v4l2_int_ioctl_desc ov5640_ioctl_desc[] = { + {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *) ioctl_dev_init}, + {vidioc_int_dev_exit_num, ioctl_dev_exit}, + {vidioc_int_s_power_num, (v4l2_int_ioctl_func *) ioctl_s_power}, + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *) ioctl_g_ifparm}, +/* {vidioc_int_g_needs_reset_num, + (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */ +/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */ + {vidioc_int_init_num, (v4l2_int_ioctl_func *) ioctl_init}, + {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, +/* {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */ + {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, +/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, */ + {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, + {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, +/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */ + {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, + {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, + {vidioc_int_enum_framesizes_num, + (v4l2_int_ioctl_func *) ioctl_enum_framesizes}, + {vidioc_int_g_chip_ident_num, + (v4l2_int_ioctl_func *) ioctl_g_chip_ident}, +}; + +static struct v4l2_int_slave ov5640_slave = { + .ioctls = ov5640_ioctl_desc, + .num_ioctls = ARRAY_SIZE(ov5640_ioctl_desc), +}; + +static struct v4l2_int_device ov5640_int_device = { + .module = THIS_MODULE, + .name = "ov5640", + .type = v4l2_int_type_slave, + .u = { + .slave = &ov5640_slave, + }, +}; + +/*! + * ov5640 I2C probe function + * + * @param adapter struct i2c_adapter * + * @return Error code indicating success or failure + */ +static int ov5640_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + int retval; + u8 chip_id_high, chip_id_low; + + /* request power down pin */ + pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); + if (!gpio_is_valid(pwn_gpio)) { + dev_warn(dev, "no sensor pwdn pin available"); + return -EINVAL; + } + retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, + "ov5640_mipi_pwdn"); + if (retval < 0) + return retval; + + /* request reset pin */ + rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); + if (!gpio_is_valid(rst_gpio)) { + dev_warn(dev, "no sensor reset pin available"); + return -EINVAL; + } + retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, + "ov5640_mipi_reset"); + if (retval < 0) + return retval; + + /* Set initial values for the sensor struct. */ + memset(&ov5640_data, 0, sizeof(ov5640_data)); + ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); + if (IS_ERR(ov5640_data.sensor_clk)) { + /* assuming clock enabled by default */ + ov5640_data.sensor_clk = NULL; + dev_err(dev, "clock-frequency missing or invalid\n"); + return PTR_ERR(ov5640_data.sensor_clk); + } + + retval = of_property_read_u32(dev->of_node, "mclk", + &(ov5640_data.mclk)); + if (retval) { + dev_err(dev, "mclk missing or invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "mclk_source", + (u32 *) &(ov5640_data.mclk_source)); + if (retval) { + dev_err(dev, "mclk_source missing or invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "csi_id", + &(ov5640_data.csi)); + if (retval) { + dev_err(dev, "csi id missing or invalid\n"); + return retval; + } + + clk_prepare_enable(ov5640_data.sensor_clk); + + ov5640_data.io_init = ov5640_reset; + ov5640_data.i2c_client = client; + ov5640_data.pix.pixelformat = V4L2_PIX_FMT_UYVY; + ov5640_data.pix.width = 640; + ov5640_data.pix.height = 480; + ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | + V4L2_CAP_TIMEPERFRAME; + ov5640_data.streamcap.capturemode = 0; + ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS; + ov5640_data.streamcap.timeperframe.numerator = 1; + + ov5640_power_on(dev); + + ov5640_reset(); + + ov5640_standby(0); + + retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high); + if (retval < 0 || chip_id_high != 0x56) { + pr_warning("camera ov5640_mipi is not found\n"); + clk_disable_unprepare(ov5640_data.sensor_clk); + return -ENODEV; + } + retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low); + if (retval < 0 || chip_id_low != 0x40) { + pr_warning("camera ov5640_mipi is not found\n"); + clk_disable_unprepare(ov5640_data.sensor_clk); + return -ENODEV; + } + + ov5640_standby(1); + + ov5640_int_device.priv = &ov5640_data; + retval = v4l2_int_device_register(&ov5640_int_device); + + clk_disable_unprepare(ov5640_data.sensor_clk); + + pr_info("camera ov5640_mipi is found\n"); + return retval; +} + +/*! + * ov5640 I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int ov5640_remove(struct i2c_client *client) +{ + v4l2_int_device_unregister(&ov5640_int_device); + + if (gpo_regulator) + regulator_disable(gpo_regulator); + + if (analog_regulator) + regulator_disable(analog_regulator); + + if (core_regulator) + regulator_disable(core_regulator); + + if (io_regulator) + regulator_disable(io_regulator); + + return 0; +} + +/*! + * ov5640 init function + * Called by insmod ov5640_camera.ko. + * + * @return Error code indicating success or failure + */ +static __init int ov5640_init(void) +{ + u8 err; + + err = i2c_add_driver(&ov5640_i2c_driver); + if (err != 0) + pr_err("%s:driver registration failed, error=%d\n", + __func__, err); + + return err; +} + +/*! + * OV5640 cleanup function + * Called on rmmod ov5640_camera.ko + * + * @return Error code indicating success or failure + */ +static void __exit ov5640_clean(void) +{ + i2c_del_driver(&ov5640_i2c_driver); +} + +module_init(ov5640_init); +module_exit(ov5640_clean); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("OV5640 MIPI Camera Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("CSI"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/capture/ov5642.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ov5642.c --- linux-3.10.30/drivers/media/platform/mxc/capture/ov5642.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/capture/ov5642.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,4252 @@ +/* + * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" + +#define OV5642_VOLTAGE_ANALOG 2800000 +#define OV5642_VOLTAGE_DIGITAL_CORE 1500000 +#define OV5642_VOLTAGE_DIGITAL_IO 1800000 + +#define MIN_FPS 15 +#define MAX_FPS 30 +#define DEFAULT_FPS 30 + +#define OV5642_XCLK_MIN 6000000 +#define OV5642_XCLK_MAX 24000000 + +#define OV5642_CHIP_ID_HIGH_BYTE 0x300A +#define OV5642_CHIP_ID_LOW_BYTE 0x300B + +enum ov5642_mode { + ov5642_mode_MIN = 0, + ov5642_mode_VGA_640_480 = 0, + ov5642_mode_QVGA_320_240 = 1, + ov5642_mode_NTSC_720_480 = 2, + ov5642_mode_PAL_720_576 = 3, + ov5642_mode_720P_1280_720 = 4, + ov5642_mode_1080P_1920_1080 = 5, + ov5642_mode_QSXGA_2592_1944 = 6, + ov5642_mode_QCIF_176_144 = 7, + ov5642_mode_XGA_1024_768 = 8, + ov5642_mode_MAX = 8 +}; + +enum ov5642_frame_rate { + ov5642_15_fps, + ov5642_30_fps +}; + +static int ov5642_framerates[] = { + [ov5642_15_fps] = 15, + [ov5642_30_fps] = 30, +}; + +struct reg_value { + u16 u16RegAddr; + u8 u8Val; + u8 u8Mask; + u32 u32Delay_ms; +}; + +struct ov5642_mode_info { + enum ov5642_mode mode; + u32 width; + u32 height; + struct reg_value *init_data_ptr; + u32 init_data_size; +}; + +/*! + * Maintains the information on the current state of the sesor. + */ +static struct sensor_data ov5642_data; +static int pwn_gpio, rst_gpio; + +static struct reg_value ov5642_rot_none_VGA[] = { + {0x3818, 0xc1, 0x00, 0x00}, {0x3621, 0x87, 0x00, 0x00}, +}; + +static struct reg_value ov5642_rot_vert_flip_VGA[] = { + {0x3818, 0x20, 0xbf, 0x00}, {0x3621, 0x20, 0xff, 0x00}, +}; + +static struct reg_value ov5642_rot_horiz_flip_VGA[] = { + {0x3818, 0x81, 0x00, 0x01}, {0x3621, 0xa7, 0x00, 0x00}, +}; + +static struct reg_value ov5642_rot_180_VGA[] = { + {0x3818, 0x60, 0xff, 0x00}, {0x3621, 0x00, 0xdf, 0x00}, +}; + + +static struct reg_value ov5642_rot_none_FULL[] = { + {0x3818, 0xc0, 0x00, 0x00}, {0x3621, 0x09, 0x00, 0x00}, +}; + +static struct reg_value ov5642_rot_vert_flip_FULL[] = { + {0x3818, 0x20, 0xbf, 0x01}, {0x3621, 0x20, 0xff, 0x00}, +}; + +static struct reg_value ov5642_rot_horiz_flip_FULL[] = { + {0x3818, 0x80, 0x00, 0x01}, {0x3621, 0x29, 0x00, 0x00}, +}; + +static struct reg_value ov5642_rot_180_FULL[] = { + {0x3818, 0x60, 0xff, 0x00}, {0x3621, 0x00, 0xdf, 0x00}, +}; + + +static struct reg_value ov5642_initial_setting[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, + {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c00, 0x04, 0, 0}, {0x3c01, 0x80, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, + {0x5182, 0x00, 0, 0}, {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, + {0x5001, 0xff, 0, 0}, {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, + {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, + {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, + {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, + {0x380b, 0xe0, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, + {0x350b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, + {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, + {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, + {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, + {0x3801, 0x80, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, + {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, + {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, + {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, + {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, + {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, + {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, + {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, + {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, + {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, + {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, + {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, + {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, + {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, + {0x3a1f, 0x10, 0, 0}, {0x3030, 0x0b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 300}, +}; + +static struct reg_value ov5642_setting_15fps_QCIF_176_144[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, + {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, + {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, + {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, + {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, + {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, + {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, + {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, + {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, + {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, + {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, + {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, + {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, + {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, + {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, + {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, + {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, + {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, + {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, + {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, + {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, + {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, + {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, + {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, + {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, + {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, + {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, + {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, + {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, + {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, + {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, + {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, + {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, + {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, + {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, + {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, + {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, + {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, + {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, + {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, + {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, + {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, + {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, + {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, + {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, + {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, + {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, + {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, + {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, + {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, + {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, + {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, + {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, + {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, + {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, + {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, + {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, + {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, + {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, + {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, + {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, + {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, + {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, + {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, + {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, + {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, + {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, + {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, + {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, + {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, + {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, + {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, + {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, + {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, + {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, + {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, + {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, + {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, + {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, + {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, + {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, + {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, + {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, + {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, + {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, + {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, + {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, + {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, + {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, + {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, + {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, + {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, + {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, + {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, + {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, + {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, + {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, + {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, + {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, + {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, + {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, + {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, + {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, + {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, + {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, + {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, + {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, + {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, + {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, + {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, + {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, + {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, + {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, + {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, + {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, + {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, + {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, + {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, + {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, + {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, + {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, + {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, + {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, + {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, + {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, + {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, + {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, + {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, + {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, + {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, + {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, + {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, + {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0x90, 0, 0}, {0x3a00, 0x78, 0, 0}, +}; + +static struct reg_value ov5642_setting_30fps_QCIF_176_144[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, + {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x10, 0, 0}, + {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, + {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, + {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, + {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, + {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, + {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, + {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, + {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, + {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, + {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, + {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, + {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, + {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, + {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, + {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, + {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, + {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, + {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, + {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, + {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, + {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, + {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, + {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, + {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, + {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, + {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, + {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, + {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, + {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, + {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, + {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, + {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, + {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, + {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, + {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, + {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, + {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, + {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, + {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, + {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, + {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, + {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, + {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, + {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, + {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, + {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, + {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, + {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, + {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, + {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, + {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, + {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, + {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, + {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, + {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, + {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, + {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, + {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, + {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, + {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, + {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, + {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, + {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, + {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, + {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, + {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, + {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, + {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, + {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, + {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, + {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, + {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, + {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, + {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, + {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, + {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, + {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, + {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, + {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, + {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, + {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, + {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, + {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, + {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, + {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, + {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, + {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, + {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, + {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, + {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, + {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, + {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, + {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, + {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, + {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, + {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, + {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, + {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, + {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, + {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, + {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, + {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, + {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, + {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, + {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, + {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, + {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, + {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, + {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, + {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, + {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, + {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, + {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, + {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, + {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, + {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, + {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, + {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, + {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, + {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, + {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, + {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, + {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, + {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, + {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, + {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, + {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, + {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, + {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, + {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, + {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0x90, 0, 0}, {0x3a00, 0x78, 0, 0}, +}; + +static struct reg_value ov5642_setting_15fps_QSXGA_2592_1944[] = { + {0x3503, 0x07, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, + {0x3002, 0x00, 0, 0}, {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, + {0x3005, 0xff, 0, 0}, {0x3006, 0xff, 0, 0}, {0x3007, 0x3f, 0, 0}, + {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3818, 0xc0, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, + {0x3602, 0xe4, 0, 0}, {0x3612, 0xac, 0, 0}, {0x3613, 0x44, 0, 0}, + {0x3622, 0x60, 0, 0}, {0x3623, 0x22, 0, 0}, {0x3604, 0x48, 0, 0}, + {0x3705, 0xda, 0, 0}, {0x370a, 0x80, 0, 0}, {0x3801, 0x95, 0, 0}, + {0x3803, 0x0e, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x20, 0, 0}, + {0x3806, 0x07, 0, 0}, {0x3807, 0x98, 0, 0}, {0x3808, 0x0a, 0, 0}, + {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, + {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0}, + {0x380f, 0xd0, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3815, 0x44, 0, 0}, + {0x3824, 0x11, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0}, + {0x3a00, 0x78, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0}, + {0x5682, 0x0a, 0, 0}, {0x5683, 0x20, 0, 0}, {0x5686, 0x07, 0, 0}, + {0x5687, 0x98, 0, 0}, {0x5001, 0xff, 0, 0}, {0x589b, 0x00, 0, 0}, + {0x589a, 0xc0, 0, 0}, {0x4407, 0x04, 0, 0}, {0x3008, 0x02, 0, 0}, + {0x460b, 0x37, 0, 0}, {0x460c, 0x22, 0, 0}, {0x471d, 0x05, 0, 0}, + {0x4713, 0x03, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x3815, 0x01, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0}, + {0x5002, 0xe0, 0, 0}, {0x530a, 0x01, 0, 0}, {0x530d, 0x10, 0, 0}, + {0x530c, 0x04, 0, 0}, {0x5312, 0x20, 0, 0}, {0x5282, 0x01, 0, 0}, + {0x3010, 0x10, 0, 0}, {0x3012, 0x00, 0, 0}, +}; + + +static struct reg_value ov5642_setting_VGA_2_QVGA[] = { + {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0xf0, 0, 0}, {0x3815, 0x04, 0, 0}, +}; + +static struct reg_value ov5642_setting_QSXGA_2_VGA[] = { + {0x3503, 0x00, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, + {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, + {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x3818, 0xc1, 0, 0}, {0x3621, 0x87, 0, 0}, + {0x350c, 0x03, 0, 0}, {0x350d, 0xe8, 0, 0}, {0x3602, 0xfc, 0, 0}, + {0x3612, 0xff, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3622, 0x60, 0, 0}, + {0x3623, 0x01, 0, 0}, {0x3604, 0x48, 0, 0}, {0x3705, 0xdb, 0, 0}, + {0x370a, 0x81, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, + {0x3807, 0xc0, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, + {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x0c, 0, 0}, + {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, + {0x3810, 0x40, 0, 0}, {0x3815, 0x04, 0, 0}, {0x3824, 0x11, 0, 0}, + {0x3825, 0xb4, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x5682, 0x05, 0, 0}, + {0x5683, 0x00, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, + {0x5001, 0xff, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x4407, 0x0c, 0, 0}, {0x3008, 0x02, 0, 0}, {0x460b, 0x37, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x471d, 0x05, 0, 0}, {0x4713, 0x02, 0, 0}, + {0x471c, 0xd0, 0, 0}, {0x3815, 0x04, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x3002, 0x5c, 0, 0}, {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0}, + {0x530a, 0x01, 0, 0}, {0x530d, 0x0c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x5312, 0x40, 0, 0}, {0x5282, 0x00, 0, 0}, + {0x3012, 0x02, 0, 0}, {0x3010, 0x00, 0, 0}, +}; + +static struct reg_value ov5642_setting_30fps_VGA_640_480[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, + {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, + {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, + {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, + {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, + {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, + {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, + {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, + {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, + {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, + {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, + {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, + {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, + {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, + {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, + {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, + {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, + {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, + {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, + {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, + {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, +}; + +static struct reg_value ov5642_setting_15fps_VGA_640_480[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, + {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, + {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, + {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, + {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x07, 0, 0}, + {0x380f, 0xd0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, + {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, + {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, + {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, + {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, + {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, + {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, + {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, + {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, + {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, + {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, + {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, + {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, + {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, + {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, + {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, +}; + + +static struct reg_value ov5642_setting_30fps_XGA_1024_768[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, + {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, + {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, + {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, + {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, + {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, + {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, + {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, + {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, + {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, + {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, + {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, + {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, + {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, + {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, + {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, + {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, + {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, + {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, + {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, + {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, + {0x380b, 0x00, 0, 0}, {0x3815, 0x02, 0, 0}, {0x302c, 0x60, 0x60, 0}, +}; + +static struct reg_value ov5642_setting_15fps_XGA_1024_768[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, + {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, + {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, + {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, + {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x07, 0, 0}, + {0x380f, 0xd0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, + {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, + {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, + {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, + {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, + {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, + {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, + {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, + {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, + {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, + {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, + {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, + {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, + {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, + {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, + {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, + {0x380b, 0x00, 0, 0}, {0x3815, 0x02, 0, 0}, {0x302c, 0x60, 0x60, 0}, +}; + +static struct reg_value ov5642_setting_30fps_QVGA_320_240[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, + {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, + {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, + {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, + {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, + {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, + {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, + {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, + {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, + {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, + {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, + {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, + {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, + {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, + {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, + {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, + {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, + {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, + {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, + {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, + {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3808, 0x01, 0, 0}, + {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0}, +}; + +static struct reg_value ov5642_setting_30fps_NTSC_720_480[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, + {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, + {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0}, + {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, + {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, + {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, + {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, + {0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, + {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0}, + {0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, + {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, + {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, + {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, + {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, + {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, + {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, + {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, + {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, + {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, + {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, + {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x302c, 0x60, 0x60, 0}, +}; + +static struct reg_value ov5642_setting_30fps_PAL_720_576[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0}, + {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, + {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, + {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0}, + {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xd8, 0, 0}, + {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, + {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0}, + {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, + {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0}, + {0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0}, + {0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x5682, 0x04, 0, 0}, + {0x5683, 0xb0, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0}, + {0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, + {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, + {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, + {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, + {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, + {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, + {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, + {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, + {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, + {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, + {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, + {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, + {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x302c, 0x60, 0x60, 0}, +}; + +static struct reg_value ov5642_setting_15fps_720P_1280_720[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0}, + {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0}, + {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0}, + {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, + {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, + {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0}, + {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, + {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, + {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0}, + {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, + {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0}, + {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0}, + {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0}, + {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0}, + {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0}, + {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, + {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, + {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0}, + {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0}, + {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, + {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0}, + {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, + {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0}, + {0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0}, + {0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0}, + {0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0}, + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0}, + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0}, + {0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0}, + {0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0}, + {0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0}, + {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0}, + {0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0}, + {0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0}, + {0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0}, + {0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, + {0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, + {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0}, {0x3010, 0x30, 0, 0}, + {0x3a08, 0x06, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x05, 0, 0}, + {0x3a0b, 0x50, 0, 0}, {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x07, 0, 0}, +}; + +static struct reg_value ov5642_setting_30fps_720P_1280_720[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0}, + {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0}, + {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0}, + {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, + {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, + {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0}, + {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, + {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, + {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0}, + {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, + {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0}, + {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0}, + {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0}, + {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0}, + {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0}, + {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, + {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, + {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0}, + {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0}, + {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, + {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0}, + {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, + {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0}, + {0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0}, + {0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0}, + {0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0}, + {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, + {0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0}, + {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0}, + {0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0}, + {0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0}, + {0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0}, + {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0}, + {0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0}, + {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0}, + {0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0}, + {0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0}, + {0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, + {0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, + {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0}, +}; + +static struct reg_value ov5642_setting_15fps_1080P_1920_1080[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0}, + {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0}, + {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0}, + {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, + {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, + {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0}, + {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, + {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, + {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0}, + {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0}, + {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0}, + {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0}, + {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0}, + {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, + {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0}, + {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0}, + {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0}, + {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, + {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, + {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0}, + {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, + {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0}, + {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0}, + {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, + {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, + {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, + {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, + {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0}, + {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, + {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0}, + {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, + {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, + {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, + {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, + {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, + {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, + {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, + {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, + {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, + {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, + {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, + {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, + {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, + {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, + {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, + {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, + {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, + {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, + {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, + {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, + {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, + {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, + {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, + {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, + {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, + {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, + {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, + {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, + {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, + {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, + {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, + {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, + {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, + {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, + {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, + {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, + {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, + {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, + {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, + {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, + {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, + {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, + {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, + {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, + {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, + {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, + {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, + {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, + {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, + {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, + {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, + {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, + {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, + {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, + {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, + {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, + {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, + {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, + {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, + {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, + {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, + {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, + {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, + {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, + {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, + {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, + {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, + {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, + {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, + {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, + {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, + {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, + {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, + {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, + {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, + {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, + {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, + {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, + {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, + {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, + {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, + {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, + {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, + {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, + {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, + {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, + {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, + {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, + {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, + {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, + {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, + {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, + {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, + {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, + {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, + {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, + {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, + {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, + {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, + {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, + {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, + {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, + {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, + {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, + {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, + {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, + {0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x07, 0, 0}, + {0x350c, 0x04, 0, 0}, {0x350d, 0x58, 0, 0}, {0x3801, 0x8a, 0, 0}, + {0x3803, 0x0a, 0, 0}, {0x3804, 0x07, 0, 0}, {0x3805, 0x80, 0, 0}, + {0x3806, 0x04, 0, 0}, {0x3807, 0x39, 0, 0}, {0x3808, 0x07, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, + {0x380c, 0x09, 0, 0}, {0x380d, 0xd6, 0, 0}, {0x380e, 0x04, 0, 0}, + {0x380f, 0x58, 0, 0}, {0x381c, 0x11, 0, 0}, {0x381d, 0xba, 0, 0}, + {0x381e, 0x04, 0, 0}, {0x381f, 0x48, 0, 0}, {0x3820, 0x04, 0, 0}, + {0x3821, 0x18, 0, 0}, {0x3a08, 0x14, 0, 0}, {0x3a09, 0xe0, 0, 0}, + {0x3a0a, 0x11, 0, 0}, {0x3a0b, 0x60, 0, 0}, {0x3a0d, 0x04, 0, 0}, + {0x3a0e, 0x03, 0, 0}, {0x5682, 0x07, 0, 0}, {0x5683, 0x60, 0, 0}, + {0x5686, 0x04, 0, 0}, {0x5687, 0x1c, 0, 0}, {0x5001, 0x7f, 0, 0}, + {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0}, {0x460c, 0x20, 0, 0}, + {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x471d, 0x05, 0, 0}, + {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, {0x501f, 0x00, 0, 0}, + {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0}, + {0x5002, 0xe0, 0, 0}, +}; + +static struct reg_value ov5642_setting_15fps_QVGA_320_240[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, + {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, + {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, + {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, + {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, + {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, + {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, + {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, + {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, + {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, + {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, + {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, + {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, + {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, + {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, + {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, + {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, + {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, + {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, + {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, + {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, + {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, + {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, + {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, + {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, + {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, + {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, + {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, + {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, + {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, + {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, + {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, + {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, + {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, + {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, + {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, + {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, + {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, + {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, + {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, + {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, + {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, + {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, + {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, + {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, + {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, + {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, + {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, + {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, + {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, + {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, + {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, + {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, + {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, + {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, + {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, + {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, + {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, + {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, + {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, + {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, + {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, + {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, + {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, + {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, + {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, + {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, + {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, + {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, + {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, + {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, + {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, + {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, + {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, + {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, + {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, + {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, + {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, + {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, + {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, + {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, + {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, + {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, + {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, + {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, + {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, + {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, + {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, + {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, + {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, + {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, + {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, + {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, + {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, + {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, + {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, + {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, + {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, + {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, + {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, + {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, + {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, + {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, + {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, + {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, + {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, + {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, + {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, + {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, + {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, + {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, + {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, + {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, + {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, + {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, + {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, + {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, + {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, + {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, + {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, + {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, + {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, + {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, + {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, + {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, + {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, + {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, + {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, + {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, + {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, + {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, + {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, + {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, + {0x380b, 0xf0, 0, 0}, {0x3a00, 0x78, 0, 0}, +}; + +static struct reg_value ov5642_setting_15fps_NTSC_720_480[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, + {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, + {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, + {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, + {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, + {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, + {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, + {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, + {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, + {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, + {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, + {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, + {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, + {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, + {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, + {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, + {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, + {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, + {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, + {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, + {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, + {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, + {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, + {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, + {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, + {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, + {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, + {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, + {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, + {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, + {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, + {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, + {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, + {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, + {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, + {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, + {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, + {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, + {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, + {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, + {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, + {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, + {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, + {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, + {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, + {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, + {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, + {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, + {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, + {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, + {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, + {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, + {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, + {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, + {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, + {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, + {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, + {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, + {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, + {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, + {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, + {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, + {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, + {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, + {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, + {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, + {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, + {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, + {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, + {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, + {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, + {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, + {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, + {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, + {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, + {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, + {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, + {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, + {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, + {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, + {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, + {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, + {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, + {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, + {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, + {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, + {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, + {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, + {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, + {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, + {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, + {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, + {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, + {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, + {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, + {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, + {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, + {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, + {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, + {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, + {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, + {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, + {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, + {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, + {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, + {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, + {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, + {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, + {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, + {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, + {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, + {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, + {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, + {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, + {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, + {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, + {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, + {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, + {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, + {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, + {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, + {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, + {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, + {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, + {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, + {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, + {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, + {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, + {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, + {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, + {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, + {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, + {0x3824, 0x11, 0, 0}, {0x3825, 0xb4, 0, 0}, {0x3826, 0x00, 0, 0}, + {0x3827, 0x3d, 0, 0}, {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0xd0, 0, 0}, {0x380A, 0x01, 0, 0}, {0x380B, 0xe0, 0, 0}, + {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, + {0x3807, 0x55, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0x55, 0, 0}, + {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, +}; + +static struct reg_value ov5642_setting_15fps_PAL_720_576[] = { + {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, + {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, + {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, + {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, + {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0}, + {0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0}, + {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0}, + {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0}, + {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0}, + {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0}, + {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0}, + {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0}, + {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0}, + {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0}, + {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, + {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, + {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, + {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0}, + {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0}, + {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0}, + {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0}, + {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0}, + {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0}, + {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0}, + {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0}, + {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0}, + {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, + {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, + {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, + {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, + {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, + {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0}, + {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0}, + {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, + {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0}, + {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0}, + {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0}, + {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0}, + {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0}, + {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0}, + {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0}, + {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0}, + {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0}, + {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0}, + {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0}, + {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0}, + {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0}, + {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0}, + {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0}, + {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0}, + {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0}, + {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0}, + {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0}, + {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0}, + {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0}, + {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0}, + {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0}, + {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0}, + {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0}, + {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0}, + {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0}, + {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0}, + {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0}, + {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0}, + {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0}, + {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0}, + {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0}, + {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0}, + {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0}, + {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0}, + {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0}, + {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0}, + {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0}, + {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0}, + {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0}, + {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0}, + {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0}, + {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0}, + {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0}, + {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0}, + {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0}, + {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0}, + {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0}, + {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0}, + {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0}, + {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0}, + {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0}, + {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0}, + {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0}, + {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0}, + {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0}, + {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, + {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0}, + {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0}, + {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0}, + {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, + {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, + {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, + {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, + {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0}, + {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0}, + {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0}, + {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0}, + {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0}, + {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0}, + {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0}, + {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0}, + {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0}, + {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0}, + {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0}, + {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0}, + {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0}, + {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0}, + {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0}, + {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0}, + {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0}, + {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0}, + {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0}, + {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0}, + {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0}, + {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0}, + {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0}, + {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0}, + {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0}, + {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0}, + {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0}, + {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0}, + {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0}, + {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0}, + {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0}, + {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0}, + {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0}, + {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0}, + {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0}, + {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0}, + {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0}, + {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0}, + {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0}, + {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0}, + {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0}, + {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0}, + {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0}, + {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0}, + {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0}, + {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0}, + {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0}, + {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0}, + {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0}, + {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0}, + {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0}, + {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0}, + {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0}, + {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0}, + {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0}, + {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0}, + {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0}, + {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0}, + {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0}, + {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0}, + {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0}, + {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0}, + {0x3824, 0x11, 0, 0}, {0x3825, 0xdc, 0, 0}, {0x3826, 0x00, 0, 0}, + {0x3827, 0x08, 0, 0}, {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, + {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, {0x3808, 0x02, 0, 0}, + {0x3809, 0xd0, 0, 0}, {0x380A, 0x02, 0, 0}, {0x380B, 0x40, 0, 0}, + {0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x3806, 0x03, 0, 0}, + {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0xc0, 0, 0}, + {0x5682, 0x04, 0, 0}, {0x5683, 0xb0, 0, 0}, +}; + +static struct ov5642_mode_info ov5642_mode_info_data[2][ov5642_mode_MAX + 1] = { + { + {ov5642_mode_VGA_640_480, 640, 480, + ov5642_setting_15fps_VGA_640_480, + ARRAY_SIZE(ov5642_setting_15fps_VGA_640_480)}, + {ov5642_mode_QVGA_320_240, 320, 240, + ov5642_setting_15fps_QVGA_320_240, + ARRAY_SIZE(ov5642_setting_15fps_QVGA_320_240)}, + {ov5642_mode_NTSC_720_480, 720, 480, + ov5642_setting_15fps_NTSC_720_480, + ARRAY_SIZE(ov5642_setting_15fps_NTSC_720_480)}, + {ov5642_mode_PAL_720_576, 720, 576, + ov5642_setting_15fps_PAL_720_576, + ARRAY_SIZE(ov5642_setting_15fps_PAL_720_576)}, + {ov5642_mode_720P_1280_720, 1280, 720, + ov5642_setting_15fps_720P_1280_720, + ARRAY_SIZE(ov5642_setting_15fps_720P_1280_720)}, + {ov5642_mode_1080P_1920_1080, 1920, 1080, + ov5642_setting_15fps_1080P_1920_1080, + ARRAY_SIZE(ov5642_setting_15fps_1080P_1920_1080)}, + {ov5642_mode_QSXGA_2592_1944, 2592, 1944, + ov5642_setting_15fps_QSXGA_2592_1944, + ARRAY_SIZE(ov5642_setting_15fps_QSXGA_2592_1944)}, + {ov5642_mode_QCIF_176_144, 176, 144, + ov5642_setting_15fps_QCIF_176_144, + ARRAY_SIZE(ov5642_setting_15fps_QCIF_176_144)}, + {ov5642_mode_XGA_1024_768, 1024, 768, + ov5642_setting_15fps_XGA_1024_768, + ARRAY_SIZE(ov5642_setting_15fps_XGA_1024_768)}, + }, + { + {ov5642_mode_VGA_640_480, 640, 480, + ov5642_setting_30fps_VGA_640_480, + ARRAY_SIZE(ov5642_setting_30fps_VGA_640_480)}, + {ov5642_mode_QVGA_320_240, 320, 240, + ov5642_setting_30fps_QVGA_320_240, + ARRAY_SIZE(ov5642_setting_30fps_QVGA_320_240)}, + {ov5642_mode_NTSC_720_480, 720, 480, + ov5642_setting_30fps_NTSC_720_480, + ARRAY_SIZE(ov5642_setting_30fps_NTSC_720_480)}, + {ov5642_mode_PAL_720_576, 720, 576, + ov5642_setting_30fps_PAL_720_576, + ARRAY_SIZE(ov5642_setting_30fps_PAL_720_576)}, + {ov5642_mode_720P_1280_720, 1280, 720, + ov5642_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov5642_setting_30fps_720P_1280_720)}, + {ov5642_mode_1080P_1920_1080, 0, 0, NULL, 0}, + {ov5642_mode_QSXGA_2592_1944, 0, 0, NULL, 0}, + {ov5642_mode_QCIF_176_144, 176, 144, + ov5642_setting_30fps_QCIF_176_144, + ARRAY_SIZE(ov5642_setting_30fps_QCIF_176_144)}, + {ov5642_mode_XGA_1024_768, 1024, 768, + ov5642_setting_30fps_XGA_1024_768, + ARRAY_SIZE(ov5642_setting_30fps_XGA_1024_768)}, + }, +}; + +static struct regulator *io_regulator; +static struct regulator *core_regulator; +static struct regulator *analog_regulator; +static struct regulator *gpo_regulator; + +static int ov5642_probe(struct i2c_client *adapter, + const struct i2c_device_id *device_id); +static int ov5642_remove(struct i2c_client *client); + +static s32 ov5642_read_reg(u16 reg, u8 *val); +static s32 ov5642_write_reg(u16 reg, u8 val); + +static const struct i2c_device_id ov5642_id[] = { + {"ov5642", 0}, + {"ov564x", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ov5642_id); + +static struct i2c_driver ov5642_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ov5642", + }, + .probe = ov5642_probe, + .remove = ov5642_remove, + .id_table = ov5642_id, +}; + +static void ov5642_standby(s32 enable) +{ + if (enable) + gpio_set_value(pwn_gpio, 1); + else + gpio_set_value(pwn_gpio, 0); + + msleep(2); +} + +static void ov5642_reset(void) +{ + /* camera reset */ + gpio_set_value(rst_gpio, 1); + + /* camera power down */ + gpio_set_value(pwn_gpio, 1); + msleep(5); + + gpio_set_value(pwn_gpio, 0); + msleep(5); + + gpio_set_value(rst_gpio, 0); + msleep(1); + + gpio_set_value(rst_gpio, 1); + msleep(5); + + gpio_set_value(pwn_gpio, 1); +} + +static int ov5642_power_on(struct device *dev) +{ + int ret = 0; + + io_regulator = devm_regulator_get(dev, "DOVDD"); + if (!IS_ERR(io_regulator)) { + regulator_set_voltage(io_regulator, + OV5642_VOLTAGE_DIGITAL_IO, + OV5642_VOLTAGE_DIGITAL_IO); + ret = regulator_enable(io_regulator); + if (ret) { + pr_err("%s:io set voltage error\n", __func__); + return ret; + } else { + dev_dbg(dev, + "%s:io set voltage ok\n", __func__); + } + } else { + pr_err("%s: cannot get io voltage error\n", __func__); + io_regulator = NULL; + } + + core_regulator = devm_regulator_get(dev, "DVDD"); + if (!IS_ERR(core_regulator)) { + regulator_set_voltage(core_regulator, + OV5642_VOLTAGE_DIGITAL_CORE, + OV5642_VOLTAGE_DIGITAL_CORE); + ret = regulator_enable(core_regulator); + if (ret) { + pr_err("%s:core set voltage error\n", __func__); + return ret; + } else { + dev_dbg(dev, + "%s:core set voltage ok\n", __func__); + } + } else { + core_regulator = NULL; + pr_err("%s: cannot get core voltage error\n", __func__); + } + + analog_regulator = devm_regulator_get(dev, "AVDD"); + if (!IS_ERR(analog_regulator)) { + regulator_set_voltage(analog_regulator, + OV5642_VOLTAGE_ANALOG, + OV5642_VOLTAGE_ANALOG); + ret = regulator_enable(analog_regulator); + if (ret) { + pr_err("%s:analog set voltage error\n", + __func__); + return ret; + } else { + dev_dbg(dev, + "%s:analog set voltage ok\n", __func__); + } + } else { + analog_regulator = NULL; + pr_err("%s: cannot get analog voltage error\n", __func__); + } + + return ret; +} + +static s32 ov5642_write_reg(u16 reg, u8 val) +{ + u8 au8Buf[3] = {0}; + + au8Buf[0] = reg >> 8; + au8Buf[1] = reg & 0xff; + au8Buf[2] = val; + + if (i2c_master_send(ov5642_data.i2c_client, au8Buf, 3) < 0) { + pr_err("%s:write reg error:reg=%x,val=%x\n", + __func__, reg, val); + return -1; + } + + return 0; +} + +static s32 ov5642_read_reg(u16 reg, u8 *val) +{ + u8 au8RegBuf[2] = {0}; + u8 u8RdVal = 0; + + au8RegBuf[0] = reg >> 8; + au8RegBuf[1] = reg & 0xff; + + if (2 != i2c_master_send(ov5642_data.i2c_client, au8RegBuf, 2)) { + pr_err("%s:write reg error:reg=%x\n", + __func__, reg); + return -1; + } + + if (1 != i2c_master_recv(ov5642_data.i2c_client, &u8RdVal, 1)) { + pr_err("%s:read reg error:reg=%x,val=%x\n", + __func__, reg, u8RdVal); + return -1; + } + + *val = u8RdVal; + + return u8RdVal; +} + +static int ov5642_set_rot_mode(struct reg_value *rot_mode) +{ + s32 i = 0; + s32 iModeSettingArySize = 2; + register u32 Delay_ms = 0; + register u16 RegAddr = 0; + register u8 Mask = 0; + register u8 Val = 0; + u8 RegVal = 0; + int retval = 0; + for (i = 0; i < iModeSettingArySize; ++i, ++rot_mode) { + Delay_ms = rot_mode->u32Delay_ms; + RegAddr = rot_mode->u16RegAddr; + Val = rot_mode->u8Val; + Mask = rot_mode->u8Mask; + + if (Mask) { + retval = ov5642_read_reg(RegAddr, &RegVal); + if (retval < 0) { + pr_err("%s, read reg 0x%x failed\n", + __func__, RegAddr); + goto err; + } + + Val |= RegVal; + Val &= Mask; + } + + retval = ov5642_write_reg(RegAddr, Val); + if (retval < 0) { + pr_err("%s, write reg 0x%x failed\n", + __func__, RegAddr); + goto err; + } + + if (Delay_ms) + mdelay(Delay_ms); + } +err: + return retval; +} +static int ov5642_init_mode(enum ov5642_frame_rate frame_rate, + enum ov5642_mode mode); +static int ov5642_write_snapshot_para(enum ov5642_frame_rate frame_rate, + enum ov5642_mode mode); +static int ov5642_change_mode(enum ov5642_frame_rate new_frame_rate, + enum ov5642_frame_rate old_frame_rate, + enum ov5642_mode new_mode, + enum ov5642_mode orig_mode) +{ + struct reg_value *pModeSetting = NULL; + s32 i = 0; + s32 iModeSettingArySize = 0; + register u32 Delay_ms = 0; + register u16 RegAddr = 0; + register u8 Mask = 0; + register u8 Val = 0; + u8 RegVal = 0; + int retval = 0; + + if (new_mode > ov5642_mode_MAX || new_mode < ov5642_mode_MIN) { + pr_err("Wrong ov5642 mode detected!\n"); + return -1; + } + + if ((new_frame_rate == old_frame_rate) && + (new_mode == ov5642_mode_VGA_640_480) && + (orig_mode == ov5642_mode_QSXGA_2592_1944)) { + pModeSetting = ov5642_setting_QSXGA_2_VGA; + iModeSettingArySize = ARRAY_SIZE(ov5642_setting_QSXGA_2_VGA); + ov5642_data.pix.width = 640; + ov5642_data.pix.height = 480; + } else if ((new_frame_rate == old_frame_rate) && + (new_mode == ov5642_mode_QVGA_320_240) && + (orig_mode == ov5642_mode_VGA_640_480)) { + pModeSetting = ov5642_setting_VGA_2_QVGA; + iModeSettingArySize = ARRAY_SIZE(ov5642_setting_VGA_2_QVGA); + ov5642_data.pix.width = 320; + ov5642_data.pix.height = 240; + } else { + retval = ov5642_write_snapshot_para(new_frame_rate, new_mode); + goto err; + } + + if (ov5642_data.pix.width == 0 || ov5642_data.pix.height == 0 || + pModeSetting == NULL || iModeSettingArySize == 0) + return -EINVAL; + + for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) { + Delay_ms = pModeSetting->u32Delay_ms; + RegAddr = pModeSetting->u16RegAddr; + Val = pModeSetting->u8Val; + Mask = pModeSetting->u8Mask; + + if (Mask) { + retval = ov5642_read_reg(RegAddr, &RegVal); + if (retval < 0) { + pr_err("read reg error addr=0x%x", RegAddr); + goto err; + } + + RegVal &= ~(u8)Mask; + Val &= Mask; + Val |= RegVal; + } + + retval = ov5642_write_reg(RegAddr, Val); + if (retval < 0) { + pr_err("write reg error addr=0x%x", RegAddr); + goto err; + } + + if (Delay_ms) + msleep(Delay_ms); + } +err: + return retval; +} +static int ov5642_init_mode(enum ov5642_frame_rate frame_rate, + enum ov5642_mode mode) +{ + struct reg_value *pModeSetting = NULL; + s32 i = 0; + s32 iModeSettingArySize = 0; + register u32 Delay_ms = 0; + register u16 RegAddr = 0; + register u8 Mask = 0; + register u8 Val = 0; + u8 RegVal = 0; + int retval = 0; + + if (mode > ov5642_mode_MAX || mode < ov5642_mode_MIN) { + pr_err("Wrong ov5642 mode detected!\n"); + return -1; + } + + pModeSetting = ov5642_mode_info_data[frame_rate][mode].init_data_ptr; + iModeSettingArySize = + ov5642_mode_info_data[frame_rate][mode].init_data_size; + + ov5642_data.pix.width = ov5642_mode_info_data[frame_rate][mode].width; + ov5642_data.pix.height = ov5642_mode_info_data[frame_rate][mode].height; + + if (ov5642_data.pix.width == 0 || ov5642_data.pix.height == 0 || + pModeSetting == NULL || iModeSettingArySize == 0) + return -EINVAL; + + for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) { + Delay_ms = pModeSetting->u32Delay_ms; + RegAddr = pModeSetting->u16RegAddr; + Val = pModeSetting->u8Val; + Mask = pModeSetting->u8Mask; + + if (Mask) { + retval = ov5642_read_reg(RegAddr, &RegVal); + if (retval < 0) { + pr_err("read reg error addr=0x%x", RegAddr); + goto err; + } + + RegVal &= ~(u8)Mask; + Val &= Mask; + Val |= RegVal; + } + + retval = ov5642_write_reg(RegAddr, Val); + if (retval < 0) { + pr_err("write reg error addr=0x%x", RegAddr); + goto err; + } + + if (Delay_ms) + msleep(Delay_ms); + } +err: + return retval; +} + +static int ov5642_write_snapshot_para(enum ov5642_frame_rate frame_rate, + enum ov5642_mode mode) +{ + int ret = 0; + bool m_60Hz = false; + u16 cap_frame_rate = 50; + u16 g_prev_frame_rate = 225; + + u8 ev_low, ev_mid, ev_high; + u8 ret_l, ret_m, ret_h, gain, lines_10ms; + u16 ulcap_ev, icap_gain, prev_maxlines; + u32 ulcap_ev_gain, cap_maxlines, g_prev_ev; + + ov5642_write_reg(0x3503, 0x07); + + ret_h = ret_m = ret_l = 0; + g_prev_ev = 0; + ov5642_read_reg(0x3500, &ret_h); + ov5642_read_reg(0x3501, &ret_m); + ov5642_read_reg(0x3502, &ret_l); + g_prev_ev = (ret_h << 12) + (ret_m << 4) + (ret_l >> 4); + + ret_h = ret_m = ret_l = 0; + prev_maxlines = 0; + ov5642_read_reg(0x380e, &ret_h); + ov5642_read_reg(0x380f, &ret_l); + prev_maxlines = (ret_h << 8) + ret_l; + /*Read back AGC Gain for preview*/ + gain = 0; + ov5642_read_reg(0x350b, &gain); + + ret = ov5642_init_mode(frame_rate, mode); + if (ret < 0) + return ret; + + ret_h = ret_m = ret_l = 0; + ov5642_read_reg(0x380e, &ret_h); + ov5642_read_reg(0x380f, &ret_l); + cap_maxlines = (ret_h << 8) + ret_l; + if (m_60Hz == true) + lines_10ms = cap_frame_rate * cap_maxlines/12000; + else + lines_10ms = cap_frame_rate * cap_maxlines/10000; + + if (prev_maxlines == 0) + prev_maxlines = 1; + + ulcap_ev = (g_prev_ev*(cap_frame_rate)*(cap_maxlines))/ + (((prev_maxlines)*(g_prev_frame_rate))); + icap_gain = (gain & 0x0f) + 16; + if (gain & 0x10) + icap_gain = icap_gain << 1; + + if (gain & 0x20) + icap_gain = icap_gain << 1; + + if (gain & 0x40) + icap_gain = icap_gain << 1; + + if (gain & 0x80) + icap_gain = icap_gain << 1; + + ulcap_ev_gain = 2 * ulcap_ev * icap_gain; + + if (ulcap_ev_gain < cap_maxlines*16) { + ulcap_ev = ulcap_ev_gain/16; + if (ulcap_ev > lines_10ms) { + ulcap_ev /= lines_10ms; + ulcap_ev *= lines_10ms; + } + } else + ulcap_ev = cap_maxlines; + + if (ulcap_ev == 0) + ulcap_ev = 1; + + icap_gain = (ulcap_ev_gain*2/ulcap_ev + 1)/2; + ev_low = ((unsigned char)ulcap_ev)<<4; + ev_mid = (unsigned char)(ulcap_ev >> 4) & 0xff; + ev_high = (unsigned char)(ulcap_ev >> 12); + + gain = 0; + if (icap_gain > 31) { + gain |= 0x10; + icap_gain = icap_gain >> 1; + } + if (icap_gain > 31) { + gain |= 0x20; + icap_gain = icap_gain >> 1; + } + if (icap_gain > 31) { + gain |= 0x40; + icap_gain = icap_gain >> 1; + } + if (icap_gain > 31) { + gain |= 0x80; + icap_gain = icap_gain >> 1; + } + if (icap_gain > 16) + gain |= ((icap_gain - 16) & 0x0f); + + if (gain == 0x10) + gain = 0x11; + + ov5642_write_reg(0x350b, gain); + ov5642_write_reg(0x3502, ev_low); + ov5642_write_reg(0x3501, ev_mid); + ov5642_write_reg(0x3500, ev_high); + msleep(500); + + return ret ; +} + + +/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ + +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + if (s == NULL) { + pr_err(" ERROR!! no slave device set!\n"); + return -1; + } + + memset(p, 0, sizeof(*p)); + p->u.bt656.clock_curr = ov5642_data.mclk; + pr_debug(" clock_curr=mclk=%d\n", ov5642_data.mclk); + p->if_type = V4L2_IF_TYPE_BT656; + p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; + p->u.bt656.clock_min = OV5642_XCLK_MIN; + p->u.bt656.clock_max = OV5642_XCLK_MAX; + p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ + + return 0; +} + +/*! + * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl + * @s: pointer to standard V4L2 device structure + * @on: indicates power mode (on or off) + * + * Turns the power on or off, depending on the value of on and returns the + * appropriate error code. + */ +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct sensor_data *sensor = s->priv; + + if (on && !sensor->on) { + if (io_regulator) + if (regulator_enable(io_regulator) != 0) + return -EIO; + if (core_regulator) + if (regulator_enable(core_regulator) != 0) + return -EIO; + if (gpo_regulator) + if (regulator_enable(gpo_regulator) != 0) + return -EIO; + if (analog_regulator) + if (regulator_enable(analog_regulator) != 0) + return -EIO; + /* Make sure power on */ + ov5642_standby(0); + } else if (!on && sensor->on) { + if (analog_regulator) + regulator_disable(analog_regulator); + if (core_regulator) + regulator_disable(core_regulator); + if (io_regulator) + regulator_disable(io_regulator); + if (gpo_regulator) + regulator_disable(gpo_regulator); + + ov5642_standby(1); + } + + sensor->on = on; + + return 0; +} + +/*! + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + int ret = 0; + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cparm->capability = sensor->streamcap.capability; + cparm->timeperframe = sensor->streamcap.timeperframe; + cparm->capturemode = sensor->streamcap.capturemode; + ret = 0; + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + u32 tgt_fps, old_fps; /* target frames per secound */ + enum ov5642_frame_rate new_frame_rate, old_frame_rate; + int ret = 0; + + /* Make sure power on */ + ov5642_standby(0); + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + /* Check that the new frame rate is allowed. */ + if ((timeperframe->numerator == 0) || + (timeperframe->denominator == 0)) { + timeperframe->denominator = DEFAULT_FPS; + timeperframe->numerator = 1; + } + + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps > MAX_FPS) { + timeperframe->denominator = MAX_FPS; + timeperframe->numerator = 1; + } else if (tgt_fps < MIN_FPS) { + timeperframe->denominator = MIN_FPS; + timeperframe->numerator = 1; + } + + /* Actual frame rate we use */ + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps == 15) + new_frame_rate = ov5642_15_fps; + else if (tgt_fps == 30) + new_frame_rate = ov5642_30_fps; + else { + pr_err(" The camera frame rate is not supported!\n"); + return -EINVAL; + } + + if (sensor->streamcap.timeperframe.numerator != 0) + old_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + else + old_fps = 30; + + if (old_fps == 15) + old_frame_rate = ov5642_15_fps; + else if (old_fps == 30) + old_frame_rate = ov5642_30_fps; + else { + pr_warning(" No valid frame rate set!\n"); + old_frame_rate = ov5642_30_fps; + } + + ret = ov5642_change_mode(new_frame_rate, old_frame_rate, + a->parm.capture.capturemode, + sensor->streamcap.capturemode); + if (ret < 0) + return ret; + + sensor->streamcap.timeperframe = *timeperframe; + sensor->streamcap.capturemode = + (u32)a->parm.capture.capturemode; + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + pr_debug(" type is not " \ + "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n", + a->type); + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct sensor_data *sensor = s->priv; + + f->fmt.pix = sensor->pix; + + return 0; +} + +/*! + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int ret = 0; + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + vc->value = ov5642_data.brightness; + break; + case V4L2_CID_HUE: + vc->value = ov5642_data.hue; + break; + case V4L2_CID_CONTRAST: + vc->value = ov5642_data.contrast; + break; + case V4L2_CID_SATURATION: + vc->value = ov5642_data.saturation; + break; + case V4L2_CID_RED_BALANCE: + vc->value = ov5642_data.red; + break; + case V4L2_CID_BLUE_BALANCE: + vc->value = ov5642_data.blue; + break; + case V4L2_CID_EXPOSURE: + vc->value = ov5642_data.ae_mode; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/*! + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = 0; + struct sensor_data *sensor = s->priv; + __u32 captureMode = sensor->streamcap.capturemode; + struct reg_value *rot_mode = NULL; + + pr_debug("In ov5642:ioctl_s_ctrl %d\n", + vc->id); + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + break; + case V4L2_CID_CONTRAST: + break; + case V4L2_CID_SATURATION: + break; + case V4L2_CID_HUE: + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + break; + case V4L2_CID_DO_WHITE_BALANCE: + break; + case V4L2_CID_RED_BALANCE: + break; + case V4L2_CID_BLUE_BALANCE: + break; + case V4L2_CID_GAMMA: + break; + case V4L2_CID_EXPOSURE: + break; + case V4L2_CID_AUTOGAIN: + break; + case V4L2_CID_GAIN: + break; + case V4L2_CID_HFLIP: + break; + case V4L2_CID_VFLIP: + break; + case V4L2_CID_MXC_ROT: + case V4L2_CID_MXC_VF_ROT: + switch (vc->value) { + case V4L2_MXC_ROTATE_NONE: + if (captureMode == ov5642_mode_QSXGA_2592_1944) + rot_mode = ov5642_rot_none_FULL; + else + rot_mode = ov5642_rot_none_VGA; + + if (ov5642_set_rot_mode(rot_mode)) + retval = -EPERM; + break; + case V4L2_MXC_ROTATE_VERT_FLIP: + if (captureMode == ov5642_mode_QSXGA_2592_1944) + rot_mode = ov5642_rot_vert_flip_FULL; + else + rot_mode = ov5642_rot_vert_flip_VGA ; + + if (ov5642_set_rot_mode(rot_mode)) + retval = -EPERM; + break; + case V4L2_MXC_ROTATE_HORIZ_FLIP: + if (captureMode == ov5642_mode_QSXGA_2592_1944) + rot_mode = ov5642_rot_horiz_flip_FULL; + else + rot_mode = ov5642_rot_horiz_flip_VGA; + + if (ov5642_set_rot_mode(rot_mode)) + retval = -EPERM; + break; + case V4L2_MXC_ROTATE_180: + if (captureMode == ov5642_mode_QSXGA_2592_1944) + rot_mode = ov5642_rot_180_FULL; + else + rot_mode = ov5642_rot_180_VGA; + + if (ov5642_set_rot_mode(rot_mode)) + retval = -EPERM; + break; + default: + retval = -EPERM; + break; + } + break; + default: + retval = -EPERM; + break; + } + + return retval; +} + +/*! + * ioctl_enum_framesizes - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMESIZES ioctl + * @s: pointer to standard V4L2 device structure + * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index > ov5642_mode_MAX) + return -EINVAL; + + fsize->pixel_format = ov5642_data.pix.pixelformat; + fsize->discrete.width = + max(ov5642_mode_info_data[0][fsize->index].width, + ov5642_mode_info_data[1][fsize->index].width); + fsize->discrete.height = + max(ov5642_mode_info_data[0][fsize->index].height, + ov5642_mode_info_data[1][fsize->index].height); + return 0; +} + +/*! + * ioctl_enum_frameintervals - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMEINTERVALS ioctl + * @s: pointer to standard V4L2 device structure + * @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_frameintervals(struct v4l2_int_device *s, + struct v4l2_frmivalenum *fival) +{ + int i, j, count; + + if (fival->index < 0 || fival->index > ov5642_mode_MAX) + return -EINVAL; + + if (fival->pixel_format == 0 || fival->width == 0 || + fival->height == 0) { + pr_warning("Please assign pixelformat, width and height.\n"); + return -EINVAL; + } + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1; + + count = 0; + for (i = 0; i < ARRAY_SIZE(ov5642_mode_info_data); i++) { + for (j = 0; j < (ov5642_mode_MAX + 1); j++) { + if (fival->pixel_format == ov5642_data.pix.pixelformat + && fival->width == ov5642_mode_info_data[i][j].width + && fival->height == ov5642_mode_info_data[i][j].height + && ov5642_mode_info_data[i][j].init_data_ptr != NULL) { + count++; + } + if (fival->index == (count - 1)) { + fival->discrete.denominator = + ov5642_framerates[i]; + return 0; + } + } + } + + return -EINVAL; +} + +/*! + * ioctl_g_chip_ident - V4L2 sensor interface handler for + * VIDIOC_DBG_G_CHIP_IDENT ioctl + * @s: pointer to standard V4L2 device structure + * @id: pointer to int + * + * Return 0. + */ +static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) +{ + ((struct v4l2_dbg_chip_ident *)id)->match.type = + V4L2_CHIP_MATCH_I2C_DRIVER; + strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, "ov5642_camera"); + + return 0; +} + +/*! + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + + return 0; +} + +/*! + * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT + * @s: pointer to standard V4L2 device structure + * @fmt: pointer to standard V4L2 fmt description structure + * + * Return 0. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index > 0) /* only 1 pixelformat support so far */ + return -EINVAL; + + fmt->pixelformat = ov5642_data.pix.pixelformat; + + return 0; +} + +/*! + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct reg_value *pModeSetting = NULL; + s32 i = 0; + s32 iModeSettingArySize = 0; + register u32 Delay_ms = 0; + register u16 RegAddr = 0; + register u8 Mask = 0; + register u8 Val = 0; + u8 RegVal = 0; + int retval = 0; + + struct sensor_data *sensor = s->priv; + u32 tgt_xclk; /* target xclk */ + u32 tgt_fps; /* target frames per secound */ + enum ov5642_frame_rate frame_rate; + + ov5642_data.on = true; + + /* mclk */ + tgt_xclk = ov5642_data.mclk; + tgt_xclk = min(tgt_xclk, (u32)OV5642_XCLK_MAX); + tgt_xclk = max(tgt_xclk, (u32)OV5642_XCLK_MIN); + ov5642_data.mclk = tgt_xclk; + + pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000); + + /* Default camera frame rate is set in probe */ + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 15) + frame_rate = ov5642_15_fps; + else if (tgt_fps == 30) + frame_rate = ov5642_30_fps; + else + return -EINVAL; /* Only support 15fps or 30fps now. */ + + pModeSetting = ov5642_initial_setting; + iModeSettingArySize = ARRAY_SIZE(ov5642_initial_setting); + + for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) { + Delay_ms = pModeSetting->u32Delay_ms; + RegAddr = pModeSetting->u16RegAddr; + Val = pModeSetting->u8Val; + Mask = pModeSetting->u8Mask; + if (Mask) { + retval = ov5642_read_reg(RegAddr, &RegVal); + if (retval < 0) + goto err; + + RegVal &= ~(u8)Mask; + Val &= Mask; + Val |= RegVal; + } + + retval = ov5642_write_reg(RegAddr, Val); + if (retval < 0) + goto err; + + if (Delay_ms) + msleep(Delay_ms); + } +err: + return retval; +} + +/*! + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the device when slave detaches to the master. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/*! + * This structure defines all the ioctls for this module and links them to the + * enumeration. + */ +static struct v4l2_int_ioctl_desc ov5642_ioctl_desc[] = { + { vidioc_int_dev_init_num, + (v4l2_int_ioctl_func *)ioctl_dev_init }, + { vidioc_int_dev_exit_num, ioctl_dev_exit}, + { vidioc_int_s_power_num, + (v4l2_int_ioctl_func *)ioctl_s_power }, + { vidioc_int_g_ifparm_num, + (v4l2_int_ioctl_func *)ioctl_g_ifparm }, +/* { vidioc_int_g_needs_reset_num, + (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, */ +/* { vidioc_int_reset_num, + (v4l2_int_ioctl_func *)ioctl_reset }, */ + { vidioc_int_init_num, + (v4l2_int_ioctl_func *)ioctl_init }, + { vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, +/* { vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, */ + { vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, +/* { vidioc_int_s_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, */ + { vidioc_int_g_parm_num, + (v4l2_int_ioctl_func *)ioctl_g_parm }, + { vidioc_int_s_parm_num, + (v4l2_int_ioctl_func *)ioctl_s_parm }, +/* { vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *)ioctl_queryctrl }, */ + { vidioc_int_g_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_g_ctrl }, + { vidioc_int_s_ctrl_num, + (v4l2_int_ioctl_func *)ioctl_s_ctrl }, + { vidioc_int_enum_framesizes_num, + (v4l2_int_ioctl_func *)ioctl_enum_framesizes }, + { vidioc_int_enum_frameintervals_num, + (v4l2_int_ioctl_func *)ioctl_enum_frameintervals }, + { vidioc_int_g_chip_ident_num, + (v4l2_int_ioctl_func *)ioctl_g_chip_ident }, +}; + +static struct v4l2_int_slave ov5642_slave = { + .ioctls = ov5642_ioctl_desc, + .num_ioctls = ARRAY_SIZE(ov5642_ioctl_desc), +}; + +static struct v4l2_int_device ov5642_int_device = { + .module = THIS_MODULE, + .name = "ov5642", + .type = v4l2_int_type_slave, + .u = { + .slave = &ov5642_slave, + }, +}; + +/*! + * ov5642 I2C probe function + * + * @param adapter struct i2c_adapter * + * @return Error code indicating success or failure + */ +static int ov5642_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pinctrl *pinctrl; + struct device *dev = &client->dev; + int retval; + u8 chip_id_high, chip_id_low; + + /* ov5642 pinctrl */ + pinctrl = devm_pinctrl_get_select_default(dev); + if (IS_ERR(pinctrl)) { + dev_err(dev, "ov5642 setup pinctrl failed!"); + return PTR_ERR(pinctrl); + } + + /* request power down pin */ + pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); + if (!gpio_is_valid(pwn_gpio)) { + dev_warn(dev, "no sensor pwdn pin available"); + return -EINVAL; + } + retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, + "ov5642_pwdn"); + if (retval < 0) + return retval; + + /* request reset pin */ + rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); + if (!gpio_is_valid(rst_gpio)) { + dev_warn(dev, "no sensor reset pin available"); + return -EINVAL; + } + retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, + "ov5642_reset"); + if (retval < 0) + return retval; + + /* Set initial values for the sensor struct. */ + memset(&ov5642_data, 0, sizeof(ov5642_data)); + ov5642_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); + if (IS_ERR(ov5642_data.sensor_clk)) { + /* assuming clock enabled by default */ + ov5642_data.sensor_clk = NULL; + dev_err(dev, "clock-frequency missing or invalid\n"); + return PTR_ERR(ov5642_data.sensor_clk); + } + + retval = of_property_read_u32(dev->of_node, "mclk", + (u32 *) &(ov5642_data.mclk)); + if (retval) { + dev_err(dev, "mclk missing or invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "mclk_source", + (u32 *) &(ov5642_data.mclk_source)); + if (retval) { + dev_err(dev, "mclk_source missing or invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "csi_id", + &(ov5642_data.csi)); + if (retval) { + dev_err(dev, "csi_id missing or invalid\n"); + return retval; + } + + clk_prepare_enable(ov5642_data.sensor_clk); + + ov5642_data.io_init = ov5642_reset; + ov5642_data.i2c_client = client; + ov5642_data.pix.pixelformat = V4L2_PIX_FMT_YUYV; + ov5642_data.pix.width = 640; + ov5642_data.pix.height = 480; + ov5642_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | + V4L2_CAP_TIMEPERFRAME; + ov5642_data.streamcap.capturemode = 0; + ov5642_data.streamcap.timeperframe.denominator = DEFAULT_FPS; + ov5642_data.streamcap.timeperframe.numerator = 1; + + ov5642_power_on(&client->dev); + + ov5642_reset(); + + ov5642_standby(0); + + retval = ov5642_read_reg(OV5642_CHIP_ID_HIGH_BYTE, &chip_id_high); + if (retval < 0 || chip_id_high != 0x56) { + pr_warning("camera ov5642 is not found\n"); + clk_disable_unprepare(ov5642_data.sensor_clk); + return -ENODEV; + } + retval = ov5642_read_reg(OV5642_CHIP_ID_LOW_BYTE, &chip_id_low); + if (retval < 0 || chip_id_low != 0x42) { + pr_warning("camera ov5642 is not found\n"); + clk_disable_unprepare(ov5642_data.sensor_clk); + return -ENODEV; + } + + ov5642_standby(1); + + ov5642_int_device.priv = &ov5642_data; + retval = v4l2_int_device_register(&ov5642_int_device); + + clk_disable_unprepare(ov5642_data.sensor_clk); + + pr_info("camera ov5642 is found\n"); + return retval; +} + +/*! + * ov5642 I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int ov5642_remove(struct i2c_client *client) +{ + v4l2_int_device_unregister(&ov5642_int_device); + + if (gpo_regulator) + regulator_disable(gpo_regulator); + + if (analog_regulator) + regulator_disable(analog_regulator); + + if (core_regulator) + regulator_disable(core_regulator); + + if (io_regulator) + regulator_disable(io_regulator); + + return 0; +} + +/*! + * ov5642 init function + * Called by insmod ov5642_camera.ko. + * + * @return Error code indicating success or failure + */ +static __init int ov5642_init(void) +{ + u8 err; + + err = i2c_add_driver(&ov5642_i2c_driver); + if (err != 0) + pr_err("%s:driver registration failed, error=%d\n", + __func__, err); + + return err; +} + +/*! + * OV5642 cleanup function + * Called on rmmod ov5642_camera.ko + * + * @return Error code indicating success or failure + */ +static void __exit ov5642_clean(void) +{ + i2c_del_driver(&ov5642_i2c_driver); +} + +module_init(ov5642_init); +module_exit(ov5642_clean); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("OV5642 Camera Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("CSI"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/output/Kconfig linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/Kconfig --- linux-3.10.30/drivers/media/platform/mxc/output/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/Kconfig 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,16 @@ +config VIDEO_MXC_IPU_OUTPUT + tristate "IPU v4l2 output support" + depends on VIDEO_MXC_OUTPUT && MXC_IPU + ---help--- + This is the video4linux2 driver for IPU post processing video output. + +config VIDEO_MXC_PXP_V4L2 + tristate "MXC PxP V4L2 driver" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF_DMA_CONTIG + ---help--- + This is a video4linux driver for the Freescale PxP + (Pixel Pipeline). This module supports output overlay of + the MXC framebuffer on a video stream. + + To compile this driver as a module, choose M here. diff -Nur linux-3.10.30/drivers/media/platform/mxc/output/Makefile linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/Makefile --- linux-3.10.30/drivers/media/platform/mxc/output/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/Makefile 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,2 @@ +obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc_vout.o +obj-$(CONFIG_VIDEO_MXC_PXP_V4L2) += mxc_pxp_v4l2.o diff -Nur linux-3.10.30/drivers/media/platform/mxc/output/mxc_pxp_v4l2.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/mxc_pxp_v4l2.c --- linux-3.10.30/drivers/media/platform/mxc/output/mxc_pxp_v4l2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/mxc_pxp_v4l2.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,1263 @@ +/* + * Copyright (C) 2010-2013 Freescale Semiconductor, 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 + * + */ +/* + * Based on STMP378X PxP driver + * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "mxc_pxp_v4l2.h" + +#define PXP_DRIVER_NAME "pxp-v4l2" +#define PXP_DRIVER_MAJOR 2 +#define PXP_DRIVER_MINOR 0 + +#define PXP_DEF_BUFS 2 +#define PXP_MIN_PIX 8 + +#define V4L2_OUTPUT_TYPE_INTERNAL 4 + +static int video_nr = -1; /* -1 ==> auto assign */ +static struct pxp_data_format pxp_s0_formats[] = { + { + .name = "24-bit RGB", + .bpp = 4, + .fourcc = V4L2_PIX_FMT_RGB24, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "16-bit RGB 5:6:5", + .bpp = 2, + .fourcc = V4L2_PIX_FMT_RGB565, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "16-bit RGB 5:5:5", + .bpp = 2, + .fourcc = V4L2_PIX_FMT_RGB555, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "YUV 4:2:0 Planar", + .bpp = 2, + .fourcc = V4L2_PIX_FMT_YUV420, + .colorspace = V4L2_COLORSPACE_JPEG, + }, { + .name = "YUV 4:2:2 Planar", + .bpp = 2, + .fourcc = V4L2_PIX_FMT_YUV422P, + .colorspace = V4L2_COLORSPACE_JPEG, + }, { + .name = "UYVY", + .bpp = 2, + .fourcc = V4L2_PIX_FMT_UYVY, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static unsigned int v4l2_fmt_to_pxp_fmt(u32 v4l2_pix_fmt) +{ + u32 pxp_fmt = 0; + + if (v4l2_pix_fmt == V4L2_PIX_FMT_RGB24) + pxp_fmt = PXP_PIX_FMT_RGB24; + else if (v4l2_pix_fmt == V4L2_PIX_FMT_RGB565) + pxp_fmt = PXP_PIX_FMT_RGB565; + else if (v4l2_pix_fmt == V4L2_PIX_FMT_RGB555) + pxp_fmt = PXP_PIX_FMT_RGB555; + else if (v4l2_pix_fmt == V4L2_PIX_FMT_RGB555) + pxp_fmt = PXP_PIX_FMT_RGB555; + else if (v4l2_pix_fmt == V4L2_PIX_FMT_YUV420) + pxp_fmt = PXP_PIX_FMT_YUV420P; + else if (v4l2_pix_fmt == V4L2_PIX_FMT_YUV422P) + pxp_fmt = PXP_PIX_FMT_YUV422P; + else if (v4l2_pix_fmt == V4L2_PIX_FMT_UYVY) + pxp_fmt = PXP_PIX_FMT_UYVY; + + return pxp_fmt; +} +struct v4l2_queryctrl pxp_controls[] = { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Rotation", + .minimum = 0, + .maximum = 270, + .step = 90, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_PRIVATE_BASE + 1, + .name = "Background Color", + .minimum = 0, + .maximum = 0xFFFFFF, + .step = 1, + .default_value = 0, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_PRIVATE_BASE + 2, + .name = "Set S0 Chromakey", + .minimum = -1, + .maximum = 0xFFFFFF, + .step = 1, + .default_value = -1, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_PRIVATE_BASE + 3, + .name = "YUV Colorspace", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, +}; + +/* callback function */ +static void video_dma_done(void *arg) +{ + struct pxp_tx_desc *tx_desc = to_tx_desc(arg); + struct dma_chan *chan = tx_desc->txd.chan; + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + struct pxps *pxp = pxp_chan->client; + struct videobuf_buffer *vb; + + dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n", + tx_desc->txd.cookie, + pxp->active ? sg_dma_address(&pxp->active->sg[0]) : 0); + + spin_lock(&pxp->lock); + if (pxp->active) { + vb = &pxp->active->vb; + + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + } + + if (list_empty(&pxp->outq)) { + pxp->active = NULL; + spin_unlock(&pxp->lock); + + return; + } + + pxp->active = list_entry(pxp->outq.next, + struct pxp_buffer, vb.queue); + pxp->active->vb.state = VIDEOBUF_ACTIVE; + spin_unlock(&pxp->lock); +} + +static int acquire_dma_channel(struct pxps *pxp) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + struct pxp_channel **pchan = &pxp->pxp_channel[0]; + + if (*pchan) { + struct videobuf_buffer *vb, *_vb; + dma_release_channel(&(*pchan)->dma_chan); + *pchan = NULL; + pxp->active = NULL; + list_for_each_entry_safe(vb, _vb, &pxp->outq, queue) { + list_del_init(&vb->queue); + vb->state = VIDEOBUF_ERROR; + wake_up(&vb->done); + } + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + chan = dma_request_channel(mask, NULL, NULL); + if (!chan) + return -EBUSY; + + *pchan = to_pxp_channel(chan); + (*pchan)->client = pxp; + + return 0; +} + +static int _get_fbinfo(struct fb_info **fbi) +{ + int i; + for (i = 0; i < num_registered_fb; i++) { + char *idstr = registered_fb[i]->fix.id; + if (strcmp(idstr, "mxs") == 0) { + *fbi = registered_fb[i]; + return 0; + } + } + + return -ENODEV; +} + +static int pxp_set_fbinfo(struct pxps *pxp) +{ + struct v4l2_framebuffer *fb = &pxp->fb; + int err; + + err = _get_fbinfo(&pxp->fbi); + if (err) + return err; + + fb->fmt.width = pxp->fbi->var.xres; + fb->fmt.height = pxp->fbi->var.yres; + pxp->pxp_conf.out_param.stride = pxp->fbi->var.xres; + if (pxp->fbi->var.bits_per_pixel == 16) + fb->fmt.pixelformat = V4L2_PIX_FMT_RGB565; + else + fb->fmt.pixelformat = V4L2_PIX_FMT_RGB24; + + fb->base = (void *)pxp->fbi->fix.smem_start; + + return 0; +} + +static int _get_cur_fb_blank(struct pxps *pxp) +{ + struct fb_info *fbi; + mm_segment_t old_fs; + int err = 0; + + err = _get_fbinfo(&fbi); + if (err) + return err; + + if (fbi->fbops->fb_ioctl) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_BLANK, + (unsigned int)(&pxp->fb_blank)); + set_fs(old_fs); + } + + return err; +} + +static int pxp_show_buf(struct pxps *pxp, bool toshow) +{ + struct fb_info *fbi = pxp->fbi; + int ret; + + console_lock(); + fbi->fix.smem_start = toshow ? + pxp->outb_phys : (unsigned long)pxp->fb.base; + ret = fb_pan_display(fbi, &fbi->var); + console_unlock(); + + return ret; +} + +static int set_fb_blank(int blank) +{ + struct fb_info *fbi; + int err = 0; + + err = _get_fbinfo(&fbi); + if (err) + return err; + + console_lock(); + fb_blank(fbi, blank); + console_unlock(); + + return err; +} + +static int pxp_set_cstate(struct pxps *pxp, struct v4l2_control *vc) +{ + + if (vc->id == V4L2_CID_HFLIP) { + pxp->pxp_conf.proc_data.hflip = vc->value; + } else if (vc->id == V4L2_CID_VFLIP) { + pxp->pxp_conf.proc_data.vflip = vc->value; + } else if (vc->id == V4L2_CID_PRIVATE_BASE) { + if (vc->value % 90) + return -ERANGE; + pxp->pxp_conf.proc_data.rotate = vc->value; + } else if (vc->id == V4L2_CID_PRIVATE_BASE + 1) { + pxp->pxp_conf.proc_data.bgcolor = vc->value; + } else if (vc->id == V4L2_CID_PRIVATE_BASE + 2) { + pxp->pxp_conf.s0_param.color_key = vc->value; + } else if (vc->id == V4L2_CID_PRIVATE_BASE + 3) { + pxp->pxp_conf.proc_data.yuv = vc->value; + } + + return 0; +} + +static int pxp_get_cstate(struct pxps *pxp, struct v4l2_control *vc) +{ + if (vc->id == V4L2_CID_HFLIP) + vc->value = pxp->pxp_conf.proc_data.hflip; + else if (vc->id == V4L2_CID_VFLIP) + vc->value = pxp->pxp_conf.proc_data.vflip; + else if (vc->id == V4L2_CID_PRIVATE_BASE) + vc->value = pxp->pxp_conf.proc_data.rotate; + else if (vc->id == V4L2_CID_PRIVATE_BASE + 1) + vc->value = pxp->pxp_conf.proc_data.bgcolor; + else if (vc->id == V4L2_CID_PRIVATE_BASE + 2) + vc->value = pxp->pxp_conf.s0_param.color_key; + else if (vc->id == V4L2_CID_PRIVATE_BASE + 3) + vc->value = pxp->pxp_conf.proc_data.yuv; + + return 0; +} + +static int pxp_enumoutput(struct file *file, void *fh, + struct v4l2_output *o) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + if ((o->index < 0) || (o->index > 1)) + return -EINVAL; + + memset(o, 0, sizeof(struct v4l2_output)); + if (o->index == 0) { + strcpy(o->name, "PxP Display Output"); + pxp->output = 0; + } else { + strcpy(o->name, "PxP Virtual Output"); + pxp->output = 1; + } + o->type = V4L2_OUTPUT_TYPE_INTERNAL; + o->std = 0; + o->reserved[0] = pxp->outb_phys; + + return 0; +} + +static int pxp_g_output(struct file *file, void *fh, + unsigned int *i) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + *i = pxp->output; + + return 0; +} + +static int pxp_s_output(struct file *file, void *fh, + unsigned int i) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + struct v4l2_pix_format *fmt = &pxp->fb.fmt; + int bpp; + + if ((i < 0) || (i > 1)) + return -EINVAL; + + if (pxp->outb) + return 0; + + /* Output buffer is same format as fbdev */ + if (fmt->pixelformat == V4L2_PIX_FMT_RGB24) + bpp = 4; + else + bpp = 2; + + pxp->outb_size = fmt->width * fmt->height * bpp; + pxp->outb = kmalloc(fmt->width * fmt->height * bpp, + GFP_KERNEL | GFP_DMA); + if (pxp->outb == NULL) { + dev_err(&pxp->pdev->dev, "No enough memory!\n"); + return -ENOMEM; + } + pxp->outb_phys = virt_to_phys(pxp->outb); + dma_map_single(NULL, pxp->outb, + fmt->width * fmt->height * bpp, DMA_TO_DEVICE); + + pxp->pxp_conf.out_param.width = fmt->width; + pxp->pxp_conf.out_param.height = fmt->height; + if (fmt->pixelformat == V4L2_PIX_FMT_RGB24) + pxp->pxp_conf.out_param.pixel_fmt = PXP_PIX_FMT_RGB24; + else + pxp->pxp_conf.out_param.pixel_fmt = PXP_PIX_FMT_RGB565; + + return 0; +} + +static int pxp_enum_fmt_video_output(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + enum v4l2_buf_type type = fmt->type; + int index = fmt->index; + + if ((fmt->index < 0) || (fmt->index >= ARRAY_SIZE(pxp_s0_formats))) + return -EINVAL; + + memset(fmt, 0, sizeof(struct v4l2_fmtdesc)); + fmt->index = index; + fmt->type = type; + fmt->pixelformat = pxp_s0_formats[index].fourcc; + strcpy(fmt->description, pxp_s0_formats[index].name); + + return 0; +} + +static int pxp_g_fmt_video_output(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pf = &f->fmt.pix; + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + struct pxp_data_format *fmt = pxp->s0_fmt; + + pf->width = pxp->pxp_conf.s0_param.width; + pf->height = pxp->pxp_conf.s0_param.height; + pf->pixelformat = fmt->fourcc; + pf->field = V4L2_FIELD_NONE; + pf->bytesperline = fmt->bpp * pf->width; + pf->sizeimage = pf->bytesperline * pf->height; + pf->colorspace = fmt->colorspace; + pf->priv = 0; + + return 0; +} + +static struct pxp_data_format *pxp_get_format(struct v4l2_format *f) +{ + struct pxp_data_format *fmt; + int i; + + for (i = 0; i < ARRAY_SIZE(pxp_s0_formats); i++) { + fmt = &pxp_s0_formats[i]; + if (fmt->fourcc == f->fmt.pix.pixelformat) + break; + } + + if (i == ARRAY_SIZE(pxp_s0_formats)) + return NULL; + + return &pxp_s0_formats[i]; +} + +static int pxp_try_fmt_video_output(struct file *file, void *fh, + struct v4l2_format *f) +{ + int w = f->fmt.pix.width; + int h = f->fmt.pix.height; + struct pxp_data_format *fmt = pxp_get_format(f); + + if (!fmt) + return -EINVAL; + + w = min(w, 2040); + w = max(w, 8); + h = min(h, 2040); + h = max(h, 8); + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = w; + f->fmt.pix.height = h; + f->fmt.pix.pixelformat = fmt->fourcc; + + return 0; +} + +static int pxp_s_fmt_video_output(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + struct v4l2_pix_format *pf = &f->fmt.pix; + int ret; + + ret = acquire_dma_channel(pxp); + if (ret < 0) + return ret; + + ret = pxp_try_fmt_video_output(file, fh, f); + if (ret == 0) { + pxp->s0_fmt = pxp_get_format(f); + pxp->pxp_conf.s0_param.pixel_fmt = + v4l2_fmt_to_pxp_fmt(pxp->s0_fmt->fourcc); + pxp->pxp_conf.s0_param.width = pf->width; + pxp->pxp_conf.s0_param.height = pf->height; + } + + + return ret; +} + +static int pxp_g_fmt_output_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + struct v4l2_window *wf = &f->fmt.win; + + memset(wf, 0, sizeof(struct v4l2_window)); + wf->chromakey = pxp->s1_chromakey; + wf->global_alpha = pxp->global_alpha; + wf->field = V4L2_FIELD_NONE; + wf->clips = NULL; + wf->clipcount = 0; + wf->bitmap = NULL; + wf->w.left = pxp->pxp_conf.proc_data.srect.left; + wf->w.top = pxp->pxp_conf.proc_data.srect.top; + wf->w.width = pxp->pxp_conf.proc_data.srect.width; + wf->w.height = pxp->pxp_conf.proc_data.srect.height; + + return 0; +} + +static int pxp_try_fmt_output_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + struct v4l2_window *wf = &f->fmt.win; + struct v4l2_rect srect; + u32 s1_chromakey = wf->chromakey; + u8 global_alpha = wf->global_alpha; + + memcpy(&srect, &(wf->w), sizeof(struct v4l2_rect)); + + pxp_g_fmt_output_overlay(file, fh, f); + + wf->chromakey = s1_chromakey; + wf->global_alpha = global_alpha; + + /* Constrain parameters to the input buffer */ + wf->w.left = srect.left; + wf->w.top = srect.top; + wf->w.width = min(srect.width, + ((__s32)pxp->pxp_conf.s0_param.width - wf->w.left)); + wf->w.height = min(srect.height, + ((__s32)pxp->pxp_conf.s0_param.height - wf->w.top)); + + return 0; +} + +static int pxp_s_fmt_output_overlay(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + struct v4l2_window *wf = &f->fmt.win; + int ret = pxp_try_fmt_output_overlay(file, fh, f); + + if (ret == 0) { + pxp->global_alpha = wf->global_alpha; + pxp->s1_chromakey = wf->chromakey; + pxp->pxp_conf.proc_data.srect.left = wf->w.left; + pxp->pxp_conf.proc_data.srect.top = wf->w.top; + pxp->pxp_conf.proc_data.srect.width = wf->w.width; + pxp->pxp_conf.proc_data.srect.height = wf->w.height; + pxp->pxp_conf.ol_param[0].global_alpha = pxp->global_alpha; + pxp->pxp_conf.ol_param[0].color_key = pxp->s1_chromakey; + pxp->pxp_conf.ol_param[0].color_key_enable = + pxp->s1_chromakey_state; + } + + return ret; +} + +static int pxp_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *r) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + return videobuf_reqbufs(&pxp->s0_vbq, r); +} + +static int pxp_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + return videobuf_querybuf(&pxp->s0_vbq, b); +} + +static int pxp_qbuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + return videobuf_qbuf(&pxp->s0_vbq, b); +} + +static int pxp_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + return videobuf_dqbuf(&pxp->s0_vbq, b, file->f_flags & O_NONBLOCK); +} + +static int pxp_streamon(struct file *file, void *priv, + enum v4l2_buf_type t) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + int ret = 0; + + if (t != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + _get_cur_fb_blank(pxp); + set_fb_blank(FB_BLANK_UNBLANK); + + ret = videobuf_streamon(&pxp->s0_vbq); + + if (!ret && (pxp->output == 0)) + pxp_show_buf(pxp, true); + + return ret; +} + +static int pxp_streamoff(struct file *file, void *priv, + enum v4l2_buf_type t) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + int ret = 0; + + if ((t != V4L2_BUF_TYPE_VIDEO_OUTPUT)) + return -EINVAL; + + ret = videobuf_streamoff(&pxp->s0_vbq); + + if (!ret) + pxp_show_buf(pxp, false); + + if (pxp->fb_blank) + set_fb_blank(FB_BLANK_POWERDOWN); + + return ret; +} + +static int pxp_buf_setup(struct videobuf_queue *q, + unsigned int *count, unsigned *size) +{ + struct pxps *pxp = q->priv_data; + + *size = pxp->pxp_conf.s0_param.width * + pxp->pxp_conf.s0_param.height * pxp->s0_fmt->bpp; + + if (0 == *count) + *count = PXP_DEF_BUFS; + + return 0; +} + +static void pxp_buf_free(struct videobuf_queue *q, struct pxp_buffer *buf) +{ + struct videobuf_buffer *vb = &buf->vb; + struct dma_async_tx_descriptor *txd = buf->txd; + + BUG_ON(in_interrupt()); + + pr_debug("%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + /* + * This waits until this buffer is out of danger, i.e., until it is no + * longer in STATE_QUEUED or STATE_ACTIVE + */ + videobuf_waiton(q, vb, 0, 0); + if (txd) + async_tx_ack(txd); + + videobuf_dma_contig_free(q, vb); + buf->txd = NULL; + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static int pxp_buf_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct pxps *pxp = q->priv_data; + struct pxp_config_data *pxp_conf = &pxp->pxp_conf; + struct pxp_proc_data *proc_data = &pxp_conf->proc_data; + struct pxp_buffer *buf = container_of(vb, struct pxp_buffer, vb); + struct pxp_tx_desc *desc; + int ret = 0; + int i, length; + + vb->width = pxp->pxp_conf.s0_param.width; + vb->height = pxp->pxp_conf.s0_param.height; + vb->size = vb->width * vb->height * pxp->s0_fmt->bpp; + vb->field = V4L2_FIELD_NONE; + if (vb->state != VIDEOBUF_NEEDS_INIT) + pxp_buf_free(q, buf); + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + struct pxp_channel *pchan = pxp->pxp_channel[0]; + struct scatterlist *sg = &buf->sg[0]; + + /* This actually (allocates and) maps buffers */ + ret = videobuf_iolock(q, vb, NULL); + if (ret) { + pr_err("fail to call videobuf_iolock, ret = %d\n", ret); + goto fail; + } + + /* + * sg[0] for input(S0) + * Sg[1] for output + */ + sg_init_table(sg, 3); + + buf->txd = pchan->dma_chan.device->device_prep_slave_sg( + &pchan->dma_chan, sg, 3, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT, NULL); + if (!buf->txd) { + ret = -EIO; + goto fail; + } + + buf->txd->callback_param = buf->txd; + buf->txd->callback = video_dma_done; + + desc = to_tx_desc(buf->txd); + length = desc->len; + for (i = 0; i < length; i++) { + if (i == 0) {/* S0 */ + memcpy(&desc->proc_data, proc_data, + sizeof(struct pxp_proc_data)); + pxp_conf->s0_param.paddr = + videobuf_to_dma_contig(vb); + memcpy(&desc->layer_param.s0_param, + &pxp_conf->s0_param, + sizeof(struct pxp_layer_param)); + } else if (i == 1) { /* Output */ + if (proc_data->rotate % 180) { + pxp_conf->out_param.width = + pxp->fb.fmt.height; + pxp_conf->out_param.height = + pxp->fb.fmt.width; + } else { + pxp_conf->out_param.width = + pxp->fb.fmt.width; + pxp_conf->out_param.height = + pxp->fb.fmt.height; + } + + pxp_conf->out_param.paddr = pxp->outb_phys; + memcpy(&desc->layer_param.out_param, + &pxp_conf->out_param, + sizeof(struct pxp_layer_param)); + } else if (pxp_conf->ol_param[0].combine_enable) { + /* Overlay */ + pxp_conf->ol_param[0].paddr = + (dma_addr_t)pxp->fb.base; + pxp_conf->ol_param[0].width = pxp->fb.fmt.width; + pxp_conf->ol_param[0].height = + pxp->fb.fmt.height; + pxp_conf->ol_param[0].pixel_fmt = + pxp_conf->out_param.pixel_fmt; + memcpy(&desc->layer_param.ol_param, + &pxp_conf->ol_param[0], + sizeof(struct pxp_layer_param)); + } + + desc = desc->next; + } + + vb->state = VIDEOBUF_PREPARED; + } + + return 0; + +fail: + pxp_buf_free(q, buf); + return ret; +} + + +static void pxp_buf_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct pxps *pxp = q->priv_data; + struct pxp_buffer *buf = container_of(vb, struct pxp_buffer, vb); + struct dma_async_tx_descriptor *txd = buf->txd; + struct pxp_channel *pchan = pxp->pxp_channel[0]; + dma_cookie_t cookie; + + BUG_ON(!irqs_disabled()); + + list_add_tail(&vb->queue, &pxp->outq); + + if (!pxp->active) { + pxp->active = buf; + vb->state = VIDEOBUF_ACTIVE; + } else { + vb->state = VIDEOBUF_QUEUED; + } + + spin_unlock_irq(&pxp->lock); + + cookie = txd->tx_submit(txd); + dev_dbg(&pxp->pdev->dev, "Submitted cookie %d DMA 0x%08x\n", + cookie, sg_dma_address(&buf->sg[0])); + mdelay(5); + /* trigger ePxP */ + dma_async_issue_pending(&pchan->dma_chan); + + spin_lock_irq(&pxp->lock); + + if (cookie >= 0) + return; + + /* Submit error */ + pr_err("%s: Submit error\n", __func__); + vb->state = VIDEOBUF_PREPARED; + + list_del_init(&vb->queue); + + if (pxp->active == buf) + pxp->active = NULL; +} + +static void pxp_buf_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct pxps *pxp = q->priv_data; + struct pxp_buffer *buf = container_of(vb, struct pxp_buffer, vb); + unsigned long flags; + + spin_lock_irqsave(&pxp->lock, flags); + if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && + !list_empty(&vb->queue)) { + vb->state = VIDEOBUF_ERROR; + + list_del_init(&vb->queue); + if (pxp->active == buf) + pxp->active = NULL; + } + spin_unlock_irqrestore(&pxp->lock, flags); + + pxp_buf_free(q, buf); +} + +static struct videobuf_queue_ops pxp_vbq_ops = { + .buf_setup = pxp_buf_setup, + .buf_prepare = pxp_buf_prepare, + .buf_queue = pxp_buf_queue, + .buf_release = pxp_buf_release, +}; + +static int pxp_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, "pxp"); + strcpy(cap->card, "pxp"); + strlcpy(cap->bus_info, dev_name(&pxp->pdev->dev), + sizeof(cap->bus_info)); + + cap->version = (PXP_DRIVER_MAJOR << 8) + PXP_DRIVER_MINOR; + + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_OUTPUT_OVERLAY | + V4L2_CAP_STREAMING; + + return 0; +} + +static int pxp_g_fbuf(struct file *file, void *priv, + struct v4l2_framebuffer *fb) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + memset(fb, 0, sizeof(*fb)); + + fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | + V4L2_FBUF_CAP_CHROMAKEY | + V4L2_FBUF_CAP_LOCAL_ALPHA | + V4L2_FBUF_CAP_GLOBAL_ALPHA; + + if (pxp->global_alpha_state) + fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA; + if (pxp->s1_chromakey_state) + fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY; + + return 0; +} + +static int pxp_s_fbuf(struct file *file, void *priv, + const struct v4l2_framebuffer *fb) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + pxp->overlay_state = + (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0; + pxp->global_alpha_state = + (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0; + pxp->s1_chromakey_state = + (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; + + pxp->pxp_conf.ol_param[0].combine_enable = pxp->overlay_state; + pxp->pxp_conf.ol_param[0].global_alpha_enable = pxp->global_alpha_state; + + return 0; +} + +static int pxp_g_crop(struct file *file, void *fh, + struct v4l2_crop *c) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) + return -EINVAL; + + c->c.left = pxp->pxp_conf.proc_data.drect.left; + c->c.top = pxp->pxp_conf.proc_data.drect.top; + c->c.width = pxp->pxp_conf.proc_data.drect.width; + c->c.height = pxp->pxp_conf.proc_data.drect.height; + + return 0; +} + +static int pxp_s_crop(struct file *file, void *fh, + const struct v4l2_crop *c) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + int l = c->c.left; + int t = c->c.top; + int w = c->c.width; + int h = c->c.height; + int fbw = pxp->fb.fmt.width; + int fbh = pxp->fb.fmt.height; + + if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) + return -EINVAL; + + /* Constrain parameters to FB limits */ + w = min(w, fbw); + w = max(w, PXP_MIN_PIX); + h = min(h, fbh); + h = max(h, PXP_MIN_PIX); + if ((l + w) > fbw) + l = 0; + if ((t + h) > fbh) + t = 0; + + /* Round up values to PxP pixel block */ + l = roundup(l, PXP_MIN_PIX); + t = roundup(t, PXP_MIN_PIX); + w = roundup(w, PXP_MIN_PIX); + h = roundup(h, PXP_MIN_PIX); + + pxp->pxp_conf.proc_data.drect.left = l; + pxp->pxp_conf.proc_data.drect.top = t; + pxp->pxp_conf.proc_data.drect.width = w; + pxp->pxp_conf.proc_data.drect.height = h; + + return 0; +} + +static int pxp_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (qc->id && qc->id == pxp_controls[i].id) { + memcpy(qc, &(pxp_controls[i]), sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + +static int pxp_g_ctrl(struct file *file, void *priv, + struct v4l2_control *vc) +{ + int i; + + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (vc->id == pxp_controls[i].id) + return pxp_get_cstate(pxp, vc); + + return -EINVAL; +} + +static int pxp_s_ctrl(struct file *file, void *priv, + struct v4l2_control *vc) +{ + int i; + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + for (i = 0; i < ARRAY_SIZE(pxp_controls); i++) + if (vc->id == pxp_controls[i].id) { + if (vc->value < pxp_controls[i].minimum || + vc->value > pxp_controls[i].maximum) + return -ERANGE; + return pxp_set_cstate(pxp, vc); + } + + return -EINVAL; +} + +void pxp_release(struct video_device *vfd) +{ + struct pxps *pxp = video_get_drvdata(vfd); + + spin_lock(&pxp->lock); + video_device_release(vfd); + spin_unlock(&pxp->lock); +} + +static int pxp_open(struct file *file) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + int ret = 0; + + mutex_lock(&pxp->mutex); + pxp->users++; + + if (pxp->users > 1) { + pxp->users--; + ret = -EBUSY; + goto out; + } +out: + mutex_unlock(&pxp->mutex); + if (ret) + return ret; + + ret = pxp_set_fbinfo(pxp); + if (ret) { + dev_err(&pxp->pdev->dev, "failed to call pxp_set_fbinfo\n"); + return ret; + } + + videobuf_queue_dma_contig_init(&pxp->s0_vbq, + &pxp_vbq_ops, + &pxp->pdev->dev, + &pxp->lock, + V4L2_BUF_TYPE_VIDEO_OUTPUT, + V4L2_FIELD_NONE, + sizeof(struct pxp_buffer), + pxp, + NULL); + dev_dbg(&pxp->pdev->dev, "call pxp_open\n"); + + return 0; +} + +static int pxp_close(struct file *file) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + + videobuf_stop(&pxp->s0_vbq); + videobuf_mmap_free(&pxp->s0_vbq); + pxp->active = NULL; + kfree(pxp->outb); + pxp->outb = NULL; + + mutex_lock(&pxp->mutex); + pxp->users--; + mutex_unlock(&pxp->mutex); + + return 0; +} + +static int pxp_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct pxps *pxp = video_get_drvdata(video_devdata(file)); + int ret; + + ret = videobuf_mmap_mapper(&pxp->s0_vbq, vma); + + return ret; +} + +static const struct v4l2_file_operations pxp_fops = { + .owner = THIS_MODULE, + .open = pxp_open, + .release = pxp_close, + .unlocked_ioctl = video_ioctl2, + .mmap = pxp_mmap, +}; + +static const struct v4l2_ioctl_ops pxp_ioctl_ops = { + .vidioc_querycap = pxp_querycap, + + .vidioc_reqbufs = pxp_reqbufs, + .vidioc_querybuf = pxp_querybuf, + .vidioc_qbuf = pxp_qbuf, + .vidioc_dqbuf = pxp_dqbuf, + + .vidioc_streamon = pxp_streamon, + .vidioc_streamoff = pxp_streamoff, + + .vidioc_enum_output = pxp_enumoutput, + .vidioc_g_output = pxp_g_output, + .vidioc_s_output = pxp_s_output, + + .vidioc_enum_fmt_vid_out = pxp_enum_fmt_video_output, + .vidioc_try_fmt_vid_out = pxp_try_fmt_video_output, + .vidioc_g_fmt_vid_out = pxp_g_fmt_video_output, + .vidioc_s_fmt_vid_out = pxp_s_fmt_video_output, + + .vidioc_try_fmt_vid_out_overlay = pxp_try_fmt_output_overlay, + .vidioc_g_fmt_vid_out_overlay = pxp_g_fmt_output_overlay, + .vidioc_s_fmt_vid_out_overlay = pxp_s_fmt_output_overlay, + + .vidioc_g_fbuf = pxp_g_fbuf, + .vidioc_s_fbuf = pxp_s_fbuf, + + .vidioc_g_crop = pxp_g_crop, + .vidioc_s_crop = pxp_s_crop, + + .vidioc_queryctrl = pxp_queryctrl, + .vidioc_g_ctrl = pxp_g_ctrl, + .vidioc_s_ctrl = pxp_s_ctrl, +}; + +static const struct video_device pxp_template = { + .name = "PxP", + .vfl_type = V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_STREAMING, + .vfl_dir = VFL_DIR_TX, + .fops = &pxp_fops, + .release = pxp_release, + .minor = -1, + .ioctl_ops = &pxp_ioctl_ops, +}; + +static const struct of_device_id imx_pxpv4l2_dt_ids[] = { + { .compatible = "fsl,imx6sl-pxp-v4l2", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_pxpv4l2_dt_ids); + +static int pxp_probe(struct platform_device *pdev) +{ + struct pxps *pxp; + int err = 0; + + pxp = kzalloc(sizeof(*pxp), GFP_KERNEL); + if (!pxp) { + dev_err(&pdev->dev, "failed to allocate control object\n"); + err = -ENOMEM; + goto exit; + } + + dev_set_drvdata(&pdev->dev, pxp); + + INIT_LIST_HEAD(&pxp->outq); + spin_lock_init(&pxp->lock); + mutex_init(&pxp->mutex); + + pxp->pdev = pdev; + + pxp->vdev = video_device_alloc(); + if (!pxp->vdev) { + dev_err(&pdev->dev, "video_device_alloc() failed\n"); + err = -ENOMEM; + goto freeirq; + } + + memcpy(pxp->vdev, &pxp_template, sizeof(pxp_template)); + video_set_drvdata(pxp->vdev, pxp); + + err = video_register_device(pxp->vdev, VFL_TYPE_GRABBER, video_nr); + if (err) { + dev_err(&pdev->dev, "failed to register video device\n"); + goto freevdev; + } + + dev_info(&pdev->dev, "initialized\n"); + +exit: + return err; + +freevdev: + video_device_release(pxp->vdev); + +freeirq: + kfree(pxp); + + return err; +} + +static int pxp_remove(struct platform_device *pdev) +{ + struct pxps *pxp = platform_get_drvdata(pdev); + + video_unregister_device(pxp->vdev); + video_device_release(pxp->vdev); + + kfree(pxp); + + return 0; +} + +static struct platform_driver pxp_driver = { + .driver = { + .name = PXP_DRIVER_NAME, + .of_match_table = of_match_ptr(imx_pxpv4l2_dt_ids), + }, + .probe = pxp_probe, + .remove = pxp_remove, +}; + +module_platform_driver(pxp_driver); + +module_param(video_nr, int, 0444); +MODULE_DESCRIPTION("MXC PxP V4L2 driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/platform/mxc/output/mxc_pxp_v4l2.h linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/mxc_pxp_v4l2.h --- linux-3.10.30/drivers/media/platform/mxc/output/mxc_pxp_v4l2.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/mxc_pxp_v4l2.h 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010-2013 Freescale Semiconductor, 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 + * + */ +/* + * Based on STMP378X PxP driver + * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +#ifndef _MXC_PXP_V4L2_H +#define _MXC_PXP_V4L2_H + +#include +#include + +struct pxp_buffer { + /* Must be first! */ + struct videobuf_buffer vb; + + /* One descriptor per scatterlist (per frame) */ + struct dma_async_tx_descriptor *txd; + + struct scatterlist sg[3]; +}; + +struct pxps { + struct platform_device *pdev; + + spinlock_t lock; + struct mutex mutex; + int users; + + struct video_device *vdev; + + struct videobuf_queue s0_vbq; + struct pxp_buffer *active; + struct list_head outq; + struct pxp_channel *pxp_channel[1]; /* We need 1 channel */ + struct pxp_config_data pxp_conf; + + int output; + u32 *outb; + dma_addr_t outb_phys; + u32 outb_size; + + /* Current S0 configuration */ + struct pxp_data_format *s0_fmt; + + struct fb_info *fbi; + struct v4l2_framebuffer fb; + + /* Output overlay support */ + int overlay_state; + int global_alpha_state; + u8 global_alpha; + int s1_chromakey_state; + u32 s1_chromakey; + + int fb_blank; +}; + +struct pxp_data_format { + char *name; + unsigned int bpp; + u32 fourcc; + enum v4l2_colorspace colorspace; +}; + +#endif diff -Nur linux-3.10.30/drivers/media/platform/mxc/output/mxc_vout.c linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/mxc_vout.c --- linux-3.10.30/drivers/media/platform/mxc/output/mxc_vout.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/platform/mxc/output/mxc_vout.c 2014-03-08 20:33:46.000000000 +0100 @@ -0,0 +1,2265 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define UYVY_BLACK (0x00800080) +#define RGB_BLACK (0x0) +#define UV_BLACK (0x80) +#define Y_BLACK (0x0) + +#define MAX_FB_NUM 6 +#define FB_BUFS 3 +#define VDOA_FB_BUFS (FB_BUFS - 1) +#define VALID_HEIGHT_1080P (1080) +#define FRAME_HEIGHT_1080P (1088) +#define FRAME_WIDTH_1080P (1920) +#define CHECK_TILED_1080P_DISPLAY(vout) \ + ((((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) || \ + ((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12F)) &&\ + ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ + ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ + (((vout)->task.input.crop.h == FRAME_HEIGHT_1080P) || \ + ((vout)->task.input.crop.h == VALID_HEIGHT_1080P)) && \ + ((vout)->task.output.width == FRAME_WIDTH_1080P) && \ + ((vout)->task.output.height == VALID_HEIGHT_1080P) && \ + ((vout)->task.output.crop.w == FRAME_WIDTH_1080P) && \ + ((vout)->task.output.crop.h == VALID_HEIGHT_1080P)) +#define CHECK_TILED_1080P_STREAM(vout) \ + ((((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) || \ + ((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12F)) &&\ + ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ + ((vout)->task.input.crop.h == FRAME_HEIGHT_1080P)) +#define IS_PLANAR_PIXEL_FORMAT(format) \ + (format == IPU_PIX_FMT_NV12 || \ + format == IPU_PIX_FMT_YUV420P2 || \ + format == IPU_PIX_FMT_YUV420P || \ + format == IPU_PIX_FMT_YVU420P || \ + format == IPU_PIX_FMT_YUV422P || \ + format == IPU_PIX_FMT_YVU422P || \ + format == IPU_PIX_FMT_YUV444P) + +#define NSEC_PER_FRAME_30FPS (33333333) + +struct mxc_vout_fb { + char *name; + int ipu_id; + struct v4l2_rect crop_bounds; + unsigned int disp_fmt; + bool disp_support_csc; + bool disp_support_windows; +}; + +struct dma_mem { + void *vaddr; + dma_addr_t paddr; + size_t size; +}; + +struct mxc_vout_output { + int open_cnt; + struct fb_info *fbi; + unsigned long fb_smem_start; + unsigned long fb_smem_len; + struct video_device *vfd; + struct mutex mutex; + struct mutex task_lock; + enum v4l2_buf_type type; + + struct videobuf_queue vbq; + spinlock_t vbq_lock; + + struct list_head queue_list; + struct list_head active_list; + + struct v4l2_rect crop_bounds; + unsigned int disp_fmt; + struct mxcfb_pos win_pos; + bool disp_support_windows; + bool disp_support_csc; + + bool fmt_init; + bool release; + bool linear_bypass_pp; + bool vdoa_1080p; + bool tiled_bypass_pp; + struct v4l2_rect in_rect; + struct ipu_task task; + struct ipu_task vdoa_task; + struct dma_mem vdoa_work; + struct dma_mem vdoa_output[VDOA_FB_BUFS]; + + bool timer_stop; + struct hrtimer timer; + struct workqueue_struct *v4l_wq; + struct work_struct disp_work; + unsigned long frame_count; + unsigned long vdi_frame_cnt; + ktime_t start_ktime; + + int ctrl_rotate; + int ctrl_vflip; + int ctrl_hflip; + + dma_addr_t disp_bufs[FB_BUFS]; + + struct videobuf_buffer *pre1_vb; + struct videobuf_buffer *pre2_vb; +}; + +struct mxc_vout_dev { + struct device *dev; + struct v4l2_device v4l2_dev; + struct mxc_vout_output *out[MAX_FB_NUM]; + int out_num; +}; + +/* Driver Configuration macros */ +#define VOUT_NAME "mxc_vout" + +/* Variables configurable through module params*/ +static int debug; +static int vdi_rate_double; +static int video_nr = 16; + +/* Module parameters */ +module_param(video_nr, int, S_IRUGO); +MODULE_PARM_DESC(video_nr, "video device numbers"); +module_param(debug, int, 0600); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); +module_param(vdi_rate_double, int, 0600); +MODULE_PARM_DESC(vdi_rate_double, "vdi frame rate double on/off"); + +static const struct v4l2_fmtdesc mxc_formats[] = { + { + .description = "RGB565", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + { + .description = "BGR24", + .pixelformat = V4L2_PIX_FMT_BGR24, + }, + { + .description = "RGB24", + .pixelformat = V4L2_PIX_FMT_RGB24, + }, + { + .description = "RGB32", + .pixelformat = V4L2_PIX_FMT_RGB32, + }, + { + .description = "BGR32", + .pixelformat = V4L2_PIX_FMT_BGR32, + }, + { + .description = "NV12", + .pixelformat = V4L2_PIX_FMT_NV12, + }, + { + .description = "UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + { + .description = "YUYV", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + { + .description = "YUV422 planar", + .pixelformat = V4L2_PIX_FMT_YUV422P, + }, + { + .description = "YUV444", + .pixelformat = V4L2_PIX_FMT_YUV444, + }, + { + .description = "YUV420", + .pixelformat = V4L2_PIX_FMT_YUV420, + }, + { + .description = "YVU420", + .pixelformat = V4L2_PIX_FMT_YVU420, + }, + { + .description = "TILED NV12P", + .pixelformat = IPU_PIX_FMT_TILED_NV12, + }, + { + .description = "TILED NV12F", + .pixelformat = IPU_PIX_FMT_TILED_NV12F, + }, + { + .description = "YUV444 planar", + .pixelformat = IPU_PIX_FMT_YUV444P, + }, +}; + +#define NUM_MXC_VOUT_FORMATS (ARRAY_SIZE(mxc_formats)) + +#define DEF_INPUT_WIDTH 320 +#define DEF_INPUT_HEIGHT 240 + +static int mxc_vidioc_streamoff(struct file *file, void *fh, + enum v4l2_buf_type i); + +static struct mxc_vout_fb g_fb_setting[MAX_FB_NUM]; +static int config_disp_output(struct mxc_vout_output *vout); +static void release_disp_output(struct mxc_vout_output *vout); + +static unsigned int get_frame_size(struct mxc_vout_output *vout) +{ + unsigned int size; + + if (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) + size = TILED_NV12_FRAME_SIZE(vout->task.input.width, + vout->task.input.height); + else if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { + size = TILED_NV12_FRAME_SIZE(vout->task.input.width, + vout->task.input.height/2); + size *= 2; + } else + size = vout->task.input.width * vout->task.input.height * + fmt_to_bpp(vout->task.input.format)/8; + + return size; +} + +static void free_dma_buf(struct mxc_vout_output *vout, struct dma_mem *buf) +{ + dma_free_coherent(vout->vbq.dev, buf->size, buf->vaddr, buf->paddr); + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "free dma size:0x%x, paddr:0x%x\n", + buf->size, buf->paddr); + memset(buf, 0, sizeof(*buf)); +} + +static int alloc_dma_buf(struct mxc_vout_output *vout, struct dma_mem *buf) +{ + + buf->vaddr = dma_alloc_coherent(vout->vbq.dev, buf->size, &buf->paddr, + GFP_DMA | GFP_KERNEL); + if (!buf->vaddr) { + v4l2_err(vout->vfd->v4l2_dev, + "cannot get dma buf size:0x%x\n", buf->size); + return -ENOMEM; + } + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "alloc dma buf size:0x%x, paddr:0x%x\n", buf->size, buf->paddr); + return 0; +} + +static ipu_channel_t get_ipu_channel(struct fb_info *fbi) +{ + ipu_channel_t ipu_ch = CHAN_NONE; + mm_segment_t old_fs; + + if (fbi->fbops->fb_ioctl) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN, + (unsigned long)&ipu_ch); + set_fs(old_fs); + } + + return ipu_ch; +} + +static unsigned int get_ipu_fmt(struct fb_info *fbi) +{ + mm_segment_t old_fs; + unsigned int fb_fmt; + + if (fbi->fbops->fb_ioctl) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT, + (unsigned long)&fb_fmt); + set_fs(old_fs); + } + + return fb_fmt; +} + +static void update_display_setting(void) +{ + int i; + struct fb_info *fbi; + struct v4l2_rect bg_crop_bounds[2]; + + for (i = 0; i < num_registered_fb; i++) { + fbi = registered_fb[i]; + + memset(&g_fb_setting[i], 0, sizeof(struct mxc_vout_fb)); + + if (!strncmp(fbi->fix.id, "DISP3", 5)) + g_fb_setting[i].ipu_id = 0; + else + g_fb_setting[i].ipu_id = 1; + + g_fb_setting[i].name = fbi->fix.id; + g_fb_setting[i].crop_bounds.left = 0; + g_fb_setting[i].crop_bounds.top = 0; + g_fb_setting[i].crop_bounds.width = fbi->var.xres; + g_fb_setting[i].crop_bounds.height = fbi->var.yres; + g_fb_setting[i].disp_fmt = get_ipu_fmt(fbi); + + if (get_ipu_channel(fbi) == MEM_BG_SYNC) { + bg_crop_bounds[g_fb_setting[i].ipu_id] = + g_fb_setting[i].crop_bounds; + g_fb_setting[i].disp_support_csc = true; + } else if (get_ipu_channel(fbi) == MEM_FG_SYNC) { + g_fb_setting[i].disp_support_csc = true; + g_fb_setting[i].disp_support_windows = true; + } + } + + for (i = 0; i < num_registered_fb; i++) { + fbi = registered_fb[i]; + + if (get_ipu_channel(fbi) == MEM_FG_SYNC) + g_fb_setting[i].crop_bounds = + bg_crop_bounds[g_fb_setting[i].ipu_id]; + } +} + +/* called after g_fb_setting filled by update_display_setting */ +static int update_setting_from_fbi(struct mxc_vout_output *vout, + struct fb_info *fbi) +{ + int i; + bool found = false; + + for (i = 0; i < MAX_FB_NUM; i++) { + if (g_fb_setting[i].name) { + if (!strcmp(fbi->fix.id, g_fb_setting[i].name)) { + vout->crop_bounds = g_fb_setting[i].crop_bounds; + vout->disp_fmt = g_fb_setting[i].disp_fmt; + vout->disp_support_csc = + g_fb_setting[i].disp_support_csc; + vout->disp_support_windows = + g_fb_setting[i].disp_support_windows; + found = true; + break; + } + } + } + + if (!found) { + v4l2_err(vout->vfd->v4l2_dev, "can not find output\n"); + return -EINVAL; + } + strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name)); + + memset(&vout->task, 0, sizeof(struct ipu_task)); + + vout->task.input.width = DEF_INPUT_WIDTH; + vout->task.input.height = DEF_INPUT_HEIGHT; + vout->task.input.crop.pos.x = 0; + vout->task.input.crop.pos.y = 0; + vout->task.input.crop.w = DEF_INPUT_WIDTH; + vout->task.input.crop.h = DEF_INPUT_HEIGHT; + + vout->task.output.width = vout->crop_bounds.width; + vout->task.output.height = vout->crop_bounds.height; + vout->task.output.crop.pos.x = 0; + vout->task.output.crop.pos.y = 0; + vout->task.output.crop.w = vout->crop_bounds.width; + vout->task.output.crop.h = vout->crop_bounds.height; + if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) + vout->task.output.format = IPU_PIX_FMT_UYVY; + else + vout->task.output.format = IPU_PIX_FMT_RGB565; + + return 0; +} + +static inline unsigned long get_jiffies(struct timeval *t) +{ + struct timeval cur; + + if (t->tv_usec >= 1000000) { + t->tv_sec += t->tv_usec / 1000000; + t->tv_usec = t->tv_usec % 1000000; + } + + do_gettimeofday(&cur); + if ((t->tv_sec < cur.tv_sec) + || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec))) + return jiffies; + + if (t->tv_usec < cur.tv_usec) { + cur.tv_sec = t->tv_sec - cur.tv_sec - 1; + cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec; + } else { + cur.tv_sec = t->tv_sec - cur.tv_sec; + cur.tv_usec = t->tv_usec - cur.tv_usec; + } + + return jiffies + timeval_to_jiffies(&cur); +} + +static bool deinterlace_3_field(struct mxc_vout_output *vout) +{ + return (vout->task.input.deinterlace.enable && + (vout->task.input.deinterlace.motion != HIGH_MOTION)); +} + +static int set_field_fmt(struct mxc_vout_output *vout, enum v4l2_field field) +{ + struct ipu_deinterlace *deinterlace = &vout->task.input.deinterlace; + + switch (field) { + /* Images are in progressive format, not interlaced */ + case V4L2_FIELD_NONE: + case V4L2_FIELD_ANY: + deinterlace->enable = false; + deinterlace->field_fmt = 0; + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "Progressive frame.\n"); + break; + case V4L2_FIELD_INTERLACED_TB: + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "Enable deinterlace TB.\n"); + deinterlace->enable = true; + deinterlace->field_fmt = IPU_DEINTERLACE_FIELD_TOP; + break; + case V4L2_FIELD_INTERLACED_BT: + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "Enable deinterlace BT.\n"); + deinterlace->enable = true; + deinterlace->field_fmt = IPU_DEINTERLACE_FIELD_BOTTOM; + break; + default: + v4l2_err(vout->vfd->v4l2_dev, + "field format:%d not supported yet!\n", field); + return -EINVAL; + } + + if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "tiled fmt enable deinterlace.\n"); + deinterlace->enable = true; + } + + if (deinterlace->enable && vdi_rate_double) + deinterlace->field_fmt |= IPU_DEINTERLACE_RATE_EN; + + return 0; +} + +static bool is_pp_bypass(struct mxc_vout_output *vout) +{ + if ((IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) || + (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format)) + return false; + if ((vout->task.input.width == vout->task.output.width) && + (vout->task.input.height == vout->task.output.height) && + (vout->task.input.crop.w == vout->task.output.crop.w) && + (vout->task.input.crop.h == vout->task.output.crop.h) && + (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) && + !vout->task.input.deinterlace.enable) { + if (vout->disp_support_csc) + return true; + else if (!need_csc(vout->task.input.format, vout->disp_fmt)) + return true; + /* + * input crop show to full output which can show based on + * xres_virtual/yres_virtual + */ + } else if ((vout->task.input.crop.w == vout->task.output.crop.w) && + (vout->task.output.crop.w == vout->task.output.width) && + (vout->task.input.crop.h == vout->task.output.crop.h) && + (vout->task.output.crop.h == + vout->task.output.height) && + (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) && + !vout->task.input.deinterlace.enable) { + if (vout->disp_support_csc) + return true; + else if (!need_csc(vout->task.input.format, vout->disp_fmt)) + return true; + } + return false; +} + +static void setup_buf_timer(struct mxc_vout_output *vout, + struct videobuf_buffer *vb) +{ + ktime_t expiry_time, now; + + /* if timestamp is 0, then default to 30fps */ + if ((vb->ts.tv_sec == 0) && (vb->ts.tv_usec == 0)) + expiry_time = ktime_add_ns(vout->start_ktime, + NSEC_PER_FRAME_30FPS * vout->frame_count); + else + expiry_time = timeval_to_ktime(vb->ts); + + now = hrtimer_cb_get_time(&vout->timer); + if ((now.tv64 > expiry_time.tv64)) { + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "warning: timer timeout already expired.\n"); + expiry_time = now; + } + + hrtimer_start(&vout->timer, expiry_time, HRTIMER_MODE_ABS); + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "timer handler next " + "schedule: %lldnsecs\n", expiry_time.tv64); +} + +static int show_buf(struct mxc_vout_output *vout, int idx, + struct ipu_pos *ipos) +{ + struct fb_info *fbi = vout->fbi; + struct fb_var_screeninfo var; + int ret; + u32 fb_base = 0; + + memcpy(&var, &fbi->var, sizeof(var)); + + if (vout->linear_bypass_pp || vout->tiled_bypass_pp) { + /* + * crack fb base + * NOTE: should not do other fb operation during v4l2 + */ + console_lock(); + fb_base = fbi->fix.smem_start; + fbi->fix.smem_start = vout->task.output.paddr; + fbi->var.yoffset = ipos->y + 1; + var.xoffset = ipos->x; + var.yoffset = ipos->y; + var.vmode |= FB_VMODE_YWRAP; + ret = fb_pan_display(fbi, &var); + fbi->fix.smem_start = fb_base; + console_unlock(); + } else { + console_lock(); + var.yoffset = idx * fbi->var.yres; + var.vmode &= ~FB_VMODE_YWRAP; + ret = fb_pan_display(fbi, &var); + console_unlock(); + } + + return ret; +} + +static void disp_work_func(struct work_struct *work) +{ + struct mxc_vout_output *vout = + container_of(work, struct mxc_vout_output, disp_work); + struct videobuf_queue *q = &vout->vbq; + struct videobuf_buffer *vb, *vb_next = NULL; + unsigned long flags = 0; + struct ipu_pos ipos; + int ret = 0; + u32 in_fmt = 0; + u32 vdi_cnt = 0; + u32 vdi_frame; + u32 index = 0; + u32 ocrop_h = 0; + u32 o_height = 0; + u32 tiled_interlaced = 0; + bool tiled_fmt = false; + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work begin one frame\n"); + + spin_lock_irqsave(q->irqlock, flags); + + if (list_empty(&vout->active_list)) { + v4l2_warn(vout->vfd->v4l2_dev, + "no entry in active_list, should not be here\n"); + spin_unlock_irqrestore(q->irqlock, flags); + return; + } + + vb = list_first_entry(&vout->active_list, + struct videobuf_buffer, queue); + ret = set_field_fmt(vout, vb->field); + if (ret < 0) { + spin_unlock_irqrestore(q->irqlock, flags); + return; + } + if (deinterlace_3_field(vout)) { + if (list_is_singular(&vout->active_list)) { + if (list_empty(&vout->queue_list)) { + vout->timer_stop = true; + spin_unlock_irqrestore(q->irqlock, flags); + v4l2_warn(vout->vfd->v4l2_dev, + "no enough entry for 3 fields " + "deinterlacer\n"); + return; + } + + /* + * We need to use the next vb even if it is + * not on the active list. + */ + vb_next = list_first_entry(&vout->queue_list, + struct videobuf_buffer, queue); + } else + vb_next = list_first_entry(vout->active_list.next, + struct videobuf_buffer, queue); + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "cur field_fmt:%d, next field_fmt:%d.\n", + vb->field, vb_next->field); + /* repeat the last field during field format changing */ + if ((vb->field != vb_next->field) && + (vb_next->field != V4L2_FIELD_NONE)) + vb_next = vb; + } + + spin_unlock_irqrestore(q->irqlock, flags); + +vdi_frame_rate_double: + mutex_lock(&vout->task_lock); + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "v4l2 frame_cnt:%ld, vb_field:%d, fmt:%d\n", + vout->frame_count, vb->field, + vout->task.input.deinterlace.field_fmt); + if (vb->memory == V4L2_MEMORY_USERPTR) + vout->task.input.paddr = vb->baddr; + else + vout->task.input.paddr = videobuf_to_dma_contig(vb); + + if (vout->task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) + index = vout->vdi_frame_cnt % FB_BUFS; + else + index = vout->frame_count % FB_BUFS; + if (vout->linear_bypass_pp) { + vout->task.output.paddr = vout->task.input.paddr; + ipos.x = vout->task.input.crop.pos.x; + ipos.y = vout->task.input.crop.pos.y; + } else { + if (deinterlace_3_field(vout)) { + if (vb->memory == V4L2_MEMORY_USERPTR) + vout->task.input.paddr_n = vb_next->baddr; + else + vout->task.input.paddr_n = + videobuf_to_dma_contig(vb_next); + } + vout->task.output.paddr = vout->disp_bufs[index]; + if (vout->vdoa_1080p) { + o_height = vout->task.output.height; + ocrop_h = vout->task.output.crop.h; + vout->task.output.height = FRAME_HEIGHT_1080P; + vout->task.output.crop.h = FRAME_HEIGHT_1080P; + } + tiled_fmt = + (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) || + (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format); + if (vout->tiled_bypass_pp) { + ipos.x = vout->task.input.crop.pos.x; + ipos.y = vout->task.input.crop.pos.y; + } else if (tiled_fmt) { + vout->vdoa_task.input.paddr = vout->task.input.paddr; + if (deinterlace_3_field(vout)) + vout->vdoa_task.input.paddr_n = + vout->task.input.paddr_n; + vout->vdoa_task.output.paddr = vout->vdoa_work.paddr; + ret = ipu_queue_task(&vout->vdoa_task); + if (ret < 0) { + mutex_unlock(&vout->task_lock); + goto err; + } + vout->task.input.paddr = vout->vdoa_task.output.paddr; + in_fmt = vout->task.input.format; + vout->task.input.format = vout->vdoa_task.output.format; + if (vout->task.input.deinterlace.enable) { + tiled_interlaced = 1; + vout->task.input.deinterlace.enable = 0; + } + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "tiled queue task\n"); + } + ret = ipu_queue_task(&vout->task); + if ((!vout->tiled_bypass_pp) && tiled_fmt) + vout->task.input.format = in_fmt; + if (tiled_interlaced) + vout->task.input.deinterlace.enable = 1; + if (ret < 0) { + mutex_unlock(&vout->task_lock); + goto err; + } + if (vout->vdoa_1080p) { + vout->task.output.crop.h = ocrop_h; + vout->task.output.height = o_height; + } + } + + mutex_unlock(&vout->task_lock); + + ret = show_buf(vout, index, &ipos); + if (ret < 0) + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "show buf with ret %d\n", ret); + + if (vout->task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) { + vdi_frame = vout->task.input.deinterlace.field_fmt + & IPU_DEINTERLACE_RATE_FRAME1; + if (vdi_frame) + vout->task.input.deinterlace.field_fmt &= + ~IPU_DEINTERLACE_RATE_FRAME1; + else + vout->task.input.deinterlace.field_fmt |= + IPU_DEINTERLACE_RATE_FRAME1; + vout->vdi_frame_cnt++; + vdi_cnt++; + if (vdi_cnt < IPU_DEINTERLACE_MAX_FRAME) + goto vdi_frame_rate_double; + } + spin_lock_irqsave(q->irqlock, flags); + + list_del(&vb->queue); + + /* + * The videobuf before the last one has been shown. Set + * VIDEOBUF_DONE state here to avoid tearing issue in ic bypass + * case, which makes sure a buffer being shown will not be + * dequeued to be overwritten. It also brings side-effect that + * the last 2 buffers can not be dequeued correctly, apps need + * to take care of it. + */ + if (vout->pre2_vb) { + vout->pre2_vb->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->pre2_vb->done); + vout->pre2_vb = NULL; + } + + if (vout->linear_bypass_pp) { + vout->pre2_vb = vout->pre1_vb; + vout->pre1_vb = vb; + } else { + if (vout->pre1_vb) { + vout->pre1_vb->state = VIDEOBUF_DONE; + wake_up_interruptible(&vout->pre1_vb->done); + vout->pre1_vb = NULL; + } + vb->state = VIDEOBUF_DONE; + wake_up_interruptible(&vb->done); + } + + vout->frame_count++; + + /* pick next queue buf to setup timer */ + if (list_empty(&vout->queue_list)) + vout->timer_stop = true; + else { + vb = list_first_entry(&vout->queue_list, + struct videobuf_buffer, queue); + setup_buf_timer(vout, vb); + } + + spin_unlock_irqrestore(q->irqlock, flags); + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work finish one frame\n"); + + return; +err: + v4l2_err(vout->vfd->v4l2_dev, "display work fail ret = %d\n", ret); + vout->timer_stop = true; + vb->state = VIDEOBUF_ERROR; + return; +} + +static enum hrtimer_restart mxc_vout_timer_handler(struct hrtimer *timer) +{ + struct mxc_vout_output *vout = container_of(timer, + struct mxc_vout_output, + timer); + struct videobuf_queue *q = &vout->vbq; + struct videobuf_buffer *vb; + unsigned long flags = 0; + + spin_lock_irqsave(q->irqlock, flags); + + /* + * put first queued entry into active, if previous entry did not + * finish, setup current entry's timer again. + */ + if (list_empty(&vout->queue_list)) { + spin_unlock_irqrestore(q->irqlock, flags); + return HRTIMER_NORESTART; + } + + /* move videobuf from queued list to active list */ + vb = list_first_entry(&vout->queue_list, + struct videobuf_buffer, queue); + list_del(&vb->queue); + list_add_tail(&vb->queue, &vout->active_list); + + if (queue_work(vout->v4l_wq, &vout->disp_work) == 0) { + v4l2_warn(vout->vfd->v4l2_dev, + "disp work was in queue already, queue buf again next time\n"); + list_del(&vb->queue); + list_add(&vb->queue, &vout->queue_list); + spin_unlock_irqrestore(q->irqlock, flags); + return HRTIMER_NORESTART; + } + + vb->state = VIDEOBUF_ACTIVE; + + spin_unlock_irqrestore(q->irqlock, flags); + + return HRTIMER_NORESTART; +} + +/* Video buffer call backs */ + +/* + * Buffer setup function is called by videobuf layer when REQBUF ioctl is + * called. This is used to setup buffers and return size and count of + * buffers allocated. After the call to this buffer, videobuf layer will + * setup buffer queue depending on the size and count of buffers + */ +static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct mxc_vout_output *vout = q->priv_data; + unsigned int frame_size; + + if (!vout) + return -EINVAL; + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) + return -EINVAL; + + frame_size = get_frame_size(vout); + *size = PAGE_ALIGN(frame_size); + + return 0; +} + +/* + * This function will be called when VIDIOC_QBUF ioctl is called. + * It prepare buffers before give out for the display. This function + * converts user space virtual address into physical address if userptr memory + * exchange mechanism is used. + */ +static int mxc_vout_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + vb->state = VIDEOBUF_PREPARED; + return 0; +} + +/* + * Buffer queue funtion will be called from the videobuf layer when _QBUF + * ioctl is called. It is used to enqueue buffer, which is ready to be + * displayed. + * This function is protected by q->irqlock. + */ +static void mxc_vout_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct mxc_vout_output *vout = q->priv_data; + struct videobuf_buffer *active_vb; + + list_add_tail(&vb->queue, &vout->queue_list); + vb->state = VIDEOBUF_QUEUED; + + if (vout->timer_stop) { + if (deinterlace_3_field(vout) && + !list_empty(&vout->active_list)) { + active_vb = list_first_entry(&vout->active_list, + struct videobuf_buffer, queue); + setup_buf_timer(vout, active_vb); + } else { + setup_buf_timer(vout, vb); + } + vout->timer_stop = false; + } +} + +/* + * Buffer release function is called from videobuf layer to release buffer + * which are already allocated + */ +static void mxc_vout_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static int mxc_vout_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret; + struct mxc_vout_output *vout = file->private_data; + + if (!vout) + return -ENODEV; + + ret = videobuf_mmap_mapper(&vout->vbq, vma); + if (ret < 0) + v4l2_err(vout->vfd->v4l2_dev, + "offset invalid [offset=0x%lx]\n", + (vma->vm_pgoff << PAGE_SHIFT)); + + return ret; +} + +static int mxc_vout_release(struct file *file) +{ + unsigned int ret = 0; + struct videobuf_queue *q; + struct mxc_vout_output *vout = file->private_data; + + if (!vout) + return 0; + + if (--vout->open_cnt == 0) { + q = &vout->vbq; + if (q->streaming) + mxc_vidioc_streamoff(file, vout, vout->type); + else { + release_disp_output(vout); + videobuf_queue_cancel(q); + } + destroy_workqueue(vout->v4l_wq); + ret = videobuf_mmap_free(q); + } + + return ret; +} + +static int mxc_vout_open(struct file *file) +{ + struct mxc_vout_output *vout = NULL; + int ret = 0; + + vout = video_drvdata(file); + + if (vout == NULL) + return -ENODEV; + + if (vout->open_cnt++ == 0) { + vout->ctrl_rotate = 0; + vout->ctrl_vflip = 0; + vout->ctrl_hflip = 0; + update_display_setting(); + ret = update_setting_from_fbi(vout, vout->fbi); + if (ret < 0) + goto err; + + vout->v4l_wq = create_singlethread_workqueue("v4l2q"); + if (!vout->v4l_wq) { + v4l2_err(vout->vfd->v4l2_dev, + "Could not create work queue\n"); + ret = -ENOMEM; + goto err; + } + + INIT_WORK(&vout->disp_work, disp_work_func); + + INIT_LIST_HEAD(&vout->queue_list); + INIT_LIST_HEAD(&vout->active_list); + + vout->fmt_init = false; + vout->frame_count = 0; + vout->vdi_frame_cnt = 0; + + vout->win_pos.x = 0; + vout->win_pos.y = 0; + vout->release = true; + } + + file->private_data = vout; + +err: + return ret; +} + +/* + * V4L2 ioctls + */ +static int mxc_vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct mxc_vout_output *vout = fh; + + strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); + strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); + cap->bus_info[0] = '\0'; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; + + return 0; +} + +static int mxc_vidioc_enum_fmt_vid_out(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index >= NUM_MXC_VOUT_FORMATS) + return -EINVAL; + + strlcpy(fmt->description, mxc_formats[fmt->index].description, + sizeof(fmt->description)); + fmt->pixelformat = mxc_formats[fmt->index].pixelformat; + + return 0; +} + +static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mxc_vout_output *vout = fh; + struct v4l2_rect rect; + + f->fmt.pix.width = vout->task.input.width; + f->fmt.pix.height = vout->task.input.height; + f->fmt.pix.pixelformat = vout->task.input.format; + f->fmt.pix.sizeimage = get_frame_size(vout); + + if (f->fmt.pix.priv) { + rect.left = vout->task.input.crop.pos.x; + rect.top = vout->task.input.crop.pos.y; + rect.width = vout->task.input.crop.w; + rect.height = vout->task.input.crop.h; + if (copy_to_user((void __user *)f->fmt.pix.priv, + &rect, sizeof(rect))) + return -EFAULT; + } + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "frame_size:0x%x, pix_fmt:0x%x\n", + f->fmt.pix.sizeimage, + vout->task.input.format); + + return 0; +} + +static inline int ipu_try_task(struct mxc_vout_output *vout) +{ + int ret; + struct ipu_task *task = &vout->task; + +again: + ret = ipu_check_task(task); + if (ret != IPU_CHECK_OK) { + if (ret > IPU_CHECK_ERR_MIN) { + if (ret == IPU_CHECK_ERR_SPLIT_INPUTW_OVER || + ret == IPU_CHECK_ERR_W_DOWNSIZE_OVER) { + task->input.crop.w -= 8; + goto again; + } + if (ret == IPU_CHECK_ERR_SPLIT_INPUTH_OVER || + ret == IPU_CHECK_ERR_H_DOWNSIZE_OVER) { + task->input.crop.h -= 8; + goto again; + } + if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { + if (vout->disp_support_windows) { + task->output.width -= 8; + task->output.crop.w = + task->output.width; + } else + task->output.crop.w -= 8; + goto again; + } + if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { + if (vout->disp_support_windows) { + task->output.height -= 8; + task->output.crop.h = + task->output.height; + } else + task->output.crop.h -= 8; + goto again; + } + ret = -EINVAL; + } + } else + ret = 0; + + return ret; +} + +static inline int vdoaipu_try_task(struct mxc_vout_output *vout) +{ + int ret; + int is_1080p_stream; + size_t size; + struct ipu_task *ipu_task = &vout->task; + struct ipu_crop *icrop = &ipu_task->input.crop; + struct ipu_task *vdoa_task = &vout->vdoa_task; + u32 deinterlace = 0; + u32 in_fmt; + + if (vout->task.input.deinterlace.enable) + deinterlace = 1; + + memset(vdoa_task, 0, sizeof(*vdoa_task)); + vdoa_task->output.format = IPU_PIX_FMT_NV12; + memcpy(&vdoa_task->input, &ipu_task->input, + sizeof(ipu_task->input)); + if ((icrop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (icrop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) { + vdoa_task->input.crop.w = + ALIGN(icrop->w, IPU_PIX_FMT_TILED_NV12_MBALIGN); + vdoa_task->input.crop.h = + ALIGN(icrop->h, IPU_PIX_FMT_TILED_NV12_MBALIGN); + } + vdoa_task->output.width = vdoa_task->input.crop.w; + vdoa_task->output.height = vdoa_task->input.crop.h; + vdoa_task->output.crop.w = vdoa_task->input.crop.w; + vdoa_task->output.crop.h = vdoa_task->input.crop.h; + + size = PAGE_ALIGN(vdoa_task->input.crop.w * + vdoa_task->input.crop.h * + fmt_to_bpp(vdoa_task->output.format)/8); + if (size > vout->vdoa_work.size) { + if (vout->vdoa_work.vaddr) + free_dma_buf(vout, &vout->vdoa_work); + vout->vdoa_work.size = size; + ret = alloc_dma_buf(vout, &vout->vdoa_work); + if (ret < 0) + return ret; + } + ret = ipu_check_task(vdoa_task); + if (ret != IPU_CHECK_OK) + return -EINVAL; + + is_1080p_stream = CHECK_TILED_1080P_STREAM(vout); + if (is_1080p_stream) + ipu_task->input.crop.h = VALID_HEIGHT_1080P; + in_fmt = ipu_task->input.format; + ipu_task->input.format = vdoa_task->output.format; + ipu_task->input.height = vdoa_task->output.height; + ipu_task->input.width = vdoa_task->output.width; + if (deinterlace) + ipu_task->input.deinterlace.enable = 0; + ret = ipu_try_task(vout); + if (deinterlace) + ipu_task->input.deinterlace.enable = 1; + ipu_task->input.format = in_fmt; + + return ret; +} + +static int mxc_vout_try_task(struct mxc_vout_output *vout) +{ + int ret = 0; + struct ipu_output *output = &vout->task.output; + struct ipu_input *input = &vout->task.input; + struct ipu_crop *crop = &input->crop; + u32 o_height = 0; + u32 ocrop_h = 0; + bool tiled_fmt = false; + bool tiled_need_pp = false; + + vout->vdoa_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (vout->vdoa_1080p) { + input->crop.h = FRAME_HEIGHT_1080P; + o_height = output->height; + ocrop_h = output->crop.h; + output->height = FRAME_HEIGHT_1080P; + output->crop.h = FRAME_HEIGHT_1080P; + } + + if ((IPU_PIX_FMT_TILED_NV12 == input->format) || + (IPU_PIX_FMT_TILED_NV12F == input->format)) { + if ((input->width % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (input->height % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN)) { + v4l2_err(vout->vfd->v4l2_dev, + "ERR: tiled fmt needs 16 pixel align.\n"); + return -EINVAL; + } + if ((crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) || + (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) + tiled_need_pp = true; + } else { + crop->w -= crop->w % 8; + crop->h -= crop->h % 8; + } + /* assume task.output already set by S_CROP */ + vout->linear_bypass_pp = is_pp_bypass(vout); + if (vout->linear_bypass_pp) { + v4l2_info(vout->vfd->v4l2_dev, "Bypass IC.\n"); + output->format = input->format; + } else { + /* if need CSC, choose IPU-DP or IPU_IC do it */ + if (vout->disp_support_csc) { + if (colorspaceofpixel(input->format) == YUV_CS) + output->format = IPU_PIX_FMT_UYVY; + else + output->format = IPU_PIX_FMT_RGB565; + } else { + if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) + output->format = IPU_PIX_FMT_UYVY; + else + output->format = IPU_PIX_FMT_RGB565; + } + + vout->tiled_bypass_pp = false; + if ((IPU_PIX_FMT_TILED_NV12 == input->format) || + (IPU_PIX_FMT_TILED_NV12F == input->format)) { + /* check resize/rotate/flip, or csc task */ + if (!(tiled_need_pp || + (IPU_ROTATE_NONE != output->rotate) || + (input->crop.w != output->crop.w) || + (input->crop.h != output->crop.h) || + (!vout->disp_support_csc && + (colorspaceofpixel(vout->disp_fmt) == RGB_CS))) + ) { + /* IC bypass */ + output->format = IPU_PIX_FMT_NV12; + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "tiled bypass pp\n"); + vout->tiled_bypass_pp = true; + } + tiled_fmt = true; + } + + if ((!vout->tiled_bypass_pp) && tiled_fmt) + ret = vdoaipu_try_task(vout); + else + ret = ipu_try_task(vout); + } + + if (vout->vdoa_1080p) { + output->height = o_height; + output->crop.h = ocrop_h; + } + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "icrop.w:%u, icrop.h:%u, iw:%u, ih:%u," + "ocrop.w:%u, ocrop.h:%u, ow:%u, oh:%u\n", + input->crop.w, input->crop.h, + input->width, input->height, + output->crop.w, output->crop.h, + output->width, output->height); + return ret; +} + +static int mxc_vout_try_format(struct mxc_vout_output *vout, + struct v4l2_format *f) +{ + int ret = 0; + struct v4l2_rect rect; + + if ((f->fmt.pix.field != V4L2_FIELD_NONE) && + (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format)) { + v4l2_err(vout->vfd->v4l2_dev, + "progressive tiled fmt should used V4L2_FIELD_NONE!\n"); + return -EINVAL; + } + + if (f->fmt.pix.priv && copy_from_user(&rect, + (void __user *)f->fmt.pix.priv, sizeof(rect))) + return -EFAULT; + + vout->task.input.width = f->fmt.pix.width; + vout->task.input.height = f->fmt.pix.height; + vout->task.input.format = f->fmt.pix.pixelformat; + + ret = set_field_fmt(vout, f->fmt.pix.field); + if (ret < 0) + return ret; + + if (f->fmt.pix.priv) { + vout->task.input.crop.pos.x = rect.left; + vout->task.input.crop.pos.y = rect.top; + vout->task.input.crop.w = rect.width; + vout->task.input.crop.h = rect.height; + } else { + vout->task.input.crop.pos.x = 0; + vout->task.input.crop.pos.y = 0; + vout->task.input.crop.w = f->fmt.pix.width; + vout->task.input.crop.h = f->fmt.pix.height; + } + memcpy(&vout->in_rect, &vout->task.input.crop, sizeof(vout->in_rect)); + + ret = mxc_vout_try_task(vout); + if (!ret) { + if (f->fmt.pix.priv) { + rect.width = vout->task.input.crop.w; + rect.height = vout->task.input.crop.h; + if (copy_to_user((void __user *)f->fmt.pix.priv, + &rect, sizeof(rect))) + ret = -EFAULT; + } else { + f->fmt.pix.width = vout->task.input.crop.w; + f->fmt.pix.height = vout->task.input.crop.h; + } + } + + return ret; +} + +static bool mxc_vout_need_fb_reconfig(struct mxc_vout_output *vout, + struct mxc_vout_output *pre_vout) +{ + if (!vout->vbq.streaming) + return false; + + if (vout->tiled_bypass_pp) + return true; + + if (vout->linear_bypass_pp != pre_vout->linear_bypass_pp) + return true; + + /* cropped output resolution or format are changed */ + if (vout->task.output.format != pre_vout->task.output.format || + vout->task.output.crop.w != pre_vout->task.output.crop.w || + vout->task.output.crop.h != pre_vout->task.output.crop.h) + return true; + + /* overlay: window position or resolution are changed */ + if (vout->disp_support_windows && + (vout->win_pos.x != pre_vout->win_pos.x || + vout->win_pos.y != pre_vout->win_pos.y || + vout->task.output.width != pre_vout->task.output.width || + vout->task.output.height != pre_vout->task.output.height)) + return true; + + /* background: cropped position is changed */ + if (!vout->disp_support_windows && + (vout->task.output.crop.pos.x != + pre_vout->task.output.crop.pos.x || + vout->task.output.crop.pos.y != + pre_vout->task.output.crop.pos.y)) + return true; + + return false; +} + +static int mxc_vidioc_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mxc_vout_output *vout = fh; + int ret = 0; + + if (vout->vbq.streaming) + return -EBUSY; + + mutex_lock(&vout->task_lock); + ret = mxc_vout_try_format(vout, f); + if (ret >= 0) + vout->fmt_init = true; + mutex_unlock(&vout->task_lock); + + return ret; +} + +static int mxc_vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + struct mxc_vout_output *vout = fh; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + cropcap->bounds = vout->crop_bounds; + cropcap->defrect = vout->crop_bounds; + + return 0; +} + +static int mxc_vidioc_g_crop(struct file *file, void *fh, + struct v4l2_crop *crop) +{ + struct mxc_vout_output *vout = fh; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (vout->disp_support_windows) { + crop->c.left = vout->win_pos.x; + crop->c.top = vout->win_pos.y; + crop->c.width = vout->task.output.width; + crop->c.height = vout->task.output.height; + } else { + if (vout->task.output.crop.w && vout->task.output.crop.h) { + crop->c.left = vout->task.output.crop.pos.x; + crop->c.top = vout->task.output.crop.pos.y; + crop->c.width = vout->task.output.crop.w; + crop->c.height = vout->task.output.crop.h; + } else { + crop->c.left = 0; + crop->c.top = 0; + crop->c.width = vout->task.output.width; + crop->c.height = vout->task.output.height; + } + } + + return 0; +} + +static int mxc_vidioc_s_crop(struct file *file, void *fh, + const struct v4l2_crop *crop) +{ + struct mxc_vout_output *vout = fh, *pre_vout; + struct v4l2_rect *b = &vout->crop_bounds; + struct v4l2_crop fix_up_crop; + int ret = 0; + + memcpy(&fix_up_crop, crop, sizeof(*crop)); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (crop->c.width < 0 || crop->c.height < 0) + return -EINVAL; + + if (crop->c.width == 0) + fix_up_crop.c.width = b->width - b->left; + if (crop->c.height == 0) + fix_up_crop.c.height = b->height - b->top; + + if (crop->c.top < b->top) + fix_up_crop.c.top = b->top; + if (crop->c.top >= b->top + b->height) + fix_up_crop.c.top = b->top + b->height - 1; + if (crop->c.height > b->top - crop->c.top + b->height) + fix_up_crop.c.height = + b->top - fix_up_crop.c.top + b->height; + + if (crop->c.left < b->left) + fix_up_crop.c.left = b->left; + if (crop->c.left >= b->left + b->width) + fix_up_crop.c.left = b->left + b->width - 1; + if (crop->c.width > b->left - crop->c.left + b->width) + fix_up_crop.c.width = + b->left - fix_up_crop.c.left + b->width; + + /* stride line limitation */ + fix_up_crop.c.height -= fix_up_crop.c.height % 8; + fix_up_crop.c.width -= fix_up_crop.c.width % 8; + if ((fix_up_crop.c.width <= 0) || (fix_up_crop.c.height <= 0) || + ((fix_up_crop.c.left + fix_up_crop.c.width) > + (b->left + b->width)) || + ((fix_up_crop.c.top + fix_up_crop.c.height) > + (b->top + b->height))) { + v4l2_err(vout->vfd->v4l2_dev, "s_crop err: %d, %d, %d, %d", + fix_up_crop.c.left, fix_up_crop.c.top, + fix_up_crop.c.width, fix_up_crop.c.height); + return -EINVAL; + } + + /* the same setting, return */ + if (vout->disp_support_windows) { + if ((vout->win_pos.x == fix_up_crop.c.left) && + (vout->win_pos.y == fix_up_crop.c.top) && + (vout->task.output.crop.w == fix_up_crop.c.width) && + (vout->task.output.crop.h == fix_up_crop.c.height)) + return 0; + } else { + if ((vout->task.output.crop.pos.x == fix_up_crop.c.left) && + (vout->task.output.crop.pos.y == fix_up_crop.c.top) && + (vout->task.output.crop.w == fix_up_crop.c.width) && + (vout->task.output.crop.h == fix_up_crop.c.height)) + return 0; + } + + pre_vout = vmalloc(sizeof(*pre_vout)); + if (!pre_vout) + return -ENOMEM; + + /* wait current work finish */ + if (vout->vbq.streaming) + flush_workqueue(vout->v4l_wq); + + mutex_lock(&vout->task_lock); + + memcpy(pre_vout, vout, sizeof(*vout)); + + if (vout->disp_support_windows) { + vout->task.output.crop.pos.x = 0; + vout->task.output.crop.pos.y = 0; + vout->win_pos.x = fix_up_crop.c.left; + vout->win_pos.y = fix_up_crop.c.top; + vout->task.output.width = fix_up_crop.c.width; + vout->task.output.height = fix_up_crop.c.height; + } else { + vout->task.output.crop.pos.x = fix_up_crop.c.left; + vout->task.output.crop.pos.y = fix_up_crop.c.top; + } + + vout->task.output.crop.w = fix_up_crop.c.width; + vout->task.output.crop.h = fix_up_crop.c.height; + + /* + * must S_CROP before S_FMT, for fist time S_CROP, will not check + * ipu task, it will check in S_FMT, after S_FMT, S_CROP should + * check ipu task too. + */ + if (vout->fmt_init) { + memcpy(&vout->task.input.crop, &vout->in_rect, + sizeof(vout->in_rect)); + ret = mxc_vout_try_task(vout); + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, + "vout check task failed\n"); + memcpy(vout, pre_vout, sizeof(*vout)); + goto done; + } + + if (mxc_vout_need_fb_reconfig(vout, pre_vout)) { + ret = config_disp_output(vout); + if (ret < 0) + v4l2_err(vout->vfd->v4l2_dev, + "Config display output failed\n"); + } + } + +done: + vfree(pre_vout); + mutex_unlock(&vout->task_lock); + + return ret; +} + +static int mxc_vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *ctrl) +{ + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0); + break; + case V4L2_CID_VFLIP: + ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); + break; + case V4L2_CID_HFLIP: + ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); + break; + case V4L2_CID_MXC_MOTION: + ret = v4l2_ctrl_query_fill(ctrl, 0, 2, 1, 0); + break; + default: + ctrl->name[0] = '\0'; + ret = -EINVAL; + } + return ret; +} + +static int mxc_vidioc_g_ctrl(struct file *file, void *fh, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct mxc_vout_output *vout = fh; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ctrl->value = vout->ctrl_rotate; + break; + case V4L2_CID_VFLIP: + ctrl->value = vout->ctrl_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = vout->ctrl_hflip; + break; + case V4L2_CID_MXC_MOTION: + if (vout->task.input.deinterlace.enable) + ctrl->value = vout->task.input.deinterlace.motion; + else + ctrl->value = 0; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static void setup_task_rotation(struct mxc_vout_output *vout) +{ + if (vout->ctrl_rotate == 0) { + if (vout->ctrl_vflip && vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_180; + else if (vout->ctrl_vflip) + vout->task.output.rotate = IPU_ROTATE_VERT_FLIP; + else if (vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP; + else + vout->task.output.rotate = IPU_ROTATE_NONE; + } else if (vout->ctrl_rotate == 90) { + if (vout->ctrl_vflip && vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_90_LEFT; + else if (vout->ctrl_vflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP; + else if (vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP; + else + vout->task.output.rotate = IPU_ROTATE_90_RIGHT; + } else if (vout->ctrl_rotate == 180) { + if (vout->ctrl_vflip && vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_NONE; + else if (vout->ctrl_vflip) + vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP; + else if (vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_VERT_FLIP; + else + vout->task.output.rotate = IPU_ROTATE_180; + } else if (vout->ctrl_rotate == 270) { + if (vout->ctrl_vflip && vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT; + else if (vout->ctrl_vflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP; + else if (vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP; + else + vout->task.output.rotate = IPU_ROTATE_90_LEFT; + } +} + +static int mxc_vidioc_s_ctrl(struct file *file, void *fh, + struct v4l2_control *ctrl) +{ + int ret = 0; + struct mxc_vout_output *vout = fh, *pre_vout; + + pre_vout = vmalloc(sizeof(*pre_vout)); + if (!pre_vout) + return -ENOMEM; + + /* wait current work finish */ + if (vout->vbq.streaming) + flush_workqueue(vout->v4l_wq); + + mutex_lock(&vout->task_lock); + + memcpy(pre_vout, vout, sizeof(*vout)); + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + { + vout->ctrl_rotate = (ctrl->value/90) * 90; + if (vout->ctrl_rotate > 270) + vout->ctrl_rotate = 270; + setup_task_rotation(vout); + break; + } + case V4L2_CID_VFLIP: + { + vout->ctrl_vflip = ctrl->value; + setup_task_rotation(vout); + break; + } + case V4L2_CID_HFLIP: + { + vout->ctrl_hflip = ctrl->value; + setup_task_rotation(vout); + break; + } + case V4L2_CID_MXC_MOTION: + { + vout->task.input.deinterlace.motion = ctrl->value; + break; + } + default: + ret = -EINVAL; + goto done; + } + + if (vout->fmt_init) { + memcpy(&vout->task.input.crop, &vout->in_rect, + sizeof(vout->in_rect)); + ret = mxc_vout_try_task(vout); + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, + "vout check task failed\n"); + memcpy(vout, pre_vout, sizeof(*vout)); + goto done; + } + + if (mxc_vout_need_fb_reconfig(vout, pre_vout)) { + ret = config_disp_output(vout); + if (ret < 0) + v4l2_err(vout->vfd->v4l2_dev, + "Config display output failed\n"); + } + } + +done: + vfree(pre_vout); + mutex_unlock(&vout->task_lock); + + return ret; +} + +static int mxc_vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + int ret = 0; + struct mxc_vout_output *vout = fh; + struct videobuf_queue *q = &vout->vbq; + + if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + /* should not be here after streaming, videobuf_reqbufs will control */ + mutex_lock(&vout->task_lock); + + ret = videobuf_reqbufs(q, req); + + mutex_unlock(&vout->task_lock); + return ret; +} + +static int mxc_vidioc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + int ret; + struct mxc_vout_output *vout = fh; + + ret = videobuf_querybuf(&vout->vbq, b); + if (!ret) { + /* return physical address */ + struct videobuf_buffer *vb = vout->vbq.bufs[b->index]; + if (b->flags & V4L2_BUF_FLAG_MAPPED) + b->m.offset = videobuf_to_dma_contig(vb); + } + + return ret; +} + +static int mxc_vidioc_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buffer) +{ + struct mxc_vout_output *vout = fh; + + return videobuf_qbuf(&vout->vbq, buffer); +} + +static int mxc_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct mxc_vout_output *vout = fh; + + if (!vout->vbq.streaming) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) + return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 1); + else + return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 0); +} + +static int set_window_position(struct mxc_vout_output *vout, + struct mxcfb_pos *pos) +{ + struct fb_info *fbi = vout->fbi; + mm_segment_t old_fs; + int ret = 0; + + if (vout->disp_support_windows) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS, + (unsigned long)pos); + set_fs(old_fs); + } + + return ret; +} + +static int config_disp_output(struct mxc_vout_output *vout) +{ + struct dma_mem *buf = NULL; + struct fb_info *fbi = vout->fbi; + struct fb_var_screeninfo var; + struct mxcfb_pos pos; + int i, fb_num, ret; + u32 fb_base; + u32 size; + u32 display_buf_size; + u32 *pixel = NULL; + u32 color; + int j; + + memcpy(&var, &fbi->var, sizeof(var)); + fb_base = fbi->fix.smem_start; + + var.xres = vout->task.output.width; + var.yres = vout->task.output.height; + if (vout->linear_bypass_pp || vout->tiled_bypass_pp) { + fb_num = 1; + /* input crop */ + if (vout->task.input.width > vout->task.output.width) + var.xres_virtual = vout->task.input.width; + else + var.xres_virtual = var.xres; + if (vout->task.input.height > vout->task.output.height) + var.yres_virtual = vout->task.input.height; + else + var.yres_virtual = var.yres; + var.rotate = vout->task.output.rotate; + var.vmode |= FB_VMODE_YWRAP; + } else { + fb_num = FB_BUFS; + var.xres_virtual = var.xres; + var.yres_virtual = fb_num * var.yres; + var.vmode &= ~FB_VMODE_YWRAP; + } + var.bits_per_pixel = fmt_to_bpp(vout->task.output.format); + var.nonstd = vout->task.output.format; + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "set display fb to %d %d\n", + var.xres, var.yres); + + /* + * To setup the overlay fb from scratch without + * the last time overlay fb position or resolution's + * impact, we take the following steps: + * - blank fb + * - set fb position to the starting point + * - reconfigure fb + * - set fb position to a specific point + * - unblank fb + * This procedure applies to non-overlay fbs as well. + */ + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + fb_blank(fbi, FB_BLANK_POWERDOWN); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + + pos.x = 0; + pos.y = 0; + ret = set_window_position(vout, &pos); + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, "failed to set fb position " + "to starting point\n"); + return ret; + } + + /* Init display channel through fb API */ + var.yoffset = 0; + var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + ret = fb_set_var(fbi, &var); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, + "ERR:%s fb_set_var ret:%d\n", __func__, ret); + return ret; + } + + ret = set_window_position(vout, &vout->win_pos); + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, "failed to set fb position\n"); + return ret; + } + + if (vout->linear_bypass_pp || vout->tiled_bypass_pp) + display_buf_size = fbi->fix.line_length * fbi->var.yres_virtual; + else + display_buf_size = fbi->fix.line_length * fbi->var.yres; + for (i = 0; i < fb_num; i++) + vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size; + if (vout->tiled_bypass_pp) { + size = PAGE_ALIGN(vout->task.input.crop.w * + vout->task.input.crop.h * + fmt_to_bpp(vout->task.output.format)/8); + if (size > vout->vdoa_output[0].size) { + for (i = 0; i < VDOA_FB_BUFS; i++) { + buf = &vout->vdoa_output[i]; + if (buf->vaddr) + free_dma_buf(vout, buf); + buf->size = size; + ret = alloc_dma_buf(vout, buf); + if (ret < 0) + goto err; + } + } + for (i = fb_num; i < (fb_num + VDOA_FB_BUFS); i++) + vout->disp_bufs[i] = + vout->vdoa_output[i - fb_num].paddr; + } + vout->fb_smem_len = fbi->fix.smem_len; + vout->fb_smem_start = fbi->fix.smem_start; + if (fb_base != fbi->fix.smem_start) { + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "realloc fb mem size:0x%x@0x%lx,old paddr @0x%x\n", + fbi->fix.smem_len, fbi->fix.smem_start, fb_base); + } + + /* fill black when video config changed */ + color = colorspaceofpixel(vout->task.output.format) == YUV_CS ? + UYVY_BLACK : RGB_BLACK; + if (IS_PLANAR_PIXEL_FORMAT(vout->task.output.format)) { + size = display_buf_size * 8 / + fmt_to_bpp(vout->task.output.format); + memset(fbi->screen_base, Y_BLACK, size); + memset(fbi->screen_base + size, UV_BLACK, + display_buf_size - size); + } else { + pixel = (u32 *)fbi->screen_base; + for (i = 0; i < (display_buf_size >> 2); i++) + *pixel++ = color; + } + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + ret = fb_blank(fbi, FB_BLANK_UNBLANK); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + vout->release = false; + + return ret; +err: + for (j = i - 1; j >= 0; j--) { + buf = &vout->vdoa_output[j]; + if (buf->vaddr) + free_dma_buf(vout, buf); + } + return ret; +} + +static inline void wait_for_vsync(struct mxc_vout_output *vout) +{ + struct fb_info *fbi = vout->fbi; + mm_segment_t old_fs; + + if (fbi->fbops->fb_ioctl) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC, + (unsigned long)NULL); + set_fs(old_fs); + } + + return; +} + +static void release_disp_output(struct mxc_vout_output *vout) +{ + struct fb_info *fbi = vout->fbi; + struct mxcfb_pos pos; + + if (vout->release) + return; + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + fb_blank(fbi, FB_BLANK_POWERDOWN); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + + /* restore pos to 0,0 avoid fb pan display hang? */ + pos.x = 0; + pos.y = 0; + set_window_position(vout, &pos); + + if (get_ipu_channel(fbi) == MEM_BG_SYNC) { + console_lock(); + fbi->fix.smem_start = vout->disp_bufs[0]; + fbi->flags |= FBINFO_MISC_USEREVENT; + fb_blank(fbi, FB_BLANK_UNBLANK); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + + } + + vout->release = true; +} + +static int mxc_vidioc_streamon(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct mxc_vout_output *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int ret; + + if (q->streaming) { + v4l2_err(vout->vfd->v4l2_dev, + "video output already run\n"); + ret = -EBUSY; + goto done; + } + + if (deinterlace_3_field(vout) && list_is_singular(&q->stream)) { + v4l2_err(vout->vfd->v4l2_dev, + "deinterlacing: need queue 2 frame before streamon\n"); + ret = -EINVAL; + goto done; + } + + ret = config_disp_output(vout); + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, + "Config display output failed\n"); + goto done; + } + + hrtimer_init(&vout->timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); + vout->timer.function = mxc_vout_timer_handler; + vout->timer_stop = true; + + vout->start_ktime = hrtimer_cb_get_time(&vout->timer); + + vout->pre1_vb = NULL; + vout->pre2_vb = NULL; + + ret = videobuf_streamon(q); +done: + return ret; +} + +static int mxc_vidioc_streamoff(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct mxc_vout_output *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int ret = 0; + + if (q->streaming) { + flush_workqueue(vout->v4l_wq); + + hrtimer_cancel(&vout->timer); + + /* + * Wait for 2 vsyncs to make sure + * frames are drained on triple + * buffer. + */ + wait_for_vsync(vout); + wait_for_vsync(vout); + + release_disp_output(vout); + + ret = videobuf_streamoff(&vout->vbq); + } + INIT_LIST_HEAD(&vout->queue_list); + INIT_LIST_HEAD(&vout->active_list); + + return ret; +} + +static const struct v4l2_ioctl_ops mxc_vout_ioctl_ops = { + .vidioc_querycap = mxc_vidioc_querycap, + .vidioc_enum_fmt_vid_out = mxc_vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = mxc_vidioc_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = mxc_vidioc_s_fmt_vid_out, + .vidioc_cropcap = mxc_vidioc_cropcap, + .vidioc_g_crop = mxc_vidioc_g_crop, + .vidioc_s_crop = mxc_vidioc_s_crop, + .vidioc_queryctrl = mxc_vidioc_queryctrl, + .vidioc_g_ctrl = mxc_vidioc_g_ctrl, + .vidioc_s_ctrl = mxc_vidioc_s_ctrl, + .vidioc_reqbufs = mxc_vidioc_reqbufs, + .vidioc_querybuf = mxc_vidioc_querybuf, + .vidioc_qbuf = mxc_vidioc_qbuf, + .vidioc_dqbuf = mxc_vidioc_dqbuf, + .vidioc_streamon = mxc_vidioc_streamon, + .vidioc_streamoff = mxc_vidioc_streamoff, +}; + +static const struct v4l2_file_operations mxc_vout_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .mmap = mxc_vout_mmap, + .open = mxc_vout_open, + .release = mxc_vout_release, +}; + +static struct video_device mxc_vout_template = { + .name = "MXC Video Output", + .fops = &mxc_vout_fops, + .ioctl_ops = &mxc_vout_ioctl_ops, + .release = video_device_release, +}; + +static struct videobuf_queue_ops mxc_vout_vbq_ops = { + .buf_setup = mxc_vout_buffer_setup, + .buf_prepare = mxc_vout_buffer_prepare, + .buf_release = mxc_vout_buffer_release, + .buf_queue = mxc_vout_buffer_queue, +}; + +static void mxc_vout_free_output(struct mxc_vout_dev *dev) +{ + int i; + int j; + struct mxc_vout_output *vout; + struct video_device *vfd; + + for (i = 0; i < dev->out_num; i++) { + vout = dev->out[i]; + vfd = vout->vfd; + if (vout->vdoa_work.vaddr) + free_dma_buf(vout, &vout->vdoa_work); + for (j = 0; j < VDOA_FB_BUFS; j++) { + if (vout->vdoa_output[j].vaddr) + free_dma_buf(vout, &vout->vdoa_output[j]); + } + if (vfd) { + if (!video_is_registered(vfd)) + video_device_release(vfd); + else + video_unregister_device(vfd); + } + kfree(vout); + } +} + +static int mxc_vout_setup_output(struct mxc_vout_dev *dev) +{ + struct videobuf_queue *q; + struct fb_info *fbi; + struct mxc_vout_output *vout; + int i, ret = 0; + + update_display_setting(); + + /* all output/overlay based on fb */ + for (i = 0; i < num_registered_fb; i++) { + fbi = registered_fb[i]; + + vout = kzalloc(sizeof(struct mxc_vout_output), GFP_KERNEL); + if (!vout) { + ret = -ENOMEM; + break; + } + + dev->out[dev->out_num] = vout; + dev->out_num++; + + vout->fbi = fbi; + vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + vout->vfd = video_device_alloc(); + if (!vout->vfd) { + ret = -ENOMEM; + break; + } + + *vout->vfd = mxc_vout_template; + vout->vfd->debug = debug; + vout->vfd->v4l2_dev = &dev->v4l2_dev; + vout->vfd->lock = &vout->mutex; + vout->vfd->vfl_dir = VFL_DIR_TX; + + mutex_init(&vout->mutex); + mutex_init(&vout->task_lock); + + strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name)); + + video_set_drvdata(vout->vfd, vout); + + if (video_register_device(vout->vfd, + VFL_TYPE_GRABBER, video_nr + i) < 0) { + ret = -ENODEV; + break; + } + + q = &vout->vbq; + q->dev = dev->dev; + spin_lock_init(&vout->vbq_lock); + videobuf_queue_dma_contig_init(q, &mxc_vout_vbq_ops, q->dev, + &vout->vbq_lock, vout->type, V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), vout, NULL); + + v4l2_info(vout->vfd->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(vout->vfd)); + + } + + return ret; +} + +static int mxc_vout_probe(struct platform_device *pdev) +{ + int ret; + struct mxc_vout_dev *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->dev = &pdev->dev; + dev->dev->dma_mask = kmalloc(sizeof(*dev->dev->dma_mask), GFP_KERNEL); + *dev->dev->dma_mask = DMA_BIT_MASK(32); + dev->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = v4l2_device_register(dev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(dev->dev, "v4l2_device_register failed\n"); + goto free_dev; + } + + ret = mxc_vout_setup_output(dev); + if (ret < 0) + goto rel_vdev; + + return 0; + +rel_vdev: + mxc_vout_free_output(dev); + v4l2_device_unregister(&dev->v4l2_dev); +free_dev: + kfree(dev); + return ret; +} + +static int mxc_vout_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct mxc_vout_dev *dev = container_of(v4l2_dev, struct + mxc_vout_dev, v4l2_dev); + + mxc_vout_free_output(dev); + v4l2_device_unregister(v4l2_dev); + kfree(dev); + return 0; +} + +static const struct of_device_id mxc_v4l2_dt_ids[] = { + { .compatible = "fsl,mxc_v4l2_output", }, + { /* sentinel */ } +}; + +static struct platform_driver mxc_vout_driver = { + .driver = { + .name = "mxc_v4l2_output", + .of_match_table = mxc_v4l2_dt_ids, + }, + .probe = mxc_vout_probe, + .remove = mxc_vout_remove, +}; + +static int __init mxc_vout_init(void) +{ + if (platform_driver_register(&mxc_vout_driver) != 0) { + printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n"); + return -EINVAL; + } + return 0; +} + +static void mxc_vout_cleanup(void) +{ + platform_driver_unregister(&mxc_vout_driver); +} + +module_init(mxc_vout_init); +module_exit(mxc_vout_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("V4L2-driver for MXC video output"); +MODULE_LICENSE("GPL"); diff -Nur linux-3.10.30/drivers/media/v4l2-core/videobuf-dma-contig.c linux-3.10.30-cubox-i/drivers/media/v4l2-core/videobuf-dma-contig.c --- linux-3.10.30/drivers/media/v4l2-core/videobuf-dma-contig.c 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/media/v4l2-core/videobuf-dma-contig.c 2014-03-08 20:33:51.000000000 +0100 @@ -307,7 +307,7 @@ size = vma->vm_end - vma->vm_start; size = (size < mem->size) ? size : mem->size; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); retval = remap_pfn_range(vma, vma->vm_start, mem->dma_handle >> PAGE_SHIFT, size, vma->vm_page_prot); diff -Nur linux-3.10.30/drivers/mfd/Kconfig linux-3.10.30-cubox-i/drivers/mfd/Kconfig --- linux-3.10.30/drivers/mfd/Kconfig 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/mfd/Kconfig 2014-03-08 20:33:51.000000000 +0100 @@ -139,6 +139,13 @@ This driver can be built as a module. If built as a module it will be called "da9055" +config MFD_MXC_HDMI + tristate "Freescale HDMI Core" + select MFD_CORE + help + This is the core driver for the Freescale i.MX6 on-chip HDMI. + This MFD driver connects with the video and audio drivers for HDMI. + config MFD_MC13783 tristate @@ -277,6 +284,13 @@ select individual components like voltage regulators, RTC and battery-charger under the corresponding menus. +config MFD_MAX17135 + tristate "Maxim MAX17135 EPD PMIC core" + depends on I2C + help + This is the MAX17135 PMIC support. It includes + core support for communication with the MAX17135 chip. + config MFD_MAX77686 bool "Maxim Semiconductor MAX77686 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS @@ -1144,7 +1158,16 @@ endmenu config VEXPRESS_CONFIG - bool + bool "ARM Versatile Express platform infrastructure" + depends on ARM || ARM64 help Platform configuration infrastructure for the ARM Ltd. Versatile Express. + +config VEXPRESS_SPC + bool "Versatile Express SPC driver support" + depends on ARM + depends on VEXPRESS_CONFIG + help + Serial Power Controller driver for ARM Ltd. test chips. + diff -Nur linux-3.10.30/drivers/mfd/Makefile linux-3.10.30-cubox-i/drivers/mfd/Makefile --- linux-3.10.30/drivers/mfd/Makefile 2014-02-13 22:48:15.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/mfd/Makefile 2014-03-08 20:33:51.000000000 +0100 @@ -104,6 +104,7 @@ da9055-objs := da9055-core.o da9055-i2c.o obj-$(CONFIG_MFD_DA9055) += da9055.o +obj-$(CONFIG_MFD_MAX17135) += max17135-core.o obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o obj-$(CONFIG_MFD_MAX8907) += max8907.o @@ -153,5 +154,7 @@ obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o +obj-$(CONFIG_VEXPRESS_SPC) += vexpress-spc.o obj-$(CONFIG_MFD_RETU) += retu-mfd.o obj-$(CONFIG_MFD_AS3711) += as3711.o +obj-$(CONFIG_MFD_MXC_HDMI) += mxc-hdmi-core.o diff -Nur linux-3.10.30/drivers/mfd/max17135-core.c linux-3.10.30-cubox-i/drivers/mfd/max17135-core.c --- linux-3.10.30/drivers/mfd/max17135-core.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/mfd/max17135-core.c 2014-03-08 20:33:52.000000000 +0100 @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2010-2013 Freescale Semiconductor, 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 + * + */ + +/*! + * @file pmic/core/max17135.c + * @brief This file contains MAX17135 specific PMIC code. This implementaion + * may differ for each PMIC chip. + * + * @ingroup PMIC_CORE + */ + +/* + * Includes + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int max17135_detect(struct i2c_client *client, + struct i2c_board_info *info); +struct i2c_client *max17135_client; +static struct regulator *gpio_regulator; + +static struct mfd_cell max17135_devs[] = { + { .name = "max17135-pmic", }, + { .name = "max17135-sns", }, +}; + +static const unsigned short normal_i2c[] = {0x48, I2C_CLIENT_END}; + +int max17135_reg_read(int reg_num, unsigned int *reg_val) +{ + int result; + + if (max17135_client == NULL) + return PMIC_ERROR; + + if ((reg_num == REG_MAX17135_EXT_TEMP) || + (reg_num == REG_MAX17135_INT_TEMP)) { + result = i2c_smbus_read_word_data(max17135_client, reg_num); + if (result < 0) { + dev_err(&max17135_client->dev, + "Unable to read MAX17135 register via I2C\n"); + return PMIC_ERROR; + } + /* Swap bytes for dword read */ + result = (result >> 8) | ((result & 0xFF) << 8); + } else { + result = i2c_smbus_read_byte_data(max17135_client, reg_num); + if (result < 0) { + dev_err(&max17135_client->dev, + "Unable to read MAX17135 register via I2C\n"); + return PMIC_ERROR; + } + } + + *reg_val = result; + return PMIC_SUCCESS; +} + +int max17135_reg_write(int reg_num, const unsigned int reg_val) +{ + int result; + + if (max17135_client == NULL) + return PMIC_ERROR; + + result = i2c_smbus_write_byte_data(max17135_client, reg_num, reg_val); + if (result < 0) { + dev_err(&max17135_client->dev, + "Unable to write MAX17135 register via I2C\n"); + return PMIC_ERROR; + } + + return PMIC_SUCCESS; +} + +#ifdef CONFIG_OF +static struct max17135_platform_data *max17135_i2c_parse_dt_pdata( + struct device *dev) +{ + struct max17135_platform_data *pdata; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } + + return pdata; +} +#else +static struct max17135_platform_data *max17135_i2c_parse_dt_pdata( + struct device *dev) +{ + return NULL; +} +#endif /* !CONFIG_OF */ + +static int max17135_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max17135 *max17135; + struct max17135_platform_data *pdata = client->dev.platform_data; + struct device_node *np = client->dev.of_node; + int ret = 0; + + if (!np) + return -ENODEV; + + gpio_regulator = devm_regulator_get(&client->dev, "SENSOR"); + if (!IS_ERR(gpio_regulator)) { + ret = regulator_enable(gpio_regulator); + if (ret) { + dev_err(&client->dev, "gpio set voltage error\n"); + return ret; + } + } + + /* Create the PMIC data structure */ + max17135 = kzalloc(sizeof(struct max17135), GFP_KERNEL); + if (max17135 == NULL) { + kfree(client); + return -ENOMEM; + } + + /* Initialize the PMIC data structure */ + i2c_set_clientdata(client, max17135); + max17135->dev = &client->dev; + max17135->i2c_client = client; + + max17135_client = client; + ret = max17135_detect(client, NULL); + if (ret) + goto err1; + + mfd_add_devices(max17135->dev, -1, max17135_devs, + ARRAY_SIZE(max17135_devs), + NULL, 0, NULL); + + if (max17135->dev->of_node) { + pdata = max17135_i2c_parse_dt_pdata(max17135->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err2; + } + + } + max17135->pdata = pdata; + + dev_info(&client->dev, "PMIC MAX17135 for eInk display\n"); + + return ret; +err2: + mfd_remove_devices(max17135->dev); +err1: + kfree(max17135); + + return ret; +} + + +static int max17135_remove(struct i2c_client *i2c) +{ + struct max17135 *max17135 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max17135->dev); + return 0; +} + +static int max17135_suspend(struct i2c_client *client, pm_message_t state) +{ + return 0; +} + +static int max17135_resume(struct i2c_client *client) +{ + return 0; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int max17135_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + u8 chip_rev, chip_id; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* detection */ + if (i2c_smbus_read_byte_data(client, + REG_MAX17135_PRODUCT_REV) != 0) { + dev_err(&adapter->dev, + "Max17135 PMIC not found!\n"); + return -ENODEV; + } + + /* identification */ + chip_rev = i2c_smbus_read_byte_data(client, + REG_MAX17135_PRODUCT_REV); + chip_id = i2c_smbus_read_byte_data(client, + REG_MAX17135_PRODUCT_ID); + + if (chip_rev != 0x00 || chip_id != 0x4D) { /* identification failed */ + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, " + "chip_id=0x%02X).\n", chip_rev, chip_id); + return -ENODEV; + } + + if (info) + strlcpy(info->type, "max17135_sensor", I2C_NAME_SIZE); + + return 0; +} + +static const struct i2c_device_id max17135_id[] = { + { "max17135", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max17135_id); + +static const struct of_device_id max17135_dt_ids[] = { + { + .compatible = "maxim,max17135", + .data = (void *) &max17135_id[0], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, max17135_dt_ids); + + +static struct i2c_driver max17135_driver = { + .driver = { + .name = "max17135", + .owner = THIS_MODULE, + .of_match_table = max17135_dt_ids, + }, + .probe = max17135_probe, + .remove = max17135_remove, + .suspend = max17135_suspend, + .resume = max17135_resume, + .id_table = max17135_id, + .detect = max17135_detect, + .address_list = &normal_i2c[0], +}; + +static int __init max17135_init(void) +{ + return i2c_add_driver(&max17135_driver); +} + +static void __exit max17135_exit(void) +{ + i2c_del_driver(&max17135_driver); +} + +/* + * Module entry points + */ +subsys_initcall(max17135_init); +module_exit(max17135_exit); diff -Nur linux-3.10.30/drivers/mfd/mxc-hdmi-core.c linux-3.10.30-cubox-i/drivers/mfd/mxc-hdmi-core.c --- linux-3.10.30/drivers/mfd/mxc-hdmi-core.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-3.10.30-cubox-i/drivers/mfd/mxc-hdmi-core.c 2014-03-08 20:33:52.000000000 +0100 @@ -0,0 +1,775 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, 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 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include