diff options
author | Matthew Weigel <Matthew.Weigel@freescale.com> | 2014-10-28 16:17:08 (GMT) |
---|---|---|
committer | Matthew Weigel <Matthew.Weigel@freescale.com> | 2014-12-11 18:38:59 (GMT) |
commit | a1a034f31f51cdfe41cf7c2e9d3f8bd4963ae516 (patch) | |
tree | c9caa656194ef66fc9f547345f917c7011584bcf | |
parent | a53aca56e29a3c4f2c76ac37740d586504676793 (diff) | |
parent | 5ccc4130fc9501cf73bd65a1386535b1e9f16d7c (diff) | |
download | linux-fsl-qoriq-a1a034f31f51cdfe41cf7c2e9d3f8bd4963ae516.tar.xz |
Merge branch 'ls1-linux/LS1-SDK' to commit 2ec9fb2.
240 files changed, 18031 insertions, 3652 deletions
diff --git a/Documentation/devicetree/bindings/arm/fsl.txt b/Documentation/devicetree/bindings/arm/fsl.txt index e935d7d..2e9b283 100644 --- a/Documentation/devicetree/bindings/arm/fsl.txt +++ b/Documentation/devicetree/bindings/arm/fsl.txt @@ -74,3 +74,31 @@ Required root node properties: i.MX6q generic board Required root node properties: - compatible = "fsl,imx6q"; + + +------------------------------------------------ + +Freescale LS1021A platfform device tree bindings +------------------------------------------------ + +Each device tree must specify the following compatible values: + + "fsl,ls1021a" + +SoC-specific device tree bindings +------------------------------------------- + +Each device tree must specify the following SoC-specific compatible values: + + - compatible = "fsl,ls1021a-scfg": + scfg is the supplemental configuration unit, provides SoC specific + configuration and status registers for the chip.there isn't a dedicate + driver for it, device that has configuration and status register located + in this space can operate on it. Such as getting PEX port status. + + - compatible = "fsl,ls1021a-dcfg": + dcfg is the device configuration unit that provides general purpose + configuration and status for the device, there isn't a dedicate driver + for it, device that has configuration and status register located in + this space can operate on it. Such as setting the secondary core start + address and release the secondary core from holdoff and startup. diff --git a/Documentation/devicetree/bindings/clock/ls1021a-clock.txt b/Documentation/devicetree/bindings/clock/ls1021a-clock.txt new file mode 100644 index 0000000..e53d976 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ls1021a-clock.txt @@ -0,0 +1,27 @@ +Gating clock bindings for Freescale LS1021A SOC + +Required properties: +- compatible: Should be "fsl,ls1021a-gate" +- reg: Address and length of the register set +- #clock-cells: Should be <1> + +The clock consumers should specify the desired clock by having one clock +ID in its "clocks" phandle cell. +Please see include/dt-bindings/clock/ls1021a-clock.h for the full list of +LS1021A clock IDs. + +Example: + +gate: gate@1ee0000 { + compatible = "fsl,ls1021a-gate"; + reg = <0x0 0x1ee0000 0x0 0x10000>; + #clock-cells = <1>; +}; + +wdog0: wdog@2ad0000 { + compatible = "fsl,ls1021a-wdt", "fsl,imx21-wdt"; + reg = <0x0 0x2ad0000 0x0 0x10000>; + interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&gate LS1021A_CLK_WDOG12_EN>; + clock-names = "wdog12_en"; +}; diff --git a/Documentation/devicetree/bindings/dma/fsl-edma.txt b/Documentation/devicetree/bindings/dma/fsl-edma.txt new file mode 100644 index 0000000..191d7bd --- /dev/null +++ b/Documentation/devicetree/bindings/dma/fsl-edma.txt @@ -0,0 +1,76 @@ +* Freescale enhanced Direct Memory Access(eDMA) Controller + + The eDMA channels have multiplex capability by programmble memory-mapped +registers. channels are split into two groups, called DMAMUX0 and DMAMUX1, +specific DMA request source can only be multiplexed by any channel of certain +group, DMAMUX0 or DMAMUX1, but not both. + +* eDMA Controller +Required properties: +- compatible : + - "fsl,vf610-edma" for eDMA used similar to that on Vybrid vf610 SoC +- reg : Specifies base physical address(s) and size of the eDMA registers. + The 1st region is eDMA control register's address and size. + The 2nd and the 3rd regions are programmable channel multiplexing + control register's address and size. +- interrupts : A list of interrupt-specifiers, one for each entry in + interrupt-names. +- interrupt-names : Should contain: + "edma-tx" - the transmission interrupt + "edma-err" - the error interrupt +- #dma-cells : Must be <2>. + The 1st cell specifies the DMAMUX(0 for DMAMUX0 and 1 for DMAMUX1). + Specific request source can only be multiplexed by specific channels + group called DMAMUX. + The 2nd cell specifies the request source(slot) ID. + See the SoC's reference manual for all the supported request sources. +- dma-channels : Number of channels supported by the controller +- clock-names : A list of channel group clock names. Should contain: + "dmamux0" - clock name of mux0 group + "dmamux1" - clock name of mux1 group +- clocks : A list of phandle and clock-specifier pairs, one for each entry in + clock-names. + +Optional properties: +- big-endian: If present registers and hardware scatter/gather descriptors + of the eDMA are implemented in big endian mode, otherwise in little + mode. + + +Examples: + +edma0: dma-controller@40018000 { + #dma-cells = <2>; + compatible = "fsl,vf610-edma"; + reg = <0x40018000 0x2000>, + <0x40024000 0x1000>, + <0x40025000 0x1000>; + interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>, + <0 9 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "edma-tx", "edma-err"; + dma-channels = <32>; + clock-names = "dmamux0", "dmamux1"; + clocks = <&clks VF610_CLK_DMAMUX0>, + <&clks VF610_CLK_DMAMUX1>; +}; + + +* DMA clients +DMA client drivers that uses the DMA function must use the format described +in the dma.txt file, using a two-cell specifier for each channel: the 1st +specifies the channel group(DMAMUX) in which this request can be multiplexed, +and the 2nd specifies the request source. + +Examples: + +sai2: sai@40031000 { + compatible = "fsl,vf610-sai"; + reg = <0x40031000 0x1000>; + interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "sai"; + clocks = <&clks VF610_CLK_SAI2>; + dma-names = "tx", "rx"; + dmas = <&edma0 0 21>, + <&edma0 0 20>; + status = "disabled"; +}; diff --git a/Documentation/devicetree/bindings/powerpc/fsl/ifc.txt b/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt index d5e3704..ee6226b 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/ifc.txt +++ b/Documentation/devicetree/bindings/memory-controllers/fsl/ifc.txt @@ -18,6 +18,8 @@ Properties: interrupt (NAND_EVTER_STAT). If there is only one, that interrupt reports both types of event. +- little-endian : If this property is absent, the big-endian mode will + be in use as default for registers. - ranges : Each range corresponds to a single chipselect, and covers the entire access window as configured. diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt new file mode 100644 index 0000000..7e1dbaf --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt @@ -0,0 +1,51 @@ +* Freescale Quad Serial Peripheral Interface(QuadSPI) + +The QuadSPI controller acts as the SPI master. It is described with a node +for the controller and a set of child nodes for each SPI NOR flash. + +Part I - The DT node for the controller: +------------------------------ + +Required properties: + - compatible : Should be "fsl,vf610-qspi" + - reg : the first contains the register location and length, + the second contains the memory mapping address and length + - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" + - interrupts : Should contain the interrupt for the device + - clocks : The clocks needed by the QuadSPI controller + - clock-names : the name of the clocks + +Optional properties: + - fsl,qspi-has-second-chip: The controller has two buses, bus A and bus B. + Each bus can be connected with two NOR flashes. + Most of the time, each bus only has one NOR flash + connected, this is the default case. + But if there are two NOR flashes connected to the + bus, you should enable this property. + (Please check the board's schematic.) + +Part II - The DT nodes for each SPI NOR flash +------------------------------ +Required properties: +- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at + +Optional properties: + Please refer to the Documentation/devicetree/bindings/mtd/spi-nor-flash.txt + If you set the "spi-nor,ddr-quad-read-dummy", it means you enable the DDR + quad read feature for the driver. + +Example: + +qspi0: quadspi@40044000 { + compatible = "fsl,vf610-qspi"; + reg = <0x40044000 0x1000>, <0x20000000 0x10000000>; + reg-names = "QuadSPI", "QuadSPI-memory"; + interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks VF610_CLK_QSPI0_EN>, + <&clks VF610_CLK_QSPI0>; + clock-names = "qspi_en", "qspi"; + + flash0: s25fl128s@0 { + .... + }; +}; diff --git a/Documentation/devicetree/bindings/mtd/spi-nor-flash.txt b/Documentation/devicetree/bindings/mtd/spi-nor-flash.txt new file mode 100644 index 0000000..aba4d54 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/spi-nor-flash.txt @@ -0,0 +1,7 @@ +This file defines some DT properties for specific SPI NOR flash features. +The SPI NOR controller drivers may refer to this file, such as fsl-quadspi.txt + +Optional properties: + - spi-nor,ddr-quad-read-dummy: The dummy cycles used by the DDR Quad read. + Please refer to the chip's datasheet. This + property can be 4 or 6 which is less then 8. diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt b/Documentation/devicetree/bindings/pci/layerscape-pci.txt new file mode 100644 index 0000000..d7b0026 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt @@ -0,0 +1,46 @@ +Freescale Layerscape PCIe controller + +This PCIe host controller is based on the Synopsis Designware PCIe IP +and thus inherits all the common properties defined in designware-pcie.txt. + +Required properties: +- compatible: should contain the platform identifier such as "fsl,ls1021a-pcie" +- reg: base addresses and lengths of the PCIe controller +- interrupts: A list of interrupt outputs of the controller. Must contain an + entry for each entry in the interrupt-names property. +- interrupt-names: Must include the following entries: + "intr": The interrupt that is asserted for controller interrupts + "msi": The interrupt that is asserted when an MSI is received + "pme": The interrupt that is asserted when PME state changes +- fsl,pcie-scfg: Must include two entries. + The first entry must be a link to the SCFG device node + The second entry must be '0' or '1' based on physical PCIe controller index. + used to get SCFG PEXN registers + +Example: + + pcie@3400000 { + compatible = "fsl,ls1021a-pcie", "snps,dw-pcie"; + reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */ + 0x40 0x00000000 0x0 0x00002000>; /* configuration space */ + reg-names = "regs", "config"; + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */ + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI interrupt */ + <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* PME interrupt */ + interrupt-names = "intr", "msi", "pme"; + fsl,pcie-scfg = <&scfg 0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + num-lanes = <4>; + bus-range = <0x0 0xff>; + ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */ + 0xc2000000 0x0 0x20000000 0x40 0x20000000 0x0 0x20000000 /* prefetchable memory */ + 0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt new file mode 100644 index 0000000..3899d6a --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt @@ -0,0 +1,52 @@ +Freescale FlexTimer Module (FTM) PWM controller + +The same FTM PWM device can have a different endianness on different SoCs. The +device tree provides a property to describing this so that an operating system +device driver can handle all variants of the device. Refer to the table below +for the endianness of the FTM PWM block as integrated into the existing SoCs: + + SoC | FTM-PWM endianness + --------+------------------- + Vybrid | LE + LS1 | BE + LS2 | LE + +Please see ../regmap/regmap.txt for more detail about how to specify endian +modes in device tree. + + +Required properties: +- compatible: Should be "fsl,vf610-ftm-pwm". +- reg: Physical base address and length of the controller's registers +- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of + the cells format. +- clock-names: Should include the following module clock source entries: + "ftm_sys" (module clock, also can be used as counter clock), + "ftm_ext" (external counter clock), + "ftm_fix" (fixed counter clock), + "ftm_cnt_clk_en" (external and fixed counter clock enable/disable). +- clocks: Must contain a phandle and clock specifier for each entry in + clock-names, please see clock/clock-bindings.txt for details of the property + values. +- pinctrl-names: Must contain a "default" entry. +- pinctrl-NNN: One property must exist for each entry in pinctrl-names. + See pinctrl/pinctrl-bindings.txt for details of the property values. +- big-endian: Boolean property, required if the FTM PWM registers use a big- + endian rather than little-endian layout. + +Example: + +pwm0: pwm@40038000 { + compatible = "fsl,vf610-ftm-pwm"; + reg = <0x40038000 0x1000>; + #pwm-cells = <3>; + clock-names = "ftm_sys", "ftm_ext", + "ftm_fix", "ftm_cnt_clk_en"; + clocks = <&clks VF610_CLK_FTM0>, + <&clks VF610_CLK_FTM0_EXT_SEL>, + <&clks VF610_CLK_FTM0_FIX_SEL>, + <&clks VF610_CLK_FTM0_EXT_FIX_EN>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm0_1>; + big-endian; +}; diff --git a/Documentation/devicetree/bindings/regmap/regmap.txt b/Documentation/devicetree/bindings/regmap/regmap.txt new file mode 100644 index 0000000..b494f8b --- /dev/null +++ b/Documentation/devicetree/bindings/regmap/regmap.txt @@ -0,0 +1,47 @@ +Device-Tree binding for regmap + +The endianness mode of CPU & Device scenarios: +Index Device Endianness properties +--------------------------------------------------- +1 BE 'big-endian' +2 LE 'little-endian' + +For one device driver, which will run in different scenarios above +on different SoCs using the devicetree, we need one way to simplify +this. + +Required properties: +- {big,little}-endian: these are boolean properties, if absent + meaning that the CPU and the Device are in the same endianness mode, + these properties are for register values and all the buffers only. + +Examples: +Scenario 1 : CPU in LE mode & device in LE mode. +dev: dev@40031000 { + compatible = "name"; + reg = <0x40031000 0x1000>; + ... +}; + +Scenario 2 : CPU in LE mode & device in BE mode. +dev: dev@40031000 { + compatible = "name"; + reg = <0x40031000 0x1000>; + ... + big-endian; +}; + +Scenario 3 : CPU in BE mode & device in BE mode. +dev: dev@40031000 { + compatible = "name"; + reg = <0x40031000 0x1000>; + ... +}; + +Scenario 4 : CPU in BE mode & device in LE mode. +dev: dev@40031000 { + compatible = "name"; + reg = <0x40031000 0x1000>; + ... + little-endian; +}; diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt index 6fd1dd1..9b8429f 100644 --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt @@ -1,7 +1,11 @@ * Freescale low power universal asynchronous receiver/transmitter (lpuart) Required properties: -- compatible : Should be "fsl,<soc>-lpuart" +- compatible : + - "fsl,vf610-lpuart" for lpuart compatible with the one integrated + on Vybrid vf610 SoC with 8-bit register organization + - "fsl,ls1021a-lpuart" for lpuart compatible with the one integrated + on LS1021A SoC with 32-bit big-endian register organization - reg : Address and length of the register set for the device - interrupts : Should contain uart interrupt diff --git a/Documentation/devicetree/bindings/serial/of-serial.txt b/Documentation/devicetree/bindings/serial/of-serial.txt index 1928a3e..8c4fd03 100644 --- a/Documentation/devicetree/bindings/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/serial/of-serial.txt @@ -14,6 +14,7 @@ Required properties: - "altr,16550-FIFO32" - "altr,16550-FIFO64" - "altr,16550-FIFO128" + - "fsl,16550-FIFO64" - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt index a1fb303..cbbe16e 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt @@ -10,6 +10,12 @@ Required properties: - pinctrl-names: must contain a "default" entry. - spi-num-chipselects : the number of the chipselect signals. - bus-num : the slave chip chipselect signal number. + +Optional property: +- big-endian: If present the dspi device's registers are implemented + in big endian mode, otherwise in native mode(same with CPU), for more + detail please see: Documentation/devicetree/bindings/regmap/regmap.txt. + Example: dspi0@4002c000 { @@ -24,6 +30,7 @@ dspi0@4002c000 { bus-num = <0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_dspi0_1>; + big-endian; status = "okay"; sflash: at26df081a@0 { diff --git a/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt b/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt new file mode 100644 index 0000000..aa8c402 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/fsl,ftm-timer.txt @@ -0,0 +1,31 @@ +Freescale FlexTimer Module (FTM) Timer + +Required properties: + +- compatible : should be "fsl,ftm-timer" +- reg : Specifies base physical address and size of the register sets for the + clock event device and clock source device. +- interrupts : Should be the clock event device interrupt. +- clocks : The clocks provided by the SoC to drive the timer, must contain an + entry for each entry in clock-names. +- clock-names : Must include the following entries: + o "ftm-evt" + o "ftm-src" + o "ftm-evt-counter-en" + o "ftm-src-counter-en" +- big-endian: One boolean property, the big endian mode will be in use if it is + present, or the little endian mode will be in use for all the device registers. + +Example: +ftm: ftm@400b8000 { + compatible = "fsl,ftm-timer"; + reg = <0x400b8000 0x1000 0x400b9000 0x1000>; + interrupts = <0 44 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "ftm-evt", "ftm-src", + "ftm-evt-counter-en", "ftm-src-counter-en"; + clocks = <&clks VF610_CLK_FTM2>, + <&clks VF610_CLK_FTM3>, + <&clks VF610_CLK_FTM2_EXT_FIX_EN>, + <&clks VF610_CLK_FTM3_EXT_FIX_EN>; + big-endian; +}; diff --git a/Documentation/devicetree/bindings/video/fsl-dcu-fb.txt b/Documentation/devicetree/bindings/video/fsl-dcu-fb.txt new file mode 100644 index 0000000..20fc74c --- /dev/null +++ b/Documentation/devicetree/bindings/video/fsl-dcu-fb.txt @@ -0,0 +1,69 @@ +* Freescale Display Control Unit (DCU) + +=== For dcu node === +Required properties: +- compatible: Should be one of "fsl,vf610-dcu" and "fsl,ls1021a-dcu". +- reg: Address and length of the register set for DCU. +- interrupts: Should contain DCU interrupts. +- clocks: From common clock binding: handle to DCU clock. +- clock-names: From common clock binding: Shall be "dcu". +- display: The phandle to display node. + +Optional properties: +- tcon-controller: The phandle of TCON controller. +- scfg-controller: The phandle of scfg node. + +=== For display sub-node === +Required properties: +- bits-per-pixel: <24> for RGB888. + +Required timing node for dispplay sub-node: +- display-timings: Refer to binding doc display-timing.txt for details. + +=== For TCON node === +Required properties: +- compatible: Should be "fsl,tcon". +- reg: Address and length of the register set for TCON. +- clocks: From common clock binding: handle to TCON clock. +- clock-names: From common clock binding: Shall be "tcon". + +Examples: + +dcu0: dcu@40058000 { + compatible = "fsl,vf610-dcu"; + reg = <0x40058000 0x1200>; + interrupts = <0 30 0x04>; + clocks = <&clks VF610_CLK_DCU0>; + clock-names = "dcu"; + tcon-controller = <&tcon0>; + scfg-controller = <&scfg>; + display = <&display>; + + display: display@0 { + bits-per-pixel = <24>; + + display-timings { + native-mode = <&timing0>; + timing0: nl4827hc19 { + clock-frequency = <10870000>; + hactive = <480>; + vactive = <272>; + hback-porch = <2>; + hfront-porch = <2>; + vback-porch = <1>; + vfront-porch = <1>; + hsync-len = <41>; + vsync-len = <2>; + hsync-active = <1>; + vsync-active = <1>; + }; + }; + }; +}; + +tcon0: tcon@4003d000 { + compatible = "fsl,vf610-tcon"; + reg = <0x4003d000 0x1000>; + clocks = <&clks VF610_CLK_TCON0>; + clock-names = "tcon"; +}; diff --git a/Documentation/devicetree/bindings/video/fsl-sii902x.txt b/Documentation/devicetree/bindings/video/fsl-sii902x.txt new file mode 100644 index 0000000..c513a41 --- /dev/null +++ b/Documentation/devicetree/bindings/video/fsl-sii902x.txt @@ -0,0 +1,17 @@ +Device-Tree bindings for framebuffer hdmi driver + +Required properties: +- compatible: Should be "fsl,sii902x". +- reg: The I2C address of the device. +- interrupts: Interrupt number to the cpu. + +Example: + +&i2c1 { + status = "okay"; + hdmi: sii9022a@39 { + compatible = "fsl,sii902x"; + reg = <0x39>; + interrupts = <GIC_SPI 167 IRQ_TYPE_EDGE_RISING>; + }; +}; diff --git a/Documentation/hwmon/ltc2945 b/Documentation/hwmon/ltc2945 new file mode 100644 index 0000000..f8d0f7f --- /dev/null +++ b/Documentation/hwmon/ltc2945 @@ -0,0 +1,84 @@ +Kernel driver ltc2945 +===================== + +Supported chips: + * Linear Technology LTC2945 + Prefix: 'ltc2945' + Addresses scanned: - + Datasheet: + http://cds.linear.com/docs/en/datasheet/2945fa.pdf + +Author: Guenter Roeck <linux@roeck-us.net> + + +Description +----------- + +The LTC2945 is a rail-to-rail system monitor that measures current, voltage, +and power consumption. + + +Usage Notes +----------- + +This driver does not probe for LTC2945 devices, since there is no register +which can be safely used to identify the chip. You will have to instantiate +the devices explicitly. + +Example: the following will load the driver for an LTC2945 at address 0x10 +on I2C bus #1: +$ modprobe ltc2945 +$ echo ltc2945 0x10 > /sys/bus/i2c/devices/i2c-1/new_device + + +Sysfs entries +------------- + +Voltage readings provided by this driver are reported as obtained from the ADC +registers. If a set of voltage divider resistors is installed, calculate the +real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the +value of the divider resistor against the measured voltage and R2 is the value +of the divider resistor against Ground. + +Current reading provided by this driver is reported as obtained from the ADC +Current Sense register. The reported value assumes that a 1 mOhm sense resistor +is installed. If a different sense resistor is installed, calculate the real +current by dividing the reported value by the sense resistor value in mOhm. + +in1_input VIN voltage (mV). Voltage is measured either at + SENSE+ or VDD pin depending on chip configuration. +in1_min Undervoltage threshold +in1_max Overvoltage threshold +in1_lowest Lowest measured voltage +in1_highest Highest measured voltage +in1_reset_history Write 1 to reset in1 history +in1_min_alarm Undervoltage alarm +in1_max_alarm Overvoltage alarm + +in2_input ADIN voltage (mV) +in2_min Undervoltage threshold +in2_max Overvoltage threshold +in2_lowest Lowest measured voltage +in2_highest Highest measured voltage +in2_reset_history Write 1 to reset in2 history +in2_min_alarm Undervoltage alarm +in2_max_alarm Overvoltage alarm + +curr1_input SENSE current (mA) +curr1_min Undercurrent threshold +curr1_max Overcurrent threshold +curr1_lowest Lowest measured current +curr1_highest Highest measured current +curr1_reset_history Write 1 to reset curr1 history +curr1_min_alarm Undercurrent alarm +curr1_max_alarm Overcurrent alarm + +power1_input Power (in uW). Power is calculated based on SENSE+/VDD + voltage or ADIN voltage depending on chip configuration. +power1_min Low lower threshold +power1_max High power threshold +power1_input_lowest Historical minimum power use +power1_input_highest Historical maximum power use +power1_reset_history Write 1 to reset power1 history +power1_min_alarm Low power alarm +power1_max_alarm High power alarm diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 802720e..282026b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -144,7 +144,9 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6q-sbc6x.dtb \ imx6q-wandboard.dtb \ imx6sl-evk.dtb \ - vf610-twr.dtb + vf610-twr.dtb \ + ls1021a-qds.dtb \ + ls1021a-twr.dtb dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \ imx23-olinuxino.dtb \ imx23-stmp378x_devb.dtb \ diff --git a/arch/arm/boot/dts/ls1021a-qds.dts b/arch/arm/boot/dts/ls1021a-qds.dts new file mode 100644 index 0000000..7df7dcf --- /dev/null +++ b/arch/arm/boot/dts/ls1021a-qds.dts @@ -0,0 +1,423 @@ +/* + * Copyright 2013-2014 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 "ls1021a.dtsi" + +/ { + model = "LS1021A QDS Board"; + + aliases { + enet0_rgmii_phy = &rgmii_phy1; + enet1_rgmii_phy = &rgmii_phy2; + enet2_rgmii_phy = &rgmii_phy3; + enet0_sgmii_phy = &sgmii_phy1c; + enet1_sgmii_phy = &sgmii_phy1d; + }; + + clocks { + sys_mclk: clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_3p3v: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + + sound { + compatible = "fsl,vf610-sgtl5000"; + simple-audio-card,name = "FSL-VF610-TWR-BOARD"; + simple-audio-card,routing = + "MIC_IN", "Microphone Jack", + "Microphone Jack", "Mic Bias", + "LINE_IN", "Line In Jack", + "Headphone Jack", "HP_OUT", + "Speaker Ext", "LINE_OUT"; + + simple-audio-card,cpu = <&sai2>; + + simple-audio-card,codec = <&codec>; + }; + + soc { + leds { + compatible = "pwm-leds"; + led0 { + label = "led0"; + pwms = <&pwm3 0 150000 0>; + max-brightness = <100>; + }; + led1 { + label = "led1"; + pwms = <&pwm3 1 150000 0>; + max-brightness = <100>; + }; + led2 { + label = "led2"; + pwms = <&pwm3 2 150000 0>; + max-brightness = <100>; + }; + led3 { + label = "led3"; + pwms = <&pwm3 3 150000 0>; + max-brightness = <100>; + }; + led4 { + label = "led4"; + pwms = <&pwm3 4 150000 0>; + max-brightness = <100>; + }; + led5 { + label = "led5"; + pwms = <&pwm3 5 150000 0>; + max-brightness = <100>; + }; + led6 { + label = "led6"; + pwms = <&pwm3 6 150000 0>; + max-brightness = <100>; + }; + led7 { + label = "led7"; + pwms = <&pwm3 7 150000 0>; + max-brightness = <100>; + }; + }; + }; +}; + +&dspi0 { + bus-num = <0>; + status = "okay"; + + dspiflash: at45db021d@0 { + compatible = "atmel,at45db021d", "atmel,at45", "atmel,dataflash"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <3000000>; + spi-cpol; + spi-cpha; + reg = <0>; + }; +}; + +&duart0 { + status = "okay"; +}; + +&duart1 { + status = "okay"; +}; + +&enet0 { + tbi-handle = <&tbi0>; + phy-handle = <&sgmii_phy1c>; + phy-connection-type = "sgmii"; + status = "okay"; +}; + +&enet1 { + tbi-handle = <&tbi0>; + phy-handle = <&sgmii_phy1d>; + phy-connection-type = "sgmii"; + status = "okay"; +}; + +&enet2 { + phy-handle = <&rgmii_phy3>; + phy-connection-type = "rgmii-id"; + status = "okay"; +}; + +&ftm0 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; + pca9547@77 { + compatible = "philips,pca9547"; + reg = <0x77>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0>; + + rtc@68 { + compatible = "dallas,ds3232"; + reg = <0x68>; + interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x2>; + + ina220@40 { + compatible = "ti,ina220"; + reg = <0x40>; + shunt-resistor = <1000>; + }; + + ina220@41 { + compatible = "ti,ina220"; + reg = <0x41>; + shunt-resistor = <1000>; + }; + }; + + i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x3>; + + eeprom@56 { + compatible = "at24,24c512"; + reg = <0x56>; + }; + + eeprom@57 { + compatible = "at24,24c512"; + reg = <0x57>; + }; + + adt7461a@4c { + compatible = "adt7461a"; + reg = <0x4c>; + }; + }; + + i2c@4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x4>; + + codec: sgtl5000@2a { + compatible = "fsl,sgtl5000"; + reg = <0x2a>; + VDDA-supply = <®_3p3v>; + VDDIO-supply = <®_3p3v>; + clocks = <&sys_mclk 1>; + }; + }; + }; +}; + +&ifc { + status = "okay"; + #address-cells = <2>; + #size-cells = <1>; + /* NOR, NAND Flashes and FPGA on board */ + ranges = <0x0 0x0 0x0 0x60000000 0x08000000 + 0x2 0x0 0x0 0x7e800000 0x00010000 + 0x3 0x0 0x0 0x7fb00000 0x00000100>; + + nor@0,0 { + compatible = "cfi-flash"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + }; + + nand@2,0 { + compatible = "fsl,ifc-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2 0x0 0x10000>; + + partition@0 { + /* This location must not be altered */ + /* 1MB for u-boot Bootloader Image */ + reg = <0x0 0x00100000>; + label = "NAND U-Boot Image"; + read-only; + }; + + partition@100000 { + /* 1MB for DTB Image */ + reg = <0x00100000 0x00100000>; + label = "NAND DTB Image"; + }; + + partition@200000 { + /* 10MB for Linux Kernel Image */ + reg = <0x00200000 0x00a00000>; + label = "NAND Linux Kernel Image"; + }; + + partition@c00000 { + /* 500MB for Root file System Image */ + reg = <0x00c00000 0x1f400000>; + label = "NAND Compressed RFS Image"; + }; + }; + + fpga: board-control@3,0 { + compatible = "fsl,ls1021aqds-fpga", "fsl,fpga-qixis"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x3 0x0 0x0000100>; + bank-width = <1>; + device-width = <1>; + ranges = <0 3 0 0x100>; + + mdio-mux-emi1 { + compatible = "mdio-mux-mmioreg"; + mdio-parent-bus = <&mdio0>; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x54 1>; /* BRDCFG4 */ + mux-mask = <0xe0>; /* EMI1[2:0] */ + + /* Onboard PHYs */ + ls1021amdio0: mdio@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + rgmii_phy1: ethernet-phy@1 { + reg = <0x1>; + }; + }; + ls1021amdio1: mdio@20 { + reg = <0x20>; + #address-cells = <1>; + #size-cells = <0>; + rgmii_phy2: ethernet-phy@2 { + reg = <0x2>; + }; + }; + ls1021amdio2: mdio@40 { + reg = <0x40>; + #address-cells = <1>; + #size-cells = <0>; + rgmii_phy3: ethernet-phy@3 { + reg = <0x3>; + }; + }; + ls1021amdio3: mdio@60 { + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + sgmii_phy1c: ethernet-phy@1c { + reg = <0x1c>; + }; + }; + ls1021amdio4: mdio@80 { + reg = <0x80>; + #address-cells = <1>; + #size-cells = <0>; + sgmii_phy1d: ethernet-phy@1d { + reg = <0x1d>; + }; + }; + + }; + + }; +}; + +&lpuart0 { + status = "okay"; +}; + +&mdio0 { + tbi0: tbi-phy@8 { + reg = <0x8>; + device_type = "tbi-phy"; + }; +}; + +&uqe { + tdma: ucc@2000 { + compatible = "fsl,ucc-tdm"; + rx-clock-name = "brg1"; + tx-clock-name = "brg1"; + fsl,rx-sync-clock = "none"; + fsl,tx-sync-clock = "none"; + fsl,tx-timeslot = <0xfffffffe>; + fsl,rx-timeslot = <0xfffffffe>; + fsl,tdm-framer-type = "e1"; + fsl,tdm-mode = "internal-loopback"; + fsl,tdm-id = <0>; + fsl,siram-entry-id = <0>; + }; + + serial: ucc@2200 { + device_type = "serial"; + compatible = "ucc_uart"; + port-number = <1>; + rx-clock-name = "brg2"; + tx-clock-name = "brg2"; + }; +}; + +&pwm3 { + status = "okay"; +}; + +&pwm7 { + status = "okay"; +}; + +&qspi { + num-cs = <2>; + bus-num = <0>; + fsl,spi-num-chipselects = <2>; + fsl,spi-flash-chipselects = <0>; + status = "okay"; + + qflash0: s25fl128s@0 { + compatible = "spansion,s25fl128s"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + reg = <0>; + }; + + qflash1: s25fl128s@1 { + compatible = "spansion,s25fl128s"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + reg = <1>; + }; +}; + +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&sai2 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts new file mode 100755 index 0000000..7f6ed3d --- /dev/null +++ b/arch/arm/boot/dts/ls1021a-twr.dts @@ -0,0 +1,299 @@ +/* + * Copyright 2013-2014 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 "ls1021a.dtsi" + +/ { + model = "LS1021A TWR Board"; + + aliases { + enet2_rgmii_phy = &rgmii_phy1; + enet0_sgmii_phy = &sgmii_phy2; + enet1_sgmii_phy = &sgmii_phy0; + }; + + clocks { + sys_mclk: clock { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24576000>; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_3p3v: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + + sound { + compatible = "fsl,vf610-sgtl5000"; + simple-audio-card,name = "FSL-VF610-TWR-BOARD"; + simple-audio-card,routing = + "MIC_IN", "Microphone Jack", + "Microphone Jack", "Mic Bias", + "LINE_IN", "Line In Jack", + "Headphone Jack", "HP_OUT", + "Speaker Ext", "LINE_OUT"; + + simple-audio-card,cpu = <&sai1>; + + simple-audio-card,codec = <&codec>; + }; +}; + +&dcu0 { + display = <&display>; + status = "okay"; + + display: display@0 { + bits-per-pixel = <24>; + + display-timings { + native-mode = <&timing0>; + timing0: nl4827hc19 { + clock-frequency = <10870000>; + hactive = <480>; + vactive = <272>; + hback-porch = <2>; + hfront-porch = <2>; + vback-porch = <2>; + vfront-porch = <2>; + hsync-len = <41>; + vsync-len = <4>; + hsync-active = <1>; + vsync-active = <1>; + }; + }; + }; +}; + +&dspi1 { + bus-num = <0>; + status = "okay"; + + dspiflash: s25fl064k@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25fl064k"; + spi-max-frequency = <16000000>; + spi-cpol; + spi-cpha; + reg = <0>; + }; +}; + +&duart0 { + status = "okay"; +}; + +&duart1 { + status = "okay"; +}; + +&enet0 { + tbi-handle = <&tbi1>; + phy-handle = <&sgmii_phy2>; + phy-connection-type = "sgmii"; + status = "okay"; +}; + +&enet1 { + tbi-handle = <&tbi1>; + phy-handle = <&sgmii_phy0>; + phy-connection-type = "sgmii"; + status = "okay"; +}; + +&enet2 { + phy-handle = <&rgmii_phy1>; + phy-connection-type = "rgmii-id"; + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + codec: sgtl5000@a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + VDDA-supply = <®_3p3v>; + VDDIO-supply = <®_3p3v>; + clocks = <&sys_mclk 1>; + }; + + hdmi: sii9022a@39 { + compatible = "fsl,sii902x"; + reg = <0x39>; + interrupts = <GIC_SPI 167 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&i2c2 { + status = "okay"; + monitor: ltc2945@67 { + reg = <0x67>; + }; +}; + +&ifc { + status = "okay"; + #address-cells = <2>; + #size-cells = <1>; + /* NOR, and CPLD on board */ + ranges = <0x0 0x0 0x0 0x60000000 0x08000000 + 0x2 0x0 0x0 0x7fb00000 0x00000100>; + + nor@0,0 { + compatible = "cfi-flash"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + /* 128KB for rcw */ + reg = <0x00000000 0x0020000>; + label = "NOR bank0 RCW Image"; + }; + + partition@20000 { + /* 1MB for DTB */ + reg = <0x00020000 0x00100000>; + label = "NOR DTB Image"; + }; + + partition@120000 { + /* 8 MB for Linux Kernel Image */ + reg = <0x00120000 0x00800000>; + label = "NOR Linux Kernel Image"; + }; + + partition@920000 { + /* 56MB for Ramdisk Root File System */ + reg = <0x00920000 0x03600000>; + label = "NOR Ramdisk Root File System Image"; + }; + + partition@3f80000 { + /* 512KB for bank4 u-boot Image */ + reg = <0x03f80000 0x80000>; + label = "NOR bank4 u-boot Image"; + }; + + partition@4000000 { + /* 128KB for bank4 RCW Image */ + reg = <0x04000000 0x20000>; + label = "NOR bank4 RCW Image"; + }; + + partition@4020000 { + /* 63MB JFFS2 ROOT File System Image */ + reg = <0x04020000 0x3f00000>; + label = "NOR JFFS2 ROOT File System Image"; + }; + + partition@7f80000 { + /* 512KB for bank0 u-boot Image */ + reg = <0x07f80000 0x80000>; + label = "NOR bank0 u-boot Image"; + }; + }; +}; + +&lpuart0 { + status = "okay"; +}; + +&mdio0 { + sgmii_phy0: ethernet-phy@0 { + reg = <0x0>; + }; + rgmii_phy1: ethernet-phy@1 { + reg = <0x1>; + }; + sgmii_phy2: ethernet-phy@2 { + reg = <0x2>; + }; + tbi1: tbi-phy@1f { + reg = <0x1f>; + device_type = "tbi-phy"; + }; +}; + +&uqe { + tdma: ucc@2000 { + compatible = "fsl,ucc-tdm"; + rx-clock-name = "clk8"; + tx-clock-name = "clk9"; + fsl,rx-sync-clock = "rsync_pin"; + fsl,tx-sync-clock = "tsync_pin"; + fsl,tx-timeslot = <0xfffffffe>; + fsl,rx-timeslot = <0xfffffffe>; + fsl,tdm-framer-type = "e1"; + fsl,tdm-mode = "normal"; + fsl,tdm-id = <0>; + fsl,siram-entry-id = <0>; + }; + + serial: ucc@2200 { + device_type = "serial"; + compatible = "ucc_uart"; + port-number = <1>; + rx-clock-name = "brg2"; + tx-clock-name = "brg2"; + }; +}; + +&pwm6 { + status = "okay"; +}; + +&pwm7 { + status = "okay"; +}; + +&qspi { + num-cs = <2>; + status = "okay"; + + qflash0: n25q128a13@0 { + compatible = "micron,n25q128a13"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + reg = <0>; + }; + + qflash1: n25q128a13@1 { + compatible = "micron,n25q128a13"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + reg = <1>; + }; +}; + +&sai1 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi new file mode 100644 index 0000000..7092336 --- /dev/null +++ b/arch/arm/boot/dts/ls1021a.dtsi @@ -0,0 +1,897 @@ +/* + * Copyright 2013-2014 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 "skeleton64.dtsi" +#include <dt-bindings/interrupt-controller/arm-gic.h> + +/ { + compatible = "fsl,ls1021a"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &lpuart0; + serial1 = &lpuart1; + serial2 = &lpuart2; + serial3 = &lpuart3; + serial4 = &lpuart4; + serial5 = &lpuart5; + ethernet0 = &enet0; + ethernet1 = &enet1; + ethernet2 = &enet2; + sysclk = &sysclk; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + gpio3 = &gpio4; + + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x20000000>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@f00 { + compatible = "arm,cortex-a7"; + device_type = "cpu"; + reg = <0xf00>; + clocks = <&cluster1_clk>; + }; + + cpu@f01 { + compatible = "arm,cortex-a7"; + device_type = "cpu"; + reg = <0xf01>; + clocks = <&cluster1_clk>; + }; + }; + + timer { + compatible = "arm,armv7-timer"; + interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, + <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>; + }; + + pmu { + compatible = "arm,cortex-a7-pmu"; + interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + device_type = "soc"; + interrupt-parent = <&gic>; + ranges; + + gic: interrupt-controller@1400000 { + compatible = "arm,cortex-a15-gic"; + #interrupt-cells = <3>; + interrupt-controller; + reg = <0x0 0x1401000 0x0 0x1000>, + <0x0 0x1402000 0x0 0x1000>, + <0x0 0x1404000 0x0 0x2000>, + <0x0 0x1406000 0x0 0x2000>; + interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>; + + }; + + ifc: ifc@1530000 { + compatible = "fsl,ifc", "simple-bus"; + reg = <0x0 0x1530000 0x0 0x10000>; + interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>; + }; + + dcfg: dcfg@1ee0000 { + compatible = "fsl,ls1021a-dcfg"; + reg = <0x0 0x1ee0000 0x0 0x10000>; + }; + + qspi: quadspi@1550000 { + compatible = "fsl,ls1-qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x1550000 0x0 0x10000>, + <0x0 0x40000000 0x0 0x10000000>; + reg-names = "QuadSPI", "QuadSPI-memory"; + interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "qspi_en", "qspi"; + clocks = <&platform_clk 1>, <&platform_clk 1>; + big-endian; + amba-base = <0x40000000>; + status = "disabled"; + }; + + esdhc: esdhc@1560000 { + compatible = "fsl,ls1021a-esdhc", "fsl,esdhc"; + reg = <0x0 0x1560000 0x0 0x10000>; + interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <0>; + voltage-ranges = <1800 1800 3300 3300>; + sdhci,auto-cmd12; + big-endian; + bus-width = <4>; + status = "disabled"; + }; + + scfg: scfg@1570000 { + compatible = "fsl,ls1021a-scfg", "syscon"; + reg = <0x0 0x1570000 0x0 0x10000>; + big-endian; + }; + + crypto: crypto@1700000 { + compatible = "fsl,sec-v5.3", "fsl,sec-v5.0", "fsl,sec-v4.0"; + fsl,sec-era = <4>; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x1700000 0x0 0x100000>; + ranges = <0x0 0x0 0x1700000 0x100000>; + interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>; + + sec_jr0: jr@10000 { + compatible = "fsl,sec-v5.3-job-ring", + "fsl,sec-v5.0-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x10000 0x10000>; + interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>; + }; + + sec_jr1: jr@20000 { + compatible = "fsl,sec-v5.3-job-ring", + "fsl,sec-v5.0-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x20000 0x10000>; + interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>; + }; + + sec_jr2: jr@30000 { + compatible = "fsl,sec-v5.3-job-ring", + "fsl,sec-v5.0-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x30000 0x10000>; + interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>; + }; + + sec_jr3: jr@40000 { + compatible = "fsl,sec-v5.3-job-ring", + "fsl,sec-v5.0-job-ring", + "fsl,sec-v4.0-job-ring"; + reg = <0x40000 0x10000>; + interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>; + }; + + }; + + clockgen: clocking@1ee1000 { + compatible = "fsl,ls1021a-clockgen"; + reg = <0x0 0x1ee1000 0x0 0x10000>; + #address-cells = <1>; + #size-cells = <0>; + sysclk: sysclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <100000000>; + clock-output-names = "sysclk"; + }; + + cga_pll1: pll1@800 { + compatible = "fsl,core-pll-clock"; + #clock-cells = <1>; + reg = <0x800>; + clocks = <&sysclk>; + clock-output-names = "cga-pll1", "cga-pll1-div2", + "cga-pll1-div4"; + }; + + platform_clk: pll@c00 { + compatible = "fsl,core-pll-clock"; + #clock-cells = <1>; + reg = <0xc00>; + clocks = <&sysclk>; + clock-output-names = "platform-clk", "platform-clk-div2"; + }; + + cluster1_clk: clk0c0@0 { + compatible = "fsl,core-mux-clock"; + #clock-cells = <0>; + reg = <0x0>; + clock-names = "pll1cga", "pll1cga-div2", "pll1cga-div4"; + clocks = <&cga_pll1 0>, <&cga_pll1 1>, <&cga_pll1 2>; + clock-output-names = "cluster1-clk"; + }; + + }; + + rcpm: rcpm@1ee2000 { + compatible = "fsl,ls1021a-rcpm", "fsl,qoriq-rcpm-2.1"; + reg = <0x0 0x1ee2000 0x0 0x10000>; + }; + + dspi0: dspi@2100000 { + compatible = "fsl,vf610-dspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2100000 0x0 0x10000>; + interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "dspi"; + clocks = <&platform_clk 1>; + spi-num-chipselects = <5>; + big-endian; + tcfq-mode; + status = "disabled"; + }; + + dspi1: dspi@2110000 { + compatible = "fsl,vf610-dspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2110000 0x0 0x10000>; + interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "dspi"; + clocks = <&platform_clk 1>; + spi-num-chipselects = <5>; + big-endian; + status = "disabled"; + }; + + i2c0: i2c@2180000 { + compatible = "fsl,vf610-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2180000 0x0 0x10000>; + interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "i2c"; + clocks = <&platform_clk 1>; + status = "disabled"; + }; + + i2c1: i2c@2190000 { + compatible = "fsl,vf610-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2190000 0x0 0x10000>; + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "i2c"; + clocks = <&platform_clk 1>; + status = "disabled"; + }; + + i2c2: i2c@21a0000 { + compatible = "fsl,vf610-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x21a0000 0x0 0x10000>; + interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "i2c"; + clocks = <&platform_clk 1>; + status = "disabled"; + }; + + duart0: serial@21c0500 { + compatible = "fsl,16550-FIFO64"; + reg = <0x0 0x21c0500 0x0 0x100>; + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <0>; + fifo-size = <63>; + status = "disabled"; + }; + + duart1: serial@21c0600 { + compatible = "fsl,16550-FIFO64"; + reg = <0x0 0x21c0600 0x0 0x100>; + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <0>; + fifo-size = <63>; + status = "disabled"; + }; + + duart2: serial@21d0500 { + compatible = "fsl,16550-FIFO64"; + reg = <0x0 0x21d0500 0x0 0x100>; + interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <0>; + fifo-size = <63>; + status = "disabled"; + }; + + duart3: serial@21d0600 { + compatible = "fsl,16550-FIFO64"; + reg = <0x0 0x21d0600 0x0 0x100>; + interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <0>; + fifo-size = <63>; + status = "disabled"; + }; + + uqe: uqe@2400000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "qe"; + compatible = "fsl,qe", "simple-bus"; + ranges = <0x0 0x0 0x2400000 0x40000>; + reg = <0x0 0x2400000 0x0 0x480>; + brg-frequency = <100000000>; + bus-frequency = <200000000>; + + fsl,qe-num-riscs = <1>; + fsl,qe-num-snums = <28>; + + qeic: qeic@80 { + compatible = "fsl,qe-ic"; + reg = <0x80 0x80>; + #address-cells = <0>; + interrupt-controller; + #interrupt-cells = <1>; + interrupts = <0 109 0x04 0 109 0x04>; + }; + + si1: si@700 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,qe-si"; + reg = <0x700 0x80>; + }; + + siram1: siram@1000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,qe-siram"; + reg = <0x1000 0x800>; + }; + + ucc@2000 { + cell-index = <1>; + reg = <0x2000 0x200>; + interrupts = <32>; + interrupt-parent = <&qeic>; + }; + + ucc@2200 { + cell-index = <3>; + reg = <0x2200 0x200>; + interrupts = <34>; + interrupt-parent = <&qeic>; + }; + + muram@10000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,qe-muram", "fsl,cpm-muram"; + ranges = <0x0 0x10000 0x6000>; + + data-only@0 { + compatible = "fsl,qe-muram-data", + "fsl,cpm-muram-data"; + reg = <0x0 0x6000>; + }; + }; + }; + + lpuart0: serial@2950000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2950000 0x0 0x1000>; + interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&sysclk>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart1: serial@2960000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2960000 0x0 0x1000>; + interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart2: serial@2970000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2970000 0x0 0x1000>; + interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart3: serial@2980000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2980000 0x0 0x1000>; + interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart4: serial@2990000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x2990000 0x0 0x1000>; + interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + lpuart5: serial@29a0000 { + compatible = "fsl,ls1021a-lpuart"; + reg = <0x0 0x29a0000 0x0 0x1000>; + interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-names = "ipg"; + status = "disabled"; + }; + + gpio1: gpio@2300000 { + compatible = "fsl,ls1021a-gpio"; + reg = <0x0 0x2300000 0x0 0x10000>; + interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio2: gpio@2310000 { + compatible = "fsl,ls1021a-gpio"; + reg = <0x0 0x2310000 0x0 0x10000>; + interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio3: gpio@2320000 { + compatible = "fsl,ls1021a-gpio"; + reg = <0x0 0x2320000 0x0 0x10000>; + interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio4: gpio@2330000 { + compatible = "fsl,ls1021a-gpio"; + reg = <0x0 0x2330000 0x0 0x10000>; + interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + ftm0: ftm0@29d0000 { + compatible = "fsl,ftm-alarm"; + reg = <0x0 0x29d0000 0x0 0x10000>; + interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>; + big-endian; + status = "disabled"; + }; + + pwm3: ftm@2a00000 { + compatible = "fsl,vf610-ftm-pwm"; + #pwm-cells = <3>; + reg = <0x0 0x2a00000 0x0 0x10000>; + interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "ftm_sys", "ftm_ext", + "ftm_fix", "ftm_cnt_clk_en"; + clocks = <&platform_clk 1>, <&platform_clk 1>, + <&platform_clk 1>, <&platform_clk 1>; + big-endian; + status = "disabled"; + }; + + pwm6: ftm@2a30000 { + compatible = "fsl,vf610-ftm-pwm"; + #pwm-cells = <3>; + reg = <0x0 0x2a30000 0x0 0x10000>; + interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "ftm_sys", "ftm_ext", + "ftm_fix", "ftm_cnt_clk_en"; + clocks = <&platform_clk 1>, <&platform_clk 1>, + <&platform_clk 1>, <&platform_clk 1>; + big-endian; + status = "disabled"; + }; + + pwm7: ftm@2a40000 { + compatible = "fsl,vf610-ftm-pwm"; + #pwm-cells = <3>; + reg = <0x0 0x2a40000 0x0 0x10000>; + interrupts = <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "ftm_sys", "ftm_ext", + "ftm_fix", "ftm_cnt_clk_en"; + clocks = <&platform_clk 1>, <&platform_clk 1>, + <&platform_clk 1>, <&platform_clk 1>; + big-endian; + status = "disabled"; + }; + + wdog0: wdog@2ad0000 { + compatible = "fsl,imx21-wdt"; + reg = <0x0 0x2ad0000 0x0 0x10000>; + interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-names = "wdog"; + big-endian; + }; + + sai1: sai@2b50000 { + compatible = "fsl,vf610-sai"; + reg = <0x0 0x2b50000 0x0 0x10000>; + interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-names = "sai"; + dma-names = "tx", "rx"; + dmas = <&edma0 1 47>, + <&edma0 1 46>; + big-endian-regs; + status = "disabled"; + }; + + sai2: sai@2b60000 { + compatible = "fsl,vf610-sai"; + reg = <0x0 0x2b60000 0x0 0x10000>; + interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-names = "sai"; + dma-names = "tx", "rx"; + dmas = <&edma0 1 45>, + <&edma0 1 44>; + big-endian-regs; + status = "disabled"; + }; + + edma0: edma@2c00000 { + #dma-cells = <2>; + compatible = "fsl,vf610-edma"; + reg = <0x0 0x2c00000 0x0 0x10000>, + <0x0 0x2c10000 0x0 0x10000>, + <0x0 0x2c20000 0x0 0x10000>; + interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "edma-tx", "edma-err"; + dma-channels = <32>; + big-endian; + clock-names = "dmamux0", "dmamux1"; + clocks = <&platform_clk 1>, + <&platform_clk 1>; + }; + + dcu0: dcu@2ce0000 { + compatible = "fsl,ls1021a-dcu"; + reg = <0x0 0x2ce0000 0x0 0x10000>; + interrupts = <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 0>; + clock-names = "dcu"; + scfg-controller = <&scfg>; + big-endian; + status = "disabled"; + }; + + mdio0: mdio@2d24000 { + compatible = "gianfar"; + device_type = "mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x2d24000 0x0 0x4000>; + }; + + enet0: ethernet@2d10000 { + compatible = "fsl,etsec2"; + device_type = "network"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + model = "eTSEC"; + fsl,dma-endian-le; + fsl,num_rx_queues = <0x1>; + fsl,num_tx_queues = <0x1>; + local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; + + queue-group@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x2d10000 0x0 0x8000>; + fsl,rx-bit-map = <0xff>; + fsl,tx-bit-map = <0xff>; + interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>; + }; + + }; + + enet1: ethernet@2d50000 { + compatible = "fsl,etsec2"; + device_type = "network"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + model = "eTSEC"; + fsl,dma-endian-le; + fsl,num_rx_queues = <0x1>; + fsl,num_tx_queues = <0x1>; + local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; + + queue-group@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x2d50000 0x0 0x8000>; + fsl,rx-bit-map = <0xff>; + fsl,tx-bit-map = <0xff>; + interrupts = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>; + }; + + }; + + enet2: ethernet@2d90000 { + compatible = "fsl,etsec2"; + device_type = "network"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&gic>; + model = "eTSEC"; + fsl,dma-endian-le; + fsl,num_rx_queues = <0x1>; + fsl,num_tx_queues = <0x1>; + local-mac-address = [ 00 00 00 00 00 00 ]; + ranges; + + queue-group@0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x2d90000 0x0 0x8000>; + fsl,rx-bit-map = <0xff>; + fsl,tx-bit-map = <0xff>; + interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + can0: can@2a70000 { + compatible = "fsl,ls1021a-flexcan"; + reg = <0x0 0x2a70000 0x0 0x1000>; + interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-frequency = <150000000>; + clock-names = "per"; + little-endian; + status = "disabled"; + }; + + can1: can@2a80000 { + compatible = "fsl,ls1021a-flexcan"; + reg = <0x0 0x2a80000 0x0 0x1000>; + interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-frequency = <150000000>; + clock-names = "per"; + little-endian; + status = "disabled"; + }; + + can2: can@2a90000 { + compatible = "fsl,ls1021a-flexcan"; + reg = <0x0 0x2a90000 0x0 0x1000>; + interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-frequency = <150000000>; + clock-names = "per"; + little-endian; + status = "disabled"; + }; + + can3: can@2aa0000 { + compatible = "fsl,ls1021a-flexcan"; + reg = <0x0 0x2aa0000 0x0 0x1000>; + interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&platform_clk 1>; + clock-frequency = <150000000>; + clock-names = "per"; + little-endian; + status = "disabled"; + }; + + usb@8600000 { + compatible = "fsl-usb2-dr-v2.5", "fsl-usb2-dr"; + reg = <0x0 0x8600000 0x0 0x1000>; + interrupts = <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH>; + dr_mode = "host"; + phy_type = "ulpi"; + }; + + usb3@3100000 { + compatible = "snps,dwc3"; + reg = <0x0 0x3100000 0x0 0x10000>; + interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>; + dr_mode = "host"; + }; + + pcie@3400000 { + compatible = "fsl,ls1021a-pcie", "snps,dw-pcie"; + reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */ + 0x40 0x00000000 0x0 0x00002000>; /* configuration space */ + reg-names = "regs", "config"; + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */ + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI interrupt */ + <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* PME interrupt */ + interrupt-names = "intr", "msi", "pme"; + fsl,pcie-scfg = <&scfg 0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + num-lanes = <4>; + bus-range = <0x0 0xff>; + ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */ + 0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>; + }; + + pcie@3500000 { + compatible = "fsl,ls1021a-pcie", "snps,dw-pcie"; + reg = <0x00 0x03500000 0x0 0x00010000 /* controller registers */ + 0x48 0x00000000 0x0 0x00002000>; /* configuration space */ + reg-names = "regs", "config"; + interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 182 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "intr", "msi", "pme"; + fsl,pcie-scfg = <&scfg 1>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + num-lanes = <2>; + bus-range = <0x0 0xff>; + ranges = <0x81000000 0x0 0x00000000 0x48 0x00010000 0x0 0x00010000 /* downstream I/O */ + 0x82000000 0x0 0x40000000 0x48 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */ + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0000 0 0 1 &gic GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 2 &gic GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 3 &gic GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>, + <0000 0 0 4 &gic GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>; + }; + }; + + dcsr { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,dcsr", "simple-bus"; + + ranges = <0x0 0x0 0x20000000 0x1000000>; + + dcsr-epu@0 { + compatible = "fsl,ls1021a-dcsr-epu"; + reg = <0x0 0x10000>; + }; + + dcsr-gdi@100000 { + compatible = "fsl,ls1021a-dcsr-gdi"; + reg = <0x100000 0x10000>; + }; + + dcsr-dddi@120000 { + compatible = "fsl,ls1021a-dcsr-dddi"; + reg = <0x120000 0x10000>; + }; + + dcsr-dcfg@220000 { + compatible = "fsl,ls1021a-dcsr-dcfg"; + reg = <0x220000 0x1000>; + }; + + dcsr-clock@221000 { + compatible = "fsl,ls1021a-dcsr-clock"; + reg = <0x221000 0x1000>; + }; + + dcsr-rcpm@222000 { + compatible = "fsl,ls1021a-dcsr-rcpm"; + reg = <0x222000 0x1000 0x223000 0x1000>; + }; + + dcsr-ccp@225000 { + compatible = "fsl,ls1021a-dcsr-ccp"; + reg = <0x225000 0x1000>; + }; + + dcsr-fusectrl@226000 { + compatible = "fsl,ls1021a-dcsr-fusectrl"; + reg = <0x226000 0x1000>; + }; + + dcsr-dap@300000 { + compatible = "fsl,ls1021a-dcsr-dap"; + reg = <0x300000 0x10000>; + }; + + dcsr-cstf@350000 { + compatible = "fsl,ls1021a-dcsr-cstf"; + reg = <0x350000 0x1000 0x3a7000 0x1000>; + }; + + dcsr-a7rom@360000 { + compatible = "fsl,ls1021a-dcsr-a7rom"; + reg = <0x360000 0x10000>; + }; + + dcsr-a7cpu@370000 { + compatible = "fsl,ls1021a-dcsr-a7cpu"; + reg = <0x370000 0x8000>; + }; + + dcsr-a7cti@378000 { + compatible = "fsl,ls1021a-dcsr-a7cti"; + reg = <0x378000 0x4000>; + }; + + dcsr-etm@37c000 { + compatible = "fsl,ls1021a-dcsr-etm"; + reg = <0x37c000 0x1000 0x37d000 0x3000>; + }; + + dcsr-hugorom@3a0000 { + compatible = "fsl,ls1021a-dcsr-hugorom"; + reg = <0x3a0000 0x1000>; + }; + + dcsr-etf@3a1000 { + compatible = "fsl,ls1021a-dcsr-etf"; + reg = <0x3a1000 0x1000 0x3a2000 0x1000>; + }; + + dcsr-etr@3a3000 { + compatible = "fsl,ls1021a-dcsr-etr"; + reg = <0x3a3000 0x1000>; + }; + + dcsr-cti@3a4000 { + compatible = "fsl,ls1021a-dcsr-cti"; + reg = <0x3a4000 0x1000 0x3a5000 0x1000 0x3a6000 0x1000>; + }; + + dcsr-atbrepl@3a8000 { + compatible = "fsl,ls1021a-dcsr-atbrepl"; + reg = <0x3a8000 0x1000>; + }; + + dcsr-tsgen-ctrl@3a9000 { + compatible = "fsl,ls1021a-dcsr-tsgen-ctrl"; + reg = <0x3a9000 0x1000>; + }; + + dcsr-tsgen-read@3aa000 { + compatible = "fsl,ls1021a-dcsr-tsgen-read"; + reg = <0x3aa000 0x1000>; + }; + }; +}; diff --git a/arch/arm/configs/ls1021a_defconfig b/arch/arm/configs/ls1021a_defconfig new file mode 100644 index 0000000..58a5241 --- /dev/null +++ b/arch/arm/configs/ls1021a_defconfig @@ -0,0 +1,180 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_BLK_CMDLINE_PARSER=y +CONFIG_ARCH_MXC=y +CONFIG_SOC_LS1021A=y +CONFIG_ARM_LPAE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_THUMB2_KERNEL=y +CONFIG_HIGHMEM=y +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +CONFIG_CMDLINE="console=ttyS0,115200" +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y +CONFIG_BINFMT_MISC=y +# CONFIG_SUSPEND is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_MROUTE=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_UDP_DIAG=y +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +CONFIG_CAN=y +# CONFIG_CAN_BCM is not set +# CONFIG_CAN_GW is not set +CONFIG_CAN_FLEXCAN=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_FW_LOADER is not set +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_BE_BYTE_SWAP=y +CONFIG_MTD_CFI_GEOMETRY=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_FSL_IFC=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=256000 +CONFIG_SRAM=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_ATA=y +CONFIG_NETDEVICES=y +CONFIG_GIANFAR=y +CONFIG_VITESSE_PHY=y +CONFIG_BROADCOM_PHY=y +CONFIG_REALTEK_PHY=y +CONFIG_NATIONAL_PHY=y +CONFIG_MICREL_PHY=y +CONFIG_MDIO_BUS_MUX_MMIOREG=y +CONFIG_INPUT_EVDEV=y +# CONFIG_MOUSE_PS2_TRACKPOINT is not set +CONFIG_SERIO_SERPORT=m +# CONFIG_CONSOLE_TRANSLATIONS is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_DMA is not set +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_IRQ_DOMAIN_DEBUG=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_GPIOLIB=y +CONFIG_OF_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_MPC8XXX=y +CONFIG_SENSORS_LM90=y +CONFIG_SENSORS_LTC2945=y +CONFIG_SENSORS_INA2XX=y +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +# CONFIG_USB_SUPPORT is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS3232=y +CONFIG_DMADEVICES=y +CONFIG_FSL_EDMA=y +CONFIG_CLK_QORIQ=y +CONFIG_MEMORY=y +# CONFIG_RESET_CONTROLLER is not set +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y +CONFIG_FANOTIFY=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=m +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=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_2=y +CONFIG_NLS_ISO8859_15=y +CONFIG_NLS_UTF8=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +CONFIG_MMC_BLOCK_BOUNCE=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ESDHC=y diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index fcc1b5b..b6c3b52 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -263,7 +263,7 @@ * you cannot return to the original mode. */ .macro safe_svcmode_maskall reg:req -#if __LINUX_ARM_ARCH__ >= 6 +#if __LINUX_ARM_ARCH__ >= 6 && !defined(CONFIG_CPU_V7M) mrs \reg , cpsr eor \reg, \reg, #HYP_MODE tst \reg, #MODE_MASK diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index cc8a2ca..3dac4cc 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -436,4 +436,50 @@ static inline void __sync_cache_range_r(volatile void *p, size_t size) #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 --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h index dff714d..a932f99 100644 --- a/arch/arm/include/asm/delay.h +++ b/arch/arm/include/asm/delay.h @@ -57,6 +57,22 @@ extern void __bad_udelay(void); __const_udelay((n) * UDELAY_MULT)) : \ __udelay(n)) +#define spin_event_timeout(condition, timeout, delay) \ +({ \ + typeof(condition) __ret; \ + int i = 0; \ + while (!(__ret = (condition)) && (i++ < timeout)) { \ + if (delay) \ + udelay(delay); \ + else \ + cpu_relax(); \ + udelay(1); \ + } \ + if (!__ret) \ + __ret = (condition); \ + __ret; \ +}) + /* Loop-based definitions for assembly code. */ extern void __loop_delay(unsigned long loops); extern void __loop_udelay(unsigned long usecs); diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index d070741..4bec694 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -206,6 +206,34 @@ extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr); #endif #endif +/* access ports */ +#define setbits32(_addr, _v) iowrite32be(ioread32be(_addr) | (_v), (_addr)) +#define clrbits32(_addr, _v) iowrite32be(ioread32be(_addr) & ~(_v), (_addr)) + +#define setbits16(_addr, _v) iowrite16be(ioread16be(_addr) | (_v), (_addr)) +#define clrbits16(_addr, _v) iowrite16be(ioread16be(_addr) & ~(_v), (_addr)) + +#define setbits8(_addr, _v) iowrite8(ioread8(_addr) | (_v), (_addr)) +#define clrbits8(_addr, _v) iowrite8(ioread8(_addr) & ~(_v), (_addr)) + +/* Clear and set bits in one shot. These macros can be used to clear and + * set multiple bits in a register using a single read-modify-write. These + * macros can also be used to set a multiple-bit bit pattern using a mask, + * by specifying the mask in the 'clear' parameter and the new bit pattern + * in the 'set' parameter. + */ + +#define clrsetbits_be32(addr, clear, set) \ + iowrite32be((ioread32be(addr) & ~(clear)) | (set), (addr)) +#define clrsetbits_le32(addr, clear, set) \ + iowrite32le((ioread32le(addr) & ~(clear)) | (set), (addr)) +#define clrsetbits_be16(addr, clear, set) \ + iowrite16be((ioread16be(addr) & ~(clear)) | (set), (addr)) +#define clrsetbits_le16(addr, clear, set) \ + iowrite16le((ioread16le(addr) & ~(clear)) | (set), (addr)) +#define clrsetbits_8(addr, clear, set) \ + iowrite8((ioread8(addr) & ~(clear)) | (set), (addr)) + /* * IO port access primitives * ------------------------- diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h index 53c15de..4358904 100644 --- a/arch/arm/include/asm/irq.h +++ b/arch/arm/include/asm/irq.h @@ -30,6 +30,8 @@ extern void asm_do_IRQ(unsigned int, struct pt_regs *); void handle_IRQ(unsigned int, struct pt_regs *); void init_IRQ(void); +extern irq_hw_number_t virq_to_hw(unsigned int virq); + #ifdef CONFIG_MULTI_IRQ_HANDLER extern void (*handle_arch_irq)(struct pt_regs *); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 9723d17..afa204a 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -121,6 +121,13 @@ void __init init_IRQ(void) machine_desc->init_irq(); } +irq_hw_number_t virq_to_hw(unsigned int virq) +{ + struct irq_data *irq_data = irq_get_irq_data(virq); + return WARN_ON(!irq_data) ? 0 : irq_data->hwirq; +} +EXPORT_SYMBOL_GPL(virq_to_hw); + #ifdef CONFIG_MULTI_IRQ_HANDLER void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) { diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S index db1536b..101a2b4 100644 --- a/arch/arm/kernel/sleep.S +++ b/arch/arm/kernel/sleep.S @@ -130,6 +130,10 @@ ENDPROC(cpu_resume_after_mmu) .data .align ENTRY(cpu_resume) +#ifdef CONFIG_ARM_VIRT_EXT + bl __hyp_stub_install_secondary +#endif + safe_svcmode_maskall r1 mov r1, #0 ALT_SMP(mrc p15, 0, r0, c0, c0, 5) ALT_UP_B(1f) @@ -147,7 +151,6 @@ ENTRY(cpu_resume) ldr r0, [r0, #SLEEP_SAVE_SP_PHYS] ldr r0, [r0, r1, lsl #2] - setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set SVC, irqs off @ load phys pgd, stack, resume fn ARM( ldmia r0!, {r1, sp, pc} ) THUMB( ldmia r0!, {r1, r2, r3} ) diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 98aee32..829a96d 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -11,25 +11,26 @@ * This file contains the ARM-specific time handling details: * reading the RTC at bootup, etc... */ +#include <linux/clk-provider.h> +#include <linux/clocksource.h> +#include <linux/errno.h> #include <linux/export.h> -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/time.h> #include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/profile.h> #include <linux/sched.h> +#include <linux/sched_clock.h> #include <linux/smp.h> +#include <linux/time.h> #include <linux/timex.h> -#include <linux/errno.h> -#include <linux/profile.h> #include <linux/timer.h> -#include <linux/clocksource.h> -#include <linux/irq.h> -#include <linux/sched_clock.h> -#include <asm/thread_info.h> -#include <asm/stacktrace.h> #include <asm/mach/arch.h> #include <asm/mach/time.h> +#include <asm/stacktrace.h> +#include <asm/thread_info.h> #if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) || \ defined(CONFIG_NVRAM) || defined(CONFIG_NVRAM_MODULE) @@ -116,8 +117,12 @@ int __init register_persistent_clock(clock_access_fn read_boot, void __init time_init(void) { - if (machine_desc->init_time) + if (machine_desc->init_time) { machine_desc->init_time(); - else + } else { +#ifdef CONFIG_COMMON_CLK + of_clk_init(NULL); +#endif clocksource_of_init(); + } } diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 29a8af6..16b5206 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -846,6 +846,25 @@ config SOC_VF610 help This enable support for Freescale Vybrid VF610 processor. +config FSL_SOC + bool + +config SOC_LS1021A + bool "Freescale LS1021A support" + select CPU_V7 + select ARM_GIC + select CLKSRC_OF + select HAVE_ARM_ARCH_TIMER + select HAVE_SMP + select FSL_SOC + select MIGHT_HAVE_PCI + select PCI_DOMAINS if PCI + select ZONE_DMA if ARM_LPAE + select FSL_SLEEP_FSM if PM + + help + This enable support for Freescale LS1021A processor. + endif source "arch/arm/mach-imx/devices/Kconfig" diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 5383c58..c5b7cca 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o ifeq ($(CONFIG_PM),y) obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o +obj-$(CONFIG_SOC_LS1021A) += pm-ls1.o sleep-ls1.o endif # i.MX5 based machines @@ -114,4 +115,8 @@ obj-$(CONFIG_SOC_IMX53) += mach-imx53.o obj-$(CONFIG_SOC_VF610) += clk-vf610.o mach-vf610.o +obj-$(CONFIG_SOC_LS1021A) += mach-ls1021a.o + +obj-$(CONFIG_SOC_LS1021A) += clk-ls1021a.o + obj-y += devices/ diff --git a/arch/arm/mach-imx/clk-ls1021a.c b/arch/arm/mach-imx/clk-ls1021a.c new file mode 100644 index 0000000..bba7c9a --- /dev/null +++ b/arch/arm/mach-imx/clk-ls1021a.c @@ -0,0 +1,191 @@ +/* + * Copyright 2014 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 <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <dt-bindings/clock/ls1021a-clock.h> + +#include "clk.h" + +static struct clk *clks[LS1021A_CLK_END]; +static struct clk_onecell_data clk_data; + +/* The DCFG registers are in big endian mode on LS1021A SoC */ +static u8 ls1021a_clk_shift_be(u8 shift) +{ + u8 m, n; + + m = shift / 8; + n = shift % 8; + + return (u8)((3 - m) * 8 + n); +} + +static void __init ls1021a_clocks_init(struct device_node *np) +{ + static void __iomem *dcfg_base; + int i; + +#define DCFG_CCSR_DEVDISR1 (dcfg_base + 0x70) +#define DCFG_CCSR_DEVDISR2 (dcfg_base + 0x74) +#define DCFG_CCSR_DEVDISR3 (dcfg_base + 0x78) +#define DCFG_CCSR_DEVDISR4 (dcfg_base + 0x7c) +#define DCFG_CCSR_DEVDISR5 (dcfg_base + 0x80) +#define DCFG_CCSR_RSTRQMR1 (dcfg_base + 0xc0) + + if (dcfg_base) { + pr_warn("LS1021A gate clk has already added.\n"); + return; + } + + dcfg_base = of_iomap(np, 0); + + BUG_ON(!dcfg_base); + + clks[LS1021A_CLK_PBL_EN] = ls1021a_clk_gate("pbl_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(0)); + clks[LS1021A_CLK_ESDHC_EN] = ls1021a_clk_gate("esdhc_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(2)); + clks[LS1021A_CLK_DMA1_EN] = ls1021a_clk_gate("dma1_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(8)); + clks[LS1021A_CLK_DMA2_EN] = ls1021a_clk_gate("dma2_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(9)); + clks[LS1021A_CLK_USB3_PHY_EN] = ls1021a_clk_gate("usb3_phy_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(12)); + clks[LS1021A_CLK_USB2_EN] = ls1021a_clk_gate("usb2_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(13)); + clks[LS1021A_CLK_SATA_EN] = ls1021a_clk_gate("sata_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(16)); + clks[LS1021A_CLK_USB3_EN] = ls1021a_clk_gate("usb3_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(17)); + clks[LS1021A_CLK_SEC_EN] = ls1021a_clk_gate("sec_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(22)); + clks[LS1021A_CLK_2D_ACE_EN] = ls1021a_clk_gate("2d_ace_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(30)); + clks[LS1021A_CLK_QE_EN] = ls1021a_clk_gate("qe_en", "dummy", + DCFG_CCSR_DEVDISR1, + ls1021a_clk_shift_be(31)); + + clks[LS1021A_CLK_ETSEC1_EN] = ls1021a_clk_gate("etsec1_en", "dummy", + DCFG_CCSR_DEVDISR2, + ls1021a_clk_shift_be(0)); + clks[LS1021A_CLK_ETSEC2_EN] = ls1021a_clk_gate("etsec2_en", "dummy", + DCFG_CCSR_DEVDISR2, + ls1021a_clk_shift_be(1)); + clks[LS1021A_CLK_ETSEC3_EN] = ls1021a_clk_gate("etsec3_en", "dummy", + DCFG_CCSR_DEVDISR2, + ls1021a_clk_shift_be(2)); + + clks[LS1021A_CLK_PEX1_EN] = ls1021a_clk_gate("pex1_en", "dummy", + DCFG_CCSR_DEVDISR3, + ls1021a_clk_shift_be(0)); + clks[LS1021A_CLK_PEX2_EN] = ls1021a_clk_gate("pex2_en", "dummy", + DCFG_CCSR_DEVDISR3, + ls1021a_clk_shift_be(1)); + + clks[LS1021A_CLK_DUART1_EN] = ls1021a_clk_gate("duart1_en", "dummy", + DCFG_CCSR_DEVDISR4, + ls1021a_clk_shift_be(2)); + clks[LS1021A_CLK_DUART2_EN] = ls1021a_clk_gate("duart2_en", "dummy", + DCFG_CCSR_DEVDISR4, + ls1021a_clk_shift_be(3)); + clks[LS1021A_CLK_QSPI_EN] = ls1021a_clk_gate("qspi_en", "dummy", + DCFG_CCSR_DEVDISR4, + ls1021a_clk_shift_be(4)); + + clks[LS1021A_CLK_DDR_EN] = ls1021a_clk_gate("ddr_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(0)); + clks[LS1021A_CLK_OCRAM1_EN] = ls1021a_clk_gate("ocram1_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(4)); + clks[LS1021A_CLK_IFC_EN] = ls1021a_clk_gate("ifc_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(8)); + clks[LS1021A_CLK_GPIO_EN] = ls1021a_clk_gate("gpio_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(9)); + clks[LS1021A_CLK_DBG_EN] = ls1021a_clk_gate("dbg_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(10)); + clks[LS1021A_CLK_FLEXCAN1_EN] = ls1021a_clk_gate("flexcan1_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(12)); + clks[LS1021A_CLK_FLEXCAN234_EN] = ls1021a_clk_gate("flexcan234_en", + "dummy", DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(13)); + /* For flextimer 2/3/4/5/6/7/8 */ + clks[LS1021A_CLK_FLEXTIMER_EN] = ls1021a_clk_gate("flextimer_en", + "dummy", DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(14)); + clks[LS1021A_CLK_SECMON_EN] = ls1021a_clk_gate("secmon_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(17)); + clks[LS1021A_CLK_WDOG_EN] = ls1021a_clk_gate("wdog_en", "dummy", + DCFG_CCSR_RSTRQMR1, + ls1021a_clk_shift_be(22)); + clks[LS1021A_CLK_WDOG12_EN] = ls1021a_clk_gate("wdog12_en", "wdog_en", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(21)); + clks[LS1021A_CLK_I2C23_EN] = ls1021a_clk_gate("i2c23_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(22)); + /* For SAI 1/2/3/4 */ + clks[LS1021A_CLK_SAI_EN] = ls1021a_clk_gate("sai_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(23)); + /* For lpuart 2/3/4/5/6 */ + clks[LS1021A_CLK_LPUART_EN] = ls1021a_clk_gate("lpuart_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(24)); + clks[LS1021A_CLK_DSPI12_EN] = ls1021a_clk_gate("dspi12_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(25)); + clks[LS1021A_CLK_ASRC_EN] = ls1021a_clk_gate("asrc_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(26)); + clks[LS1021A_CLK_SPDIF_EN] = ls1021a_clk_gate("spdif_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(27)); + clks[LS1021A_CLK_I2C1_EN] = ls1021a_clk_gate("i2c1_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(29)); + clks[LS1021A_CLK_LPUART1_EN] = ls1021a_clk_gate("lpuart1_en", "dummy", + DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(30)); + clks[LS1021A_CLK_FLEXTIMER1_EN] = ls1021a_clk_gate("flextimer1_en", + "dummy", DCFG_CCSR_DEVDISR5, + ls1021a_clk_shift_be(31)); + + for (i = 0; i < LS1021A_CLK_END; i++) { + if (IS_ERR(clks[i])) { + pr_err("LS1021A clk %d: register failed with %ld\n", + i, PTR_ERR(clks[i])); + BUG(); + } + } + + /* Add the clocks to provider list */ + clk_data.clks = clks; + clk_data.clk_num = LS1021A_CLK_END; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(ls1021a, "fsl,ls1021a-gate", ls1021a_clocks_init); diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 048c5ad8..2a58d01 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -86,6 +86,14 @@ static inline struct clk *imx_clk_gate(const char *name, const char *parent, shift, 0, &imx_ccm_lock); } +static inline struct clk *ls1021a_clk_gate(const char *name, const char *parent, + void __iomem *reg, u8 shift) +{ + return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT | + CLK_IGNORE_UNUSED, reg, shift, + CLK_GATE_SET_TO_DISABLE, &imx_ccm_lock); +} + static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char **parents, int num_parents) { diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 4517fd7..b34ff2e 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -115,6 +115,7 @@ void tzic_handle_irq(struct pt_regs *); extern void imx_enable_cpu(int cpu, bool enable); extern void imx_set_cpu_jump(int cpu, void *jump_addr); extern u32 imx_get_cpu_arg(int cpu); +extern u32 ls1_get_cpu_arg(int cpu); extern void imx_set_cpu_arg(int cpu, u32 arg); extern void v7_cpu_resume(void); #ifdef CONFIG_SMP @@ -122,6 +123,7 @@ extern void v7_secondary_startup(void); extern void imx_scu_map_io(void); extern void imx_smp_prepare(void); extern void imx_scu_standby_enable(void); +extern void secondary_startup(void); #else static inline void imx_scu_map_io(void) {} static inline void imx_smp_prepare(void) {} @@ -144,6 +146,9 @@ extern void imx6q_set_chicken_bit(void); extern void imx_cpu_die(unsigned int cpu); extern int imx_cpu_kill(unsigned int cpu); +extern void ls1021a_cpu_die(unsigned int cpu); +extern int ls1021a_cpu_kill(unsigned int cpu); + #ifdef CONFIG_PM extern void imx6q_pm_init(void); extern void imx5_pm_init(void); @@ -165,5 +170,6 @@ static inline void imx_init_l2cache(void) {} #endif extern struct smp_operations imx_smp_ops; +extern struct smp_operations ls1021a_smp_ops; #endif diff --git a/arch/arm/mach-imx/hotplug.c b/arch/arm/mach-imx/hotplug.c index 3daf1ed..bbbb96e 100644 --- a/arch/arm/mach-imx/hotplug.c +++ b/arch/arm/mach-imx/hotplug.c @@ -14,6 +14,9 @@ #include <linux/jiffies.h> #include <asm/cp15.h> #include <asm/proc-fns.h> +#include<asm/smp.h> +#include<asm/smp_plat.h> +#include<asm/cacheflush.h> #include "common.h" @@ -38,6 +41,22 @@ static inline void cpu_enter_lowpower(void) : "cc"); } +static inline void cpu_leave_lowpower(void) +{ + unsigned int v; + + asm volatile( + " mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " orr %0, %0, %2\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (CR_C), "Ir" (0x40) + : "cc"); +} + /* * platform-specific code to shutdown a CPU * @@ -66,3 +85,41 @@ int imx_cpu_kill(unsigned int cpu) imx_set_cpu_arg(cpu, 0); return 1; } + +/* + * For LS102x platforms, shutdowning a CPU is not supported by hardware. + * So we just put the offline CPU into lower-power state here. + */ +void __ref ls1021a_cpu_die(unsigned int cpu) +{ + v7_exit_coherency_flush(louis); + + /*we are ready to enter lower-power state*/ + wfi(); + /* + * bring this CPU back into the world of cache + * coherency, and then restore interrupts + */ + cpu_leave_lowpower(); + + /* + * Do not return to the idle loop - jump back to the secondary + * cpu initialisation. There's some initialisation which needs + * to be repeated to undo the effects of taking the CPU offline. + */ + __asm__("mov sp, %0\n" + " mov fp, #0\n" + " b secondary_startup" + : + : "r" (task_stack_page(current) + THREAD_SIZE - 8)); +} + +int ls1021a_cpu_kill(unsigned int cpu) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(50); + + while (!ls1_get_cpu_arg(cpu)) + if (time_after(jiffies, timeout)) + return 0; + return 1; +} diff --git a/arch/arm/mach-imx/mach-ls1021a.c b/arch/arm/mach-imx/mach-ls1021a.c new file mode 100644 index 0000000..3895ffc --- /dev/null +++ b/arch/arm/mach-imx/mach-ls1021a.c @@ -0,0 +1,31 @@ +/* + * Copyright 2013-2014 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 <linux/of_platform.h> +#include <asm/mach/arch.h> + +#include "common.h" + +static void __init ls1021a_init_machine(void) +{ + mxc_arch_reset_init_dt(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static const char *ls1021a_dt_compat[] __initdata = { + "fsl,ls1021a", + NULL, +}; + +DT_MACHINE_START(LS1021A, "Freescale LS1021A") + .smp = smp_ops(ls1021a_smp_ops), + .init_machine = ls1021a_init_machine, + .dt_compat = ls1021a_dt_compat, + .restart = mxc_restart, +MACHINE_END diff --git a/arch/arm/mach-imx/platsmp.c b/arch/arm/mach-imx/platsmp.c index 1f24c1f..c412178 100644 --- a/arch/arm/mach-imx/platsmp.c +++ b/arch/arm/mach-imx/platsmp.c @@ -16,6 +16,8 @@ #include <asm/page.h> #include <asm/smp_scu.h> #include <asm/mach/map.h> +#include <linux/of.h> +#include <linux/of_address.h> #include "common.h" #include "hardware.h" @@ -105,3 +107,37 @@ struct smp_operations imx_smp_ops __initdata = { .cpu_kill = imx_cpu_kill, #endif }; + +#define DCFG_CCSR_SCRATCHRW1 0x200 + +static int ls1021a_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + return 0; +} + +static void __init ls1021a_smp_prepare_cpus(unsigned int max_cpus) +{ + struct device_node *np; + void __iomem *dcfg_base; + unsigned long paddr; + + np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcfg"); + dcfg_base = of_iomap(np, 0); + BUG_ON(!dcfg_base); + + paddr = virt_to_phys(secondary_startup); + writel_relaxed(cpu_to_be32(paddr), dcfg_base + DCFG_CCSR_SCRATCHRW1); + + iounmap(dcfg_base); +} + +struct smp_operations ls1021a_smp_ops __initdata = { + .smp_prepare_cpus = ls1021a_smp_prepare_cpus, + .smp_boot_secondary = ls1021a_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = ls1021a_cpu_die, + .cpu_kill = ls1021a_cpu_kill, +#endif +}; diff --git a/arch/arm/mach-imx/pm-ls1.c b/arch/arm/mach-imx/pm-ls1.c new file mode 100644 index 0000000..bdcd92b --- /dev/null +++ b/arch/arm/mach-imx/pm-ls1.c @@ -0,0 +1,389 @@ +/* + * Support deep sleep feature for LS1 + * + * Copyright 2014 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 <linux/kernel.h> +#include <linux/suspend.h> +#include <linux/io.h> +#include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/cpu_pm.h> +#include <asm/suspend.h> +#include <asm/delay.h> +#include <asm/cp15.h> +#include <asm/cacheflush.h> +#include <asm/idmap.h> + +#include "common.h" +#include "sleep-ls1.h" + +#define FSL_SLEEP 0x1 +#define FSL_DEEP_SLEEP 0x2 + +#define DCSR_EPU_EPSMCR15 0x278 +#define DCSR_EPU_EPECR0 0x300 +#define DCSR_RCPM_CG1CR0 0x31c +#define DCSR_RCPM_CSTTACR0 0xb00 + +#define CCSR_SCFG_DPSLPCR 0 +#define CCSR_SCFG_DPSLPCR_VAL 0x1 +#define CCSR_SCFG_PMCINTECR 0x160 +#define CCSR_SCFG_PMCINTLECR 0x164 +#define CCSR_SCFG_PMCINTSR 0x168 +#define CCSR_SCFG_SPARECR2 0x504 +#define CCSR_SCFG_SPARECR3 0x508 + +#define CCSR_DCFG_CRSTSR 0x400 +#define CCSR_DCFG_CRSTSR_VAL 0x00000008 + +#define CCSR_RCPM_POWMGTCSR 0x130 +#define CCSR_RCPM_POWMGTCSR_LPM20_REQ 0x00100000 +#define CCSR_RCPM_POWMGTCSR_LPM20_ST 0x00000200 +#define CCSR_RCPM_POWMGTCSR_P_LPM20_ST 0x00000100 +#define CCSR_RCPM_CLPCL10SETR 0x1c4 +#define CCSR_RCPM_CLPCL10SETR_C0 0x1 +#define CCSR_RCPM_IPPDEXPCR0 0x140 +#define CCSR_RCPM_IPPDEXPCR1 0x144 + +#define QIXIS_CTL_SYS 0x5 +#define QIXIS_CTL_SYS_EVTSW_MASK 0x0c +#define QIXIS_CTL_SYS_EVTSW_IRQ 0x04 + +#define QIXIS_PWR_CTL2 0x21 +#define QIXIS_PWR_CTL2_PCTL 0x2 + +#define OCRAM_BASE 0x10000000 +#define OCRAM_SIZE 0x10000 /* 64K */ +/* use the last page of SRAM */ +#define SRAM_CODE_BASE_PHY (OCRAM_BASE + OCRAM_SIZE - PAGE_SIZE) + +struct ls1_pm_baseaddr { + void __iomem *epu; + void __iomem *dcsr_rcpm1; + void __iomem *dcsr_rcpm2; + void __iomem *rcpm; + void __iomem *scfg; + void __iomem *dcfg; + void __iomem *fpga; + void __iomem *sram; +}; + +/* 128 bytes buffer for restoring data broke by DDR training initialization */ +#define DDR_BUF_SIZE 128 +static u8 ddr_buff[DDR_BUF_SIZE] __aligned(64); +static struct ls1_pm_baseaddr ls1_pm_base; +/* supported sleep modes by the present platform */ +static unsigned int sleep_modes; + +static void ls1_pm_iomap(void) +{ + struct device_node *np; + void *base; + + np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcsr-epu"); + base = of_iomap(np, 0); + BUG_ON(!base); + ls1_pm_base.epu = base; + + np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcsr-rcpm"); + base = of_iomap(np, 0); + BUG_ON(!base); + ls1_pm_base.dcsr_rcpm1 = base; + base = of_iomap(np, 1); + BUG_ON(!base); + ls1_pm_base.dcsr_rcpm2 = base; + + np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg"); + base = of_iomap(np, 0); + BUG_ON(!base); + ls1_pm_base.scfg = base; + + np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-dcfg"); + base = of_iomap(np, 0); + BUG_ON(!base); + ls1_pm_base.dcfg = base; + + np = of_find_compatible_node(NULL, NULL, "fsl,ls1021aqds-fpga"); + base = of_iomap(np, 0); + BUG_ON(!base); + ls1_pm_base.fpga = base; + + base = ioremap(SRAM_CODE_BASE_PHY, PAGE_SIZE); + BUG_ON(!base); + ls1_pm_base.sram = base; +} + +static void ls1_pm_uniomap(void) +{ + iounmap(ls1_pm_base.epu); + iounmap(ls1_pm_base.dcsr_rcpm1); + iounmap(ls1_pm_base.dcsr_rcpm2); + iounmap(ls1_pm_base.scfg); + iounmap(ls1_pm_base.dcfg); + iounmap(ls1_pm_base.fpga); + iounmap(ls1_pm_base.sram); +} + + +static void ls1_setup_wakeup_source(void) +{ + /* enable wakeup interrupt during deep sleep */ + iowrite32be(0xfcfc0000, ls1_pm_base.scfg + CCSR_SCFG_PMCINTECR); + iowrite32be(0, ls1_pm_base.scfg + CCSR_SCFG_PMCINTLECR); + /* clear PMC interrupt status */ + iowrite32be(0xffffffff, ls1_pm_base.scfg + CCSR_SCFG_PMCINTSR); +} + +static void ls1_clear_wakeup_source(void) +{ + /* disable wakeup interrupt during deep sleep */ + iowrite32be(0, ls1_pm_base.scfg + CCSR_SCFG_PMCINTECR); + /* clear PMC interrupt status */ + iowrite32be(0xffffffff, ls1_pm_base.scfg + CCSR_SCFG_PMCINTSR); +} + +/* set IP powerdown exception, make them work during sleep/deep sleep */ +static void ls1_set_powerdown(void) +{ + iowrite32be(0x8000c000, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR0); + iowrite32be(0xf0000000, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR1); +} + +static void ls1_save_ddr(void *base) +{ + u32 ddr_buff_addr; + + /* + * DDR training initialization will break 128 bytes at the beginning + * of DDR, therefore, save them so that the bootloader will restore + * them. Assume that DDR is mapped to the address space started with + * CONFIG_PAGE_OFFSET. + */ + memcpy(ddr_buff, (void *)CONFIG_PAGE_OFFSET, DDR_BUF_SIZE); + + ddr_buff_addr = (u32)__pa(ddr_buff); + + /* + * the bootloader will restore the first 128 bytes of DDR from + * the location indicated by the register SPARECR3 + */ + iowrite32(ddr_buff_addr, base + CCSR_SCFG_SPARECR3); +} + +static void ls1_set_resume_entry(void *base) +{ + u32 resume_addr; + + /* the bootloader will finally jump to this address to resume kernel */ + resume_addr = (u32)(__pa(ls1_deepsleep_resume)); + + /* use the register SPARECR2 to save the return entry */ + iowrite32(resume_addr, base + CCSR_SCFG_SPARECR2); +} + +static void ls1_copy_sram_code(void) +{ + memcpy(ls1_pm_base.sram, ls1_start_fsm, ls1_sram_code_size); +} + +static int ls1_start_deepsleep(unsigned long addr) +{ + typedef void (*ls1_deepsleep_t)(unsigned long); + ls1_deepsleep_t ls1_do_deepsleep_phy; + + /* Switch to the identity mapping */ + setup_mm_for_reboot(); + v7_exit_coherency_flush(all); + + ls1_do_deepsleep_phy = + (ls1_deepsleep_t)(unsigned long)virt_to_phys(ls1_do_deepsleep); + ls1_do_deepsleep_phy(addr); + + /* never get here */ + BUG(); + + return 0; +} + +void ls1_fsm_setup(void) +{ + iowrite32be(0x00001001, ls1_pm_base.dcsr_rcpm1 + DCSR_RCPM_CSTTACR0); + iowrite32be(0x00000001, ls1_pm_base.dcsr_rcpm1 + DCSR_RCPM_CG1CR0); + + fsl_epu_setup_default(ls1_pm_base.epu); + + /* + * pull the MCKE signal(EVT4_B pin) low before enabling + * deep sleep signals by FPGA + */ + iowrite32be(0x5, ls1_pm_base.epu + DCSR_EPU_EPECR0); + + iowrite32be(0x76300000, ls1_pm_base.epu + DCSR_EPU_EPSMCR15); +} + +static inline void ls1_clrsetbits_be32(void __iomem *addr, u32 clear, u32 set) +{ + u32 tmp; + + tmp = ioread32be(addr); + tmp = (tmp & ~clear) | set; + iowrite32be(tmp, addr); +} + +static void ls1_enter_deepsleep(void) +{ + u32 tmp; + + /* disable QE device to make deep sleep work */ + iowrite32be(0x00000001, ls1_pm_base.dcfg + 0x70); + + /* save DDR data */ + ls1_save_ddr(ls1_pm_base.scfg); + + /* save kernel resume entry */ + ls1_set_resume_entry(ls1_pm_base.scfg); + + /* Request to put cluster 0 in PCL10 state */ + ls1_clrsetbits_be32(ls1_pm_base.rcpm + CCSR_RCPM_CLPCL10SETR, + CCSR_RCPM_CLPCL10SETR_C0, + CCSR_RCPM_CLPCL10SETR_C0); + + /* setup the registers of the EPU FSM for deep sleep */ + ls1_fsm_setup(); + + /* connect the EVENT button to IRQ in FPGA */ + tmp = ioread8(ls1_pm_base.fpga + QIXIS_CTL_SYS); + tmp &= ~QIXIS_CTL_SYS_EVTSW_MASK; + tmp |= QIXIS_CTL_SYS_EVTSW_IRQ; + iowrite8(tmp, ls1_pm_base.fpga + QIXIS_CTL_SYS); + + /* enable deep sleep signals in FPGA */ + tmp = ioread8(ls1_pm_base.fpga + QIXIS_PWR_CTL2); + iowrite8(tmp | QIXIS_PWR_CTL2_PCTL, ls1_pm_base.fpga + QIXIS_PWR_CTL2); + + /* enable Warm Device Reset */ + ls1_clrsetbits_be32(ls1_pm_base.scfg + CCSR_SCFG_DPSLPCR, + CCSR_SCFG_DPSLPCR_VAL, CCSR_SCFG_DPSLPCR_VAL); + + ls1_clrsetbits_be32(ls1_pm_base.dcfg + CCSR_DCFG_CRSTSR, + CCSR_DCFG_CRSTSR_VAL, CCSR_DCFG_CRSTSR_VAL); + + /* copy the last stage code to sram */ + ls1_copy_sram_code(); + + ls1_setup_wakeup_source(); + + cpu_suspend(SRAM_CODE_BASE_PHY, ls1_start_deepsleep); + + ls1_clear_wakeup_source(); + + /* disable Warm Device Reset */ + ls1_clrsetbits_be32(ls1_pm_base.scfg + CCSR_SCFG_DPSLPCR, + CCSR_SCFG_DPSLPCR_VAL, 0); + + /* disable deep sleep signals in FPGA */ + tmp = ioread8(ls1_pm_base.fpga + QIXIS_PWR_CTL2); + iowrite8(tmp & ~QIXIS_PWR_CTL2_PCTL, ls1_pm_base.fpga + QIXIS_PWR_CTL2); + +#define DCFG_CCSR_SCRATCHRW1 0x200 + /* prepare for booting secondary cores */ + iowrite32be(virt_to_phys(secondary_startup), + ls1_pm_base.dcfg + DCFG_CCSR_SCRATCHRW1); +} + +static int ls1_suspend_enter(suspend_state_t state) +{ + int ret = 0; + + ls1_set_powerdown(); + + switch (state) { + case PM_SUSPEND_STANDBY: + flush_cache_louis(); + ls1_clrsetbits_be32(ls1_pm_base.rcpm + CCSR_RCPM_POWMGTCSR, + CCSR_RCPM_POWMGTCSR_LPM20_REQ, + CCSR_RCPM_POWMGTCSR_LPM20_REQ); + + cpu_do_idle(); + break; + + case PM_SUSPEND_MEM: + ls1_enter_deepsleep(); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static int ls1_suspend_valid(suspend_state_t state) +{ + if (state == PM_SUSPEND_STANDBY && (sleep_modes & FSL_SLEEP)) + return 1; + + if (state == PM_SUSPEND_MEM && (sleep_modes & FSL_DEEP_SLEEP)) + return 1; + + return 0; +} + +static int ls1_suspend_begin(suspend_state_t state) +{ + ls1_pm_iomap(); + + return 0; +} + +static void ls1_suspend_end(void) +{ + ls1_pm_uniomap(); +} + +static const struct platform_suspend_ops ls1_suspend_ops = { + .valid = ls1_suspend_valid, + .enter = ls1_suspend_enter, + .begin = ls1_suspend_begin, + .end = ls1_suspend_end, +}; + + +static const struct of_device_id rcpm_matches[] = { + { + .compatible = "fsl,ls1021a-rcpm", + .data = (void *)(FSL_SLEEP | FSL_DEEP_SLEEP), + }, + {} +}; + +static int __init ls1_pm_init(void) +{ + const struct of_device_id *match; + struct device_node *np; + void *base; + + np = of_find_matching_node_and_match(NULL, rcpm_matches, &match); + if (!np) { + pr_err("%s: can't find the rcpm node.\n", __func__); + return -EINVAL; + } + + base = of_iomap(np, 0); + of_node_put(np); + if (!base) + return -ENOMEM; + + sleep_modes = (unsigned int)match->data; + ls1_pm_base.rcpm = base; + suspend_set_ops(&ls1_suspend_ops); + return 0; +} +arch_initcall(ls1_pm_init); diff --git a/arch/arm/mach-imx/sleep-ls1.S b/arch/arm/mach-imx/sleep-ls1.S new file mode 100644 index 0000000..ae0a8af --- /dev/null +++ b/arch/arm/mach-imx/sleep-ls1.S @@ -0,0 +1,132 @@ +/* + * Support deep sleep feature for LS1 + * + * Copyright 2014 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 <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/cache.h> +#include <asm/cp15.h> + +#define CCSR_DDR_BASE 0x01080000 +#define CCSR_DDR_SDRAM_CFG_2 0x114 + +#define CCSR_SCFG_BASE 0x01570000 +#define CCSR_SCFG_HRSTCR 0x1a8 + +#define DCSR_EPU_BASE 0x20000000 +#define DCSR_EPU_EPGCR 0x0 +#define DCSR_EPU_EPECR0 0x300 +#define DCSR_EPU_EPECR15 0x33c + +/* for big endian registers */ +.macro ls1_set_bits, addr, value + ldr r4, \addr + ldr r5, [r4] + ldr r6, \value + rev r6, r6 + orr r5, r5, r6 + str r5, [r4] +.endm + +/* 1000 loops per round */ +.macro ls1_delay, count + mov r0, \count +11: mov r7, #1000 +12: subs r7, r7, #1 + bne 12b + subs r0, r0, #1 + bne 11b +.endm + +/* + * r0: the physical entry address of SRAM code + * + */ + .align L1_CACHE_SHIFT + .pushsection .idmap.text,"ax" +ENTRY(ls1_do_deepsleep) + /* disable MMU, M bit in SCTLR */ + mrc p15, 0, r3, c1, c0, 0 + bic r3, r3, #CR_M + mcr p15, 0, r3, c1, c0, 0 + isb + + /* jump to sram code using physical address */ + bx r0 +ENDPROC(ls1_do_deepsleep) + .popsection + +/* + * The code will be copied to SRAM. + */ + .align L1_CACHE_SHIFT +ENTRY(ls1_start_fsm) + /* set HRSTCR */ + ls1_set_bits ls1_ccsr_scfg_hrstcr_addr, ls1_ccsr_scfg_hrstcr_val + + /* Place DDR controller in self refresh mode */ + ls1_set_bits ls1_ddr_cfg2_addr, ls1_ddr_cfg2_val + + ls1_delay #2000 + + /* Set EVT4_B to lock the signal MCKE down */ + ldr r4, ls1_dcsr_epu_epecr0 + ldr r5, ls1_dcsr_epu_epecr0_val + rev r5, r5 + str r5, [r4] + + ls1_delay #2000 + + /* Enable all EPU Counters */ + ls1_set_bits ls1_dcsr_epu_epgcr_addr, ls1_dcsr_epu_epgcr_val + + /* Enable SCU15 */ + ls1_set_bits ls1_dcsr_epu_epecr15, ls1_dcsr_epu_epecr15_val + + /* Enter WFI mode, and EPU FSM will start */ +20: wfi + b 20b + +ls1_ccsr_scfg_hrstcr_addr: + .word CCSR_SCFG_BASE + CCSR_SCFG_HRSTCR +ls1_ccsr_scfg_hrstcr_val: + .word 0x80000000 + +ls1_ddr_cfg2_addr: + .word CCSR_DDR_BASE + CCSR_DDR_SDRAM_CFG_2 +ls1_ddr_cfg2_val: + .word (1 << 31) + +ls1_dcsr_epu_epgcr_addr: + .word DCSR_EPU_BASE + DCSR_EPU_EPGCR +ls1_dcsr_epu_epgcr_val: + .word 0x80000000 + +ls1_dcsr_epu_epecr0: + .word DCSR_EPU_BASE + DCSR_EPU_EPECR0 +ls1_dcsr_epu_epecr0_val: + .word 0 + +ls1_dcsr_epu_epecr15: + .word DCSR_EPU_BASE + DCSR_EPU_EPECR15 +ls1_dcsr_epu_epecr15_val: + .word 0x90000004 + +ENTRY(ls1_sram_code_size) + .word . - ls1_start_fsm + +/* the bootloader will jump to here after wakeup from deep sleep */ + .align L1_CACHE_SHIFT +ENTRY(ls1_deepsleep_resume) + THUMB( adr r6, BSYM(1f) ) + THUMB( bx r6 ) + THUMB( .thumb ) + THUMB(1: ) + b cpu_resume diff --git a/arch/arm/mach-imx/sleep-ls1.h b/arch/arm/mach-imx/sleep-ls1.h new file mode 100644 index 0000000..5255999 --- /dev/null +++ b/arch/arm/mach-imx/sleep-ls1.h @@ -0,0 +1,19 @@ +/* + * Copyright 2014 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 __ARM_SLEEP_LS1_H +#define __ARM_SLEEP_LS1_H + +void ls1_do_deepsleep(unsigned long addr); +void ls1_start_fsm(void); +void ls1_deepsleep_resume(void); +void fsl_epu_setup_default(void __iomem *epu_base); + +extern int ls1_sram_code_size; + +#endif diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c index 10a6b1a..e73cbbe 100644 --- a/arch/arm/mach-imx/src.c +++ b/arch/arm/mach-imx/src.c @@ -30,6 +30,8 @@ #define BP_SRC_SCR_CORE1_RST 14 #define BP_SRC_SCR_CORE1_ENABLE 22 +#define CCSR_TWAITSR0 0x04C + static void __iomem *src_base; static DEFINE_SPINLOCK(scr_lock); @@ -114,6 +116,25 @@ void imx_set_cpu_arg(int cpu, u32 arg) writel_relaxed(arg, src_base + SRC_GPR1 + cpu * 8 + 4); } +u32 ls1_get_cpu_arg(int cpu) +{ + struct device_node *np; + void __iomem *ls1_rcpm_base; + + np = of_find_compatible_node(NULL, NULL, "fsl,qoriq-rcpm-2.1"); + if (!np) { + pr_err("%s(): Can not find the RCPM node.\n", __func__); + return -ENODEV; + } + + ls1_rcpm_base = of_iomap(np, 0); + of_node_put(np); + WARN_ON(!ls1_rcpm_base); + + cpu = cpu_logical_map(cpu); + return ioread32be(ls1_rcpm_base + CCSR_TWAITSR0) & (1 << cpu); +} + void imx_src_prepare_restart(void) { u32 val; diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c index 3a6384c..14d4996 100644 --- a/arch/arm/mach-vexpress/dcscb.c +++ b/arch/arm/mach-vexpress/dcscb.c @@ -133,38 +133,8 @@ static void dcscb_power_down(void) if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { arch_spin_unlock(&dcscb_lock); - /* - * Flush all cache levels for this cluster. - * - * To do so we do: - * - Clear the SCTLR.C bit to prevent further cache allocations - * - Flush the whole cache - * - Clear the ACTLR "SMP" bit to disable local coherency - * - * Let's do it in the safest possible way i.e. with - * no memory access within the following sequence - * including to the stack. - * - * Note: fp is preserved to the stack explicitly prior doing - * this since adding it to the clobber list is incompatible - * with having CONFIG_FRAME_POINTER=y. - */ - asm volatile( - "str fp, [sp, #-4]! \n\t" - "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t" - "bic r0, r0, #"__stringify(CR_C)" \n\t" - "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t" - "isb \n\t" - "bl v7_flush_dcache_all \n\t" - "clrex \n\t" - "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t" - "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" - "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t" - "isb \n\t" - "dsb \n\t" - "ldr fp, [sp], #4" - : : : "r0","r1","r2","r3","r4","r5","r6","r7", - "r9","r10","lr","memory"); + /* Flush all cache levels for this cluster. */ + v7_exit_coherency_flush(all); /* * This is a harmless no-op. On platforms with a real @@ -183,26 +153,8 @@ static void dcscb_power_down(void) } else { arch_spin_unlock(&dcscb_lock); - /* - * Flush the local CPU cache. - * Let's do it in the safest possible way as above. - */ - asm volatile( - "str fp, [sp, #-4]! \n\t" - "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t" - "bic r0, r0, #"__stringify(CR_C)" \n\t" - "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t" - "isb \n\t" - "bl v7_flush_dcache_louis \n\t" - "clrex \n\t" - "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t" - "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" - "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t" - "isb \n\t" - "dsb \n\t" - "ldr fp, [sp], #4" - : : : "r0","r1","r2","r3","r4","r5","r6","r7", - "r9","r10","lr","memory"); + /* Disable and flush the local CPU cache. */ + v7_exit_coherency_flush(louis); } __mcpm_cpu_down(cpu, cluster); diff --git a/arch/arm/mach-vexpress/tc2_pm.c b/arch/arm/mach-vexpress/tc2_pm.c index e6eb481..4eb92eb 100644 --- a/arch/arm/mach-vexpress/tc2_pm.c +++ b/arch/arm/mach-vexpress/tc2_pm.c @@ -156,32 +156,7 @@ static void tc2_pm_down(u64 residency) : : "r" (0x400) ); } - /* - * We need to disable and flush the whole (L1 and L2) cache. - * Let's do it in the safest possible way i.e. with - * no memory access within the following sequence - * including the stack. - * - * Note: fp is preserved to the stack explicitly prior doing - * this since adding it to the clobber list is incompatible - * with having CONFIG_FRAME_POINTER=y. - */ - asm volatile( - "str fp, [sp, #-4]! \n\t" - "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t" - "bic r0, r0, #"__stringify(CR_C)" \n\t" - "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t" - "isb \n\t" - "bl v7_flush_dcache_all \n\t" - "clrex \n\t" - "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t" - "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" - "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t" - "isb \n\t" - "dsb \n\t" - "ldr fp, [sp], #4" - : : : "r0","r1","r2","r3","r4","r5","r6","r7", - "r9","r10","lr","memory"); + v7_exit_coherency_flush(all); cci_disable_port_by_cpu(mpidr); @@ -197,26 +172,7 @@ static void tc2_pm_down(u64 residency) arch_spin_unlock(&tc2_pm_lock); - /* - * We need to disable and flush only the L1 cache. - * Let's do it in the safest possible way as above. - */ - asm volatile( - "str fp, [sp, #-4]! \n\t" - "mrc p15, 0, r0, c1, c0, 0 @ get CR \n\t" - "bic r0, r0, #"__stringify(CR_C)" \n\t" - "mcr p15, 0, r0, c1, c0, 0 @ set CR \n\t" - "isb \n\t" - "bl v7_flush_dcache_louis \n\t" - "clrex \n\t" - "mrc p15, 0, r0, c1, c0, 1 @ get AUXCR \n\t" - "bic r0, r0, #(1 << 6) @ disable local coherency \n\t" - "mcr p15, 0, r0, c1, c0, 1 @ set AUXCR \n\t" - "isb \n\t" - "dsb \n\t" - "ldr fp, [sp], #4" - : : : "r0","r1","r2","r3","r4","r5","r6","r7", - "r9","r10","lr","memory"); + v7_exit_coherency_flush(louis); } __mcpm_cpu_down(cpu, cluster); diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index ced046d..2267b73 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -92,7 +92,7 @@ ENDPROC(cpu_v7_dcache_clean_area) /* Suspend/resume support: derived from arch/arm/mach-s5pv210/sleep.S */ .globl cpu_v7_suspend_size -.equ cpu_v7_suspend_size, 4 * 8 +.equ cpu_v7_suspend_size, 4 * 9 #ifdef CONFIG_ARM_CPU_SUSPEND ENTRY(cpu_v7_do_suspend) stmfd sp!, {r4 - r10, lr} @@ -101,13 +101,17 @@ ENTRY(cpu_v7_do_suspend) stmia r0!, {r4 - r5} #ifdef CONFIG_MMU mrc p15, 0, r6, c3, c0, 0 @ Domain ID +#ifdef CONFIG_ARM_LPAE + mrrc p15, 1, r5, r7, c2 @ TTB 1 +#else mrc p15, 0, r7, c2, c0, 1 @ TTB 1 +#endif mrc p15, 0, r11, c2, c0, 2 @ TTB control register #endif mrc p15, 0, r8, c1, c0, 0 @ Control register mrc p15, 0, r9, c1, c0, 1 @ Auxiliary control register mrc p15, 0, r10, c1, c0, 2 @ Co-processor access control - stmia r0, {r6 - r11} + stmia r0, {r5 - r11} ldmfd sp!, {r4 - r10, pc} ENDPROC(cpu_v7_do_suspend) @@ -118,16 +122,19 @@ ENTRY(cpu_v7_do_resume) ldmia r0!, {r4 - r5} mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID mcr p15, 0, r5, c13, c0, 3 @ User r/o thread ID - ldmia r0, {r6 - r11} + ldmia r0, {r5 - r11} #ifdef CONFIG_MMU mcr p15, 0, ip, c8, c7, 0 @ invalidate TLBs mcr p15, 0, r6, c3, c0, 0 @ Domain ID -#ifndef CONFIG_ARM_LPAE +#ifdef CONFIG_ARM_LPAE + mcrr p15, 0, r1, ip, c2 @ TTB 0 + mcrr p15, 1, r5, r7, c2 @ TTB 1 +#else ALT_SMP(orr r1, r1, #TTB_FLAGS_SMP) ALT_UP(orr r1, r1, #TTB_FLAGS_UP) -#endif mcr p15, 0, r1, c2, c0, 0 @ TTB 0 mcr p15, 0, r7, c2, c0, 1 @ TTB 1 +#endif mcr p15, 0, r11, c2, c0, 2 @ TTB control register ldr r4, =PRRR @ PRRR ldr r5, =NMRR @ NMRR diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 95c2bf0..cafd166 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -721,10 +721,6 @@ config FSL_LBC controller. Also contains some common code used by drivers for specific local bus peripherals. -config FSL_IFC - bool - depends on FSL_SOC - config FSL_GTM bool depends on PPC_83xx || QUICC_ENGINE || CPM2 @@ -1028,8 +1024,6 @@ source "drivers/Kconfig" source "fs/Kconfig" -source "arch/powerpc/sysdev/qe_lib/Kconfig" - source "lib/Kconfig" source "arch/powerpc/Kconfig.debug" @@ -1048,7 +1042,4 @@ config PPC_CLOCK default n select HAVE_CLK -config PPC_LIB_RHEAP - bool - source "arch/powerpc/kvm/Kconfig" diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig index a2afada..3a225a4 100644 --- a/arch/powerpc/configs/corenet32_smp_defconfig +++ b/arch/powerpc/configs/corenet32_smp_defconfig @@ -149,6 +149,7 @@ CONFIG_RTC_DRV_DS3232=y CONFIG_RTC_DRV_CMOS=y CONFIG_UIO=y CONFIG_STAGING=y +CONFIG_MEMORY=y CONFIG_FSL_PME2=y CONFIG_FSL_PAMU=y CONFIG_VIRT_DRIVERS=y diff --git a/arch/powerpc/configs/corenet64_smp_defconfig b/arch/powerpc/configs/corenet64_smp_defconfig index 61c01cc..bcca3b3 100644 --- a/arch/powerpc/configs/corenet64_smp_defconfig +++ b/arch/powerpc/configs/corenet64_smp_defconfig @@ -172,6 +172,7 @@ CONFIG_FSL_PME2=y CONFIG_FSL_PAMU=y CONFIG_VIRT_DRIVERS=y CONFIG_FSL_HV_MANAGER=y +CONFIG_MEMORY=y CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y CONFIG_ISO9660_FS=m diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index 7e4a6dd..c18161c 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -216,6 +216,7 @@ CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_CMOS=y CONFIG_DMADEVICES=y CONFIG_FSL_DMA=y +CONFIG_MEMORY=y # CONFIG_NET_DMA is not set CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index 7eb8788..fe09bef 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -222,6 +222,7 @@ CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_CMOS=y CONFIG_DMADEVICES=y CONFIG_FSL_DMA=y +CONFIG_MEMORY=y # CONFIG_NET_DMA is not set CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y diff --git a/arch/powerpc/include/asm/fsl_85xx_cache_sram.h b/arch/powerpc/include/asm/fsl_85xx_cache_sram.h index 2af2bdc..5071a2c 100644 --- a/arch/powerpc/include/asm/fsl_85xx_cache_sram.h +++ b/arch/powerpc/include/asm/fsl_85xx_cache_sram.h @@ -26,7 +26,7 @@ #ifndef __ASM_POWERPC_FSL_85XX_CACHE_SRAM_H__ #define __ASM_POWERPC_FSL_85XX_CACHE_SRAM_H__ -#include <asm/rheap.h> +#include <linux/fsl/rheap.h> #include <linux/spinlock.h> /* @@ -37,7 +37,7 @@ struct mpc85xx_cache_sram { phys_addr_t base_phys; void *base_virt; unsigned int size; - rh_info_t *rh; + struct _rh_info *rh; spinlock_t lock; }; diff --git a/arch/powerpc/include/asm/fsl_pm.h b/arch/powerpc/include/asm/fsl_pm.h index 8e6a656..9811cb2 100644 --- a/arch/powerpc/include/asm/fsl_pm.h +++ b/arch/powerpc/include/asm/fsl_pm.h @@ -37,10 +37,16 @@ struct fsl_pm_ops { extern const struct fsl_pm_ops *qoriq_pm_ops; -struct fsm_reg_vals; +struct fsm_reg_vals { + u32 offset; + u32 value; +}; -extern void fsl_dp_fsm_setup(void __iomem *dcsr_base, struct fsm_reg_vals *val); -extern void fsl_dp_fsm_clean(void __iomem *dcsr_base, struct fsm_reg_vals *val); +void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val); +void fsl_epu_setup_default(void __iomem *epu_base); +void fsl_npc_setup_default(void __iomem *npc_base); +void fsl_fsm_clean(void __iomem *base, struct fsm_reg_vals *val); +void fsl_epu_clean_default(void __iomem *epu_base); extern int fsl_dp_iomap(void); extern void fsl_dp_iounmap(void); diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 4504332..c7b4e2f 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -26,8 +26,6 @@ obj-$(CONFIG_SMP) += locks.o obj-$(CONFIG_ALTIVEC) += vmx-helper.o endif -obj-$(CONFIG_PPC_LIB_RHEAP) += rheap.o - obj-y += code-patching.o obj-y += feature-fixups.o obj-$(CONFIG_FTR_FIXUP_SELFTEST) += feature-fixups-test.o diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index d6c7506..0ea1aee 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -254,7 +254,7 @@ config PPC4xx_GPIO config PPC4xx_OCM bool "PPC4xx On Chip Memory (OCM) support" depends on 4xx - select PPC_LIB_RHEAP + select LIB_RHEAP help Enable OCM support for PowerPC 4xx platforms with on chip memory, OCM provides the fast place for memory access to improve performance. diff --git a/arch/powerpc/platforms/83xx/km83xx.c b/arch/powerpc/platforms/83xx/km83xx.c index bf4c447..584d8cc 100644 --- a/arch/powerpc/platforms/83xx/km83xx.c +++ b/arch/powerpc/platforms/83xx/km83xx.c @@ -37,8 +37,8 @@ #include <asm/udbg.h> #include <sysdev/fsl_soc.h> #include <sysdev/fsl_pci.h> -#include <asm/qe.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/qe_ic.h> #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/misc.c b/arch/powerpc/platforms/83xx/misc.c index 125336f..3e2e6d2 100644 --- a/arch/powerpc/platforms/83xx/misc.c +++ b/arch/powerpc/platforms/83xx/misc.c @@ -17,7 +17,7 @@ #include <asm/io.h> #include <asm/hw_irq.h> #include <asm/ipic.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe_ic.h> #include <sysdev/fsl_soc.h> #include <sysdev/fsl_pci.h> diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index 8d76220..e1186be 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -36,8 +36,8 @@ #include <asm/udbg.h> #include <sysdev/fsl_soc.h> #include <sysdev/fsl_pci.h> -#include <asm/qe.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/qe_ic.h> #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index eff5baa..9f75944 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -25,8 +25,8 @@ #include <asm/time.h> #include <asm/ipic.h> #include <asm/udbg.h> -#include <asm/qe.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/qe_ic.h> #include <sysdev/fsl_soc.h> #include <sysdev/fsl_pci.h> diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index 1a26d2f..7c1a22f 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -44,8 +44,8 @@ #include <sysdev/fsl_soc.h> #include <sysdev/fsl_pci.h> #include <sysdev/simple_gpio.h> -#include <asm/qe.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/qe_ic.h> #include "mpc83xx.h" diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c index b63b42d..5e17d71 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_rdk.c +++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c @@ -20,8 +20,8 @@ #include <asm/time.h> #include <asm/ipic.h> #include <asm/udbg.h> -#include <asm/qe.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/qe_ic.h> #include <sysdev/fsl_soc.h> #include <sysdev/fsl_pci.h> diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index ae9fdb51..ab4777a 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -19,7 +19,7 @@ if PPC32 config FSL_85XX_CACHE_SRAM bool "Freescale l2cache sram support" - select PPC_LIB_RHEAP + select LIB_RHEAP help When selected, this option enables cache-sram support for memory allocation on P1/P2 QorIQ platforms. diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c index 7731c75..7157baa 100644 --- a/arch/powerpc/platforms/85xx/common.c +++ b/arch/powerpc/platforms/85xx/common.c @@ -7,7 +7,7 @@ */ #include <linux/of_platform.h> -#include <asm/qe.h> +#include <linux/fsl/qe.h> #include <sysdev/cpm2_pic.h> #include "mpc85xx.h" diff --git a/arch/powerpc/platforms/85xx/corenet_generic.c b/arch/powerpc/platforms/85xx/corenet_generic.c index e4a1400..adb65a5 100644 --- a/arch/powerpc/platforms/85xx/corenet_generic.c +++ b/arch/powerpc/platforms/85xx/corenet_generic.c @@ -26,7 +26,7 @@ #include <asm/udbg.h> #include <asm/mpic.h> #include <asm/ehv_pic.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe_ic.h> #include <linux/of_platform.h> #include <sysdev/fsl_soc.h> diff --git a/arch/powerpc/platforms/85xx/deepsleep.c b/arch/powerpc/platforms/85xx/deepsleep.c index 88dee90..03852dc 100644 --- a/arch/powerpc/platforms/85xx/deepsleep.c +++ b/arch/powerpc/platforms/85xx/deepsleep.c @@ -34,6 +34,12 @@ #define CCSR_GPIO1_GPDAT 0x130008 #define CCSR_GPIO1_GPDIR_29 0x4 +#define EPU_BLOCK_OFFSET 0x00000000 +#define NPC_BLOCK_OFFSET 0x00001000 + +#define CSTTACR0 0xb00 +#define CG1CR0 0x31c + /* 128 bytes buffer for restoring data broke by DDR training initialization */ #define DDR_BUF_SIZE 128 static u8 ddr_buff[DDR_BUF_SIZE] __aligned(64); @@ -179,14 +185,17 @@ int fsl_enter_epu_deepsleep(void) */ setbits32(ccsr_base + CPC_CPCHDBCR0, CPC_CPCHDBCR0_SPEC_DIS); - fsl_dp_fsm_setup(dcsr_base, NULL); + fsl_epu_setup_default(dcsr_base + EPU_BLOCK_OFFSET); + fsl_npc_setup_default(dcsr_base + NPC_BLOCK_OFFSET); + out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CSTTACR0, 0x00001001); + out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CG1CR0, 0x00000001); fsl_dp_enter_low(ccsr_base, dcsr_base, pld_base, pld_flag); /* disable Warm Device Reset request */ clrbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN); - fsl_dp_fsm_clean(dcsr_base, NULL); + fsl_epu_clean_default(dcsr_base + EPU_BLOCK_OFFSET); return 0; } diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index c111fba..7171ab0 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -47,8 +47,8 @@ #include <sysdev/fsl_soc.h> #include <sysdev/fsl_pci.h> #include <sysdev/simple_gpio.h> -#include <asm/qe.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/qe_ic.h> #include <asm/mpic.h> #include <asm/swiotlb.h> #include <asm/fsl_guts.h> diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index 2746b8b..ccfc8d0 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -25,8 +25,8 @@ #include <asm/prom.h> #include <asm/udbg.h> #include <asm/mpic.h> -#include <asm/qe.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/qe_ic.h> #include <asm/fsl_guts.h> #include <sysdev/fsl_soc.h> diff --git a/arch/powerpc/platforms/85xx/twr_p102x.c b/arch/powerpc/platforms/85xx/twr_p102x.c index 4e119ac..79dc247 100644 --- a/arch/powerpc/platforms/85xx/twr_p102x.c +++ b/arch/powerpc/platforms/85xx/twr_p102x.c @@ -34,8 +34,8 @@ #include <asm/prom.h> #include <asm/udbg.h> #include <asm/mpic.h> -#include <asm/qe.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/qe_ic.h> #include <asm/fsl_guts.h> #include <sysdev/fsl_soc.h> diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index be181b6..9c38a8d 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -278,30 +278,11 @@ config TAU_AVERAGE If in doubt, say N here. -config QUICC_ENGINE - bool "Freescale QUICC Engine (QE) Support" - depends on FSL_SOC && (PPC32 || PPC64) - select PPC_LIB_RHEAP - select CRC32 - help - The QUICC Engine (QE) is a new generation of communications - coprocessors on Freescale embedded CPUs (akin to CPM in older chips). - Selecting this option means that you wish to build a kernel - for a machine with a QE coprocessor. - -config QE_GPIO - bool "QE GPIO support" - depends on QUICC_ENGINE - select ARCH_REQUIRE_GPIOLIB - help - Say Y here if you're going to use hardware that connects to the - QE GPIOs. - config CPM2 bool "Enable support for the CPM2 (Communications Processor Module)" depends on (FSL_SOC_BOOKE && PPC32) || 8260 select CPM - select PPC_LIB_RHEAP + select LIB_RHEAP select PPC_PCI_CHOICE select ARCH_REQUIRE_GPIOLIB help diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 1211e3b..1d7281d 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -33,7 +33,7 @@ config PPC_8xx bool "Freescale 8xx" select FSL_SOC select 8xx - select PPC_LIB_RHEAP + select LIB_RHEAP config 40x bool "AMCC 40x" diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index ca258c5..b91e758 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -22,13 +22,11 @@ obj-$(CONFIG_FSL_LBC) += fsl_lbc.o obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) obj-$(CONFIG_FSL_PMC) += fsl_pmc.o obj-$(CONFIG_FSL_CORENET_RCPM) += fsl_rcpm.o -obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_FSL_GTM) += fsl_gtm.o obj-$(CONFIG_FSL_85XX_CACHE_SRAM) += fsl_85xx_l2ctlr.o fsl_85xx_cache_sram.o obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o obj-$(CONFIG_FSL_RIO) += fsl_rio.o fsl_rmu.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o -obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ mv64x60-$(CONFIG_PCI) += mv64x60_pci.o obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o \ mv64x60_udbg.o diff --git a/arch/powerpc/sysdev/cpm1.c b/arch/powerpc/sysdev/cpm1.c index 5e6ff38..c6f5762 100644 --- a/arch/powerpc/sysdev/cpm1.c +++ b/arch/powerpc/sysdev/cpm1.c @@ -38,7 +38,7 @@ #include <asm/cpm1.h> #include <asm/io.h> #include <asm/tlbflush.h> -#include <asm/rheap.h> +#include <linux/fsl/rheap.h> #include <asm/prom.h> #include <asm/cpm.h> diff --git a/arch/powerpc/sysdev/cpm2.c b/arch/powerpc/sysdev/cpm2.c index 8dc1e24..5a63d35 100644 --- a/arch/powerpc/sysdev/cpm2.c +++ b/arch/powerpc/sysdev/cpm2.c @@ -41,7 +41,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/cpm2.h> -#include <asm/rheap.h> +#include <linux/fsl/rheap.h> #include <asm/fs_pd.h> #include <sysdev/fsl_soc.h> diff --git a/arch/powerpc/sysdev/cpm_common.c b/arch/powerpc/sysdev/cpm_common.c index 4dd5341..0a98dca 100644 --- a/arch/powerpc/sysdev/cpm_common.c +++ b/arch/powerpc/sysdev/cpm_common.c @@ -26,7 +26,7 @@ #include <asm/udbg.h> #include <asm/io.h> -#include <asm/rheap.h> +#include <linux/fsl/rheap.h> #include <asm/cpm.h> #include <mm/mmu_decl.h> @@ -65,8 +65,8 @@ void __init udbg_init_cpm(void) #endif static spinlock_t cpm_muram_lock; -static rh_block_t cpm_boot_muram_rh_block[16]; -static rh_info_t cpm_muram_info; +static struct _rh_block cpm_boot_muram_rh_block[16]; +static struct _rh_info cpm_muram_info; static u8 __iomem *muram_vbase; static phys_addr_t muram_pbase; diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c index 1b15f93..6ae237a 100644 --- a/arch/powerpc/sysdev/ppc4xx_ocm.c +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c @@ -26,7 +26,7 @@ #include <linux/kernel.h> #include <linux/dma-mapping.h> #include <linux/of.h> -#include <asm/rheap.h> +#include <linux/fsl/rheap.h> #include <asm/ppc4xx_ocm.h> #include <linux/slab.h> #include <linux/debugfs.h> @@ -49,7 +49,7 @@ struct ocm_region { int memtotal; int memfree; - rh_info_t *rh; + struct _rh_info *rh; struct list_head list; }; diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig deleted file mode 100644 index 644b7dbf..0000000 --- a/arch/powerpc/sysdev/qe_lib/Kconfig +++ /dev/null @@ -1,27 +0,0 @@ -# -# QE Communication options -# - -config UCC_SLOW - bool - default y if SERIAL_QE - help - This option provides qe_lib support to UCC slow - protocols: UART, BISYNC, QMC - -config UCC_FAST - bool - default y if UCC_GETH || FSL_UCC_TDM || FSL_UCC_HDLC - help - This option provides qe_lib support to UCC fast - protocols: HDLC, Ethernet, ATM, transparent - -config UCC - bool - default y if UCC_FAST || UCC_SLOW - -config QE_USB - bool - default y if USB_FSL_QE - help - QE USB Controller support diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 117b312..2f5902f 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -199,13 +199,20 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, const char *algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(tfm)); unsigned int i, j, k, temp; struct scatterlist sg[8]; - char result[64]; + char *result; + char *key; struct ahash_request *req; struct tcrypt_result tresult; void *hash_buff; char *xbuf[XBUFSIZE]; int ret = -ENOMEM; + result = kmalloc(MAX_DIGEST_SIZE, GFP_KERNEL); + if (!result) + return ret; + key = kmalloc(MAX_KEYLEN, GFP_KERNEL); + if (!key) + goto out_nobuf; if (testmgr_alloc_buf(xbuf)) goto out_nobuf; @@ -230,7 +237,7 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, goto out; j++; - memset(result, 0, 64); + memset(result, 0, MAX_DIGEST_SIZE); hash_buff = xbuf[0]; hash_buff += align_offset; @@ -240,8 +247,14 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, if (template[i].ksize) { crypto_ahash_clear_flags(tfm, ~0); - ret = crypto_ahash_setkey(tfm, template[i].key, - template[i].ksize); + if (template[i].ksize > MAX_KEYLEN) { + pr_err("alg: hash: setkey failed on test %d for %s: key size %d > %d\n", + j, algo, template[i].ksize, MAX_KEYLEN); + ret = -EINVAL; + goto out; + } + memcpy(key, template[i].key, template[i].ksize); + ret = crypto_ahash_setkey(tfm, key, template[i].ksize); if (ret) { printk(KERN_ERR "alg: hash: setkey failed on " "test %d for %s: ret=%d\n", j, algo, @@ -301,7 +314,7 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, if (template[i].np) { j++; - memset(result, 0, 64); + memset(result, 0, MAX_DIGEST_SIZE); temp = 0; sg_init_table(sg, template[i].np); @@ -320,8 +333,16 @@ static int __test_hash(struct crypto_ahash *tfm, struct hash_testvec *template, } if (template[i].ksize) { + if (template[i].ksize > MAX_KEYLEN) { + pr_err("alg: hash: setkey failed on test %d for %s: key size %d > %d\n", + j, algo, template[i].ksize, + MAX_KEYLEN); + ret = -EINVAL; + goto out; + } crypto_ahash_clear_flags(tfm, ~0); - ret = crypto_ahash_setkey(tfm, template[i].key, + memcpy(key, template[i].key, template[i].ksize); + ret = crypto_ahash_setkey(tfm, key, template[i].ksize); if (ret) { @@ -373,6 +394,8 @@ out: out_noreq: testmgr_free_buf(xbuf); out_nobuf: + kfree(key); + kfree(result); return ret; } @@ -422,16 +445,21 @@ static int __test_aead(struct crypto_aead *tfm, int enc, void *input; void *output; void *assoc; - char iv[MAX_IVLEN]; + char *iv; char *xbuf[XBUFSIZE]; char *xoutbuf[XBUFSIZE]; char *axbuf[XBUFSIZE]; + iv = kzalloc(MAX_IVLEN, GFP_KERNEL); + if (!iv) + return ret; + key = kmalloc(MAX_KEYLEN, GFP_KERNEL); + if (!key) + goto out_noxbuf; if (testmgr_alloc_buf(xbuf)) goto out_noxbuf; if (testmgr_alloc_buf(axbuf)) goto out_noaxbuf; - if (diff_dst && testmgr_alloc_buf(xoutbuf)) goto out_nooutbuf; @@ -492,7 +520,14 @@ static int __test_aead(struct crypto_aead *tfm, int enc, crypto_aead_set_flags( tfm, CRYPTO_TFM_REQ_WEAK_KEY); - key = template[i].key; + if (template[i].klen > MAX_KEYLEN) { + pr_err("alg: aead%s: setkey failed on test %d for %s: key size %d > %d\n", + d, j, algo, template[i].klen, + MAX_KEYLEN); + ret = -EINVAL; + goto out; + } + memcpy(key, template[i].key, template[i].klen); ret = crypto_aead_setkey(tfm, key, template[i].klen); @@ -593,7 +628,14 @@ static int __test_aead(struct crypto_aead *tfm, int enc, if (template[i].wk) crypto_aead_set_flags( tfm, CRYPTO_TFM_REQ_WEAK_KEY); - key = template[i].key; + if (template[i].klen > MAX_KEYLEN) { + pr_err("alg: aead%s: setkey failed on test %d for %s: key size %d > %d\n", + d, j, algo, template[i].klen, + MAX_KEYLEN); + ret = -EINVAL; + goto out; + } + memcpy(key, template[i].key, template[i].klen); ret = crypto_aead_setkey(tfm, key, template[i].klen); if (!ret == template[i].fail) { @@ -775,6 +817,8 @@ out_nooutbuf: out_noaxbuf: testmgr_free_buf(xbuf); out_noxbuf: + kfree(key); + kfree(iv); return ret; } diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 7bdadcc..4bbf13a 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -32,7 +32,7 @@ #define MAX_DIGEST_SIZE 64 #define MAX_TAP 8 -#define MAX_KEYLEN 56 +#define MAX_KEYLEN 160 #define MAX_IVLEN 32 struct hash_testvec { diff --git a/drivers/Kconfig b/drivers/Kconfig index e82d0ab..774d064 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -130,6 +130,8 @@ source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" +source "drivers/soc/Kconfig" + source "drivers/clk/Kconfig" source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 1af40e9..ae44784 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -31,6 +31,9 @@ obj-y += amba/ # really early. obj-$(CONFIG_DMADEVICES) += dma/ +# SOC specific infrastructure drivers. +obj-y += soc/ + obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_XEN) += xen/ diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index fa6bf52..1f603a6 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -96,6 +96,8 @@ static struct regmap_bus regmap_i2c = { .write = regmap_i2c_write, .gather_write = regmap_i2c_gather_write, .read = regmap_i2c_read, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, }; /** diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 98745dd..1f95f8b 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -26,19 +26,75 @@ struct regmap_mmio_context { void __iomem *regs; + unsigned reg_bytes; unsigned val_bytes; + unsigned pad_bytes; struct clk *clk; }; +static inline void regmap_mmio_regsize_check(size_t reg_size) +{ + switch (reg_size) { + case 1: + case 2: + case 4: +#ifdef CONFIG_64BIT + case 8: +#endif + break; + default: + BUG(); + } +} + +static int regmap_mmio_regbits_check(size_t reg_bits) +{ + switch (reg_bits) { + case 8: + case 16: + case 32: +#ifdef CONFIG_64BIT + case 64: +#endif + return 0; + default: + return -EINVAL; + } +} + +static inline void regmap_mmio_count_check(size_t count) +{ + BUG_ON(count % 2 != 0); +} + +static inline unsigned int +regmap_mmio_get_offset(const void *reg, size_t reg_size) +{ + switch (reg_size) { + case 1: + return *(u8 *)reg; + case 2: + return *(u16 *)reg; + case 4: + return *(u32 *)reg; +#ifdef CONFIG_64BIT + case 8: + return *(u64 *)reg; +#endif + default: + BUG(); + } +} + static int regmap_mmio_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) { struct regmap_mmio_context *ctx = context; - u32 offset; + unsigned int offset; int ret; - BUG_ON(reg_size != 4); + regmap_mmio_regsize_check(reg_size); if (ctx->clk) { ret = clk_enable(ctx->clk); @@ -46,7 +102,7 @@ static int regmap_mmio_gather_write(void *context, return ret; } - offset = *(u32 *)reg; + offset = regmap_mmio_get_offset(reg, reg_size); while (val_size) { switch (ctx->val_bytes) { @@ -81,9 +137,13 @@ static int regmap_mmio_gather_write(void *context, static int regmap_mmio_write(void *context, const void *data, size_t count) { - BUG_ON(count < 4); + struct regmap_mmio_context *ctx = context; + unsigned int offset = ctx->reg_bytes + ctx->pad_bytes; - return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); + regmap_mmio_count_check(count); + + return regmap_mmio_gather_write(context, data, ctx->reg_bytes, + data + offset, count - offset); } static int regmap_mmio_read(void *context, @@ -91,10 +151,10 @@ static int regmap_mmio_read(void *context, void *val, size_t val_size) { struct regmap_mmio_context *ctx = context; - u32 offset; + unsigned int offset; int ret; - BUG_ON(reg_size != 4); + regmap_mmio_regsize_check(reg_size); if (ctx->clk) { ret = clk_enable(ctx->clk); @@ -102,7 +162,7 @@ static int regmap_mmio_read(void *context, return ret; } - offset = *(u32 *)reg; + offset = regmap_mmio_get_offset(reg, reg_size); while (val_size) { switch (ctx->val_bytes) { @@ -165,8 +225,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, int min_stride; int ret; - if (config->reg_bits != 32) - return ERR_PTR(-EINVAL); + ret = regmap_mmio_regbits_check(config->reg_bits); + if (ret) + return ERR_PTR(ret); if (config->pad_bits) return ERR_PTR(-EINVAL); @@ -209,6 +270,8 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, ctx->regs = regs; ctx->val_bytes = config->val_bits / 8; + ctx->reg_bytes = config->reg_bits / 8; + ctx->pad_bytes = config->pad_bits / 8; if (clk_id == NULL) return ctx; diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 4c506bd..4f4074a 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -109,6 +109,8 @@ static struct regmap_bus regmap_spi = { .async_alloc = regmap_spi_async_alloc, .read = regmap_spi_read, .read_flag_mask = 0x80, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, }; /** diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7d689a1..b4116f2 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -15,6 +15,7 @@ #include <linux/export.h> #include <linux/mutex.h> #include <linux/err.h> +#include <linux/of.h> #include <linux/rbtree.h> #include <linux/sched.h> @@ -201,6 +202,13 @@ static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift) b[0] = cpu_to_be16(val << shift); } +static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift) +{ + __le16 *b = buf; + + b[0] = cpu_to_le16(val << shift); +} + static void regmap_format_16_native(void *buf, unsigned int val, unsigned int shift) { @@ -225,6 +233,13 @@ static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift) b[0] = cpu_to_be32(val << shift); } +static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift) +{ + __le32 *b = buf; + + b[0] = cpu_to_le32(val << shift); +} + static void regmap_format_32_native(void *buf, unsigned int val, unsigned int shift) { @@ -249,6 +264,13 @@ static unsigned int regmap_parse_16_be(const void *buf) return be16_to_cpu(b[0]); } +static unsigned int regmap_parse_16_le(const void *buf) +{ + const __le16 *b = buf; + + return le16_to_cpu(b[0]); +} + static void regmap_parse_16_be_inplace(void *buf) { __be16 *b = buf; @@ -256,6 +278,13 @@ static void regmap_parse_16_be_inplace(void *buf) b[0] = be16_to_cpu(b[0]); } +static void regmap_parse_16_le_inplace(void *buf) +{ + __le16 *b = buf; + + b[0] = le16_to_cpu(b[0]); +} + static unsigned int regmap_parse_16_native(const void *buf) { return *(u16 *)buf; @@ -278,6 +307,13 @@ static unsigned int regmap_parse_32_be(const void *buf) return be32_to_cpu(b[0]); } +static unsigned int regmap_parse_32_le(const void *buf) +{ + const __le32 *b = buf; + + return le32_to_cpu(b[0]); +} + static void regmap_parse_32_be_inplace(void *buf) { __be32 *b = buf; @@ -285,6 +321,13 @@ static void regmap_parse_32_be_inplace(void *buf) b[0] = be32_to_cpu(b[0]); } +static void regmap_parse_32_le_inplace(void *buf) +{ + __le32 *b = buf; + + b[0] = le32_to_cpu(b[0]); +} + static unsigned int regmap_parse_32_native(const void *buf) { return *(u32 *)buf; @@ -389,6 +432,71 @@ static void regmap_range_exit(struct regmap *map) kfree(map->selector_work_buf); } +static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus, + const struct regmap_config *config) +{ + enum regmap_endian endian; + + /* Retrieve the endianness specification from the regmap config */ + endian = config->reg_format_endian; + + /* If the regmap config specified a non-default value, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + + /* Retrieve the endianness specification from the bus config */ + if (bus && bus->reg_format_endian_default) + endian = bus->reg_format_endian_default; + + /* If the bus specified a non-default value, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + + /* Use this if no other value was found */ + return REGMAP_ENDIAN_BIG; +} + +static enum regmap_endian regmap_get_val_endian(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config) +{ + struct device_node *np; + enum regmap_endian endian; + + /* Retrieve the endianness specification from the regmap config */ + endian = config->val_format_endian; + + /* If the regmap config specified a non-default value, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + + /* If the dev and dev->of_node exist try to get endianness from DT */ + if (dev && dev->of_node) { + np = dev->of_node; + + /* Parse the device's DT node for an endianness specification */ + if (of_property_read_bool(np, "big-endian")) + endian = REGMAP_ENDIAN_BIG; + else if (of_property_read_bool(np, "little-endian")) + endian = REGMAP_ENDIAN_LITTLE; + + /* If the endianness was specified in DT, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + } + + /* Retrieve the endianness specification from the bus config */ + if (bus && bus->val_format_endian_default) + endian = bus->val_format_endian_default; + + /* If the bus specified a non-default value, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + + /* Use this if no other value was found */ + return REGMAP_ENDIAN_BIG; +} + /** * regmap_init(): Initialise register map * @@ -484,17 +592,8 @@ struct regmap *regmap_init(struct device *dev, map->reg_read = _regmap_bus_read; } - reg_endian = config->reg_format_endian; - if (reg_endian == REGMAP_ENDIAN_DEFAULT) - reg_endian = bus->reg_format_endian_default; - if (reg_endian == REGMAP_ENDIAN_DEFAULT) - reg_endian = REGMAP_ENDIAN_BIG; - - val_endian = config->val_format_endian; - if (val_endian == REGMAP_ENDIAN_DEFAULT) - val_endian = bus->val_format_endian_default; - if (val_endian == REGMAP_ENDIAN_DEFAULT) - val_endian = REGMAP_ENDIAN_BIG; + reg_endian = regmap_get_reg_endian(bus, config); + val_endian = regmap_get_val_endian(dev, bus, config); switch (config->reg_bits + map->reg_shift) { case 2: @@ -593,6 +692,11 @@ struct regmap *regmap_init(struct device *dev, map->format.parse_val = regmap_parse_16_be; map->format.parse_inplace = regmap_parse_16_be_inplace; break; + case REGMAP_ENDIAN_LITTLE: + map->format.format_val = regmap_format_16_le; + map->format.parse_val = regmap_parse_16_le; + map->format.parse_inplace = regmap_parse_16_le_inplace; + break; case REGMAP_ENDIAN_NATIVE: map->format.format_val = regmap_format_16_native; map->format.parse_val = regmap_parse_16_native; @@ -614,6 +718,11 @@ struct regmap *regmap_init(struct device *dev, map->format.parse_val = regmap_parse_32_be; map->format.parse_inplace = regmap_parse_32_be_inplace; break; + case REGMAP_ENDIAN_LITTLE: + map->format.format_val = regmap_format_32_le; + map->format.parse_val = regmap_parse_32_le; + map->format.parse_inplace = regmap_parse_32_le_inplace; + break; case REGMAP_ENDIAN_NATIVE: map->format.format_val = regmap_format_32_native; map->format.parse_val = regmap_parse_32_native; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 279407a..0e29396 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -86,12 +86,12 @@ config COMMON_CLK_AXI_CLKGEN Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx FPGAs. It is commonly used in Analog Devices' reference designs. -config CLK_PPC_CORENET - bool "Clock driver for PowerPC corenet platforms" - depends on PPC_E500MC && OF +config CLK_QORIQ + bool "Clock driver for PowerPC corenet and ARM-based platforms" + depends on (PPC_E500MC || ARM) && OF ---help--- This adds the clock driver support for Freescale PowerPC corenet - platforms using common clock framework. + and Freescale ARM based platforms using common clock framework. endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 7b11106..d653137 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -42,4 +42,4 @@ obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o -obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o +obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-qoriq.c index e958707..c674bbf 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-qoriq.c @@ -11,6 +11,7 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/of.h> #include <linux/slab.h> @@ -19,6 +20,7 @@ struct cmux_clk { struct clk_hw hw; void __iomem *reg; u32 flags; + unsigned int num_parents; }; #define PLL_KILL BIT(31) @@ -27,14 +29,13 @@ struct cmux_clk { #define to_cmux_clk(p) container_of(p, struct cmux_clk, hw) static void __iomem *base; -static unsigned int clocks_per_pll; static int cmux_set_parent(struct clk_hw *hw, u8 idx) { struct cmux_clk *clk = to_cmux_clk(hw); u32 clksel; - clksel = ((idx / clocks_per_pll) << 2) + idx % clocks_per_pll; + clksel = ((idx / clk->num_parents) << 2) + idx % clk->num_parents; if (clk->flags & CLKSEL_ADJUST) clksel += 8; clksel = (clksel & 0xf) << CLKSEL_SHIFT; @@ -52,7 +53,7 @@ static u8 cmux_get_parent(struct clk_hw *hw) clksel = (clksel >> CLKSEL_SHIFT) & 0xf; if (clk->flags & CLKSEL_ADJUST) clksel -= 8; - clksel = (clksel >> 2) * clocks_per_pll + clksel % 4; + clksel = (clksel >> 2) * clk->num_parents + clksel % 4; return clksel; } @@ -100,6 +101,7 @@ static void __init core_mux_init(struct device_node *np) goto err_name; } cmux_clk->reg = base + offset; + cmux_clk->num_parents = count; node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen"); if (node && (offset >= 0x80)) @@ -176,9 +178,6 @@ static void __init core_pll_init(struct device_node *np) return; } - /* output clock number per PLL */ - clocks_per_pll = count; - subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL); if (!subclks) { pr_err("%s: could not allocate subclks\n", __func__); @@ -243,7 +242,7 @@ static const struct of_device_id clk_match[] __initconst = { {} }; -static int __init ppc_corenet_clk_probe(struct platform_device *pdev) +static int __init qoriq_corenet_clk_probe(struct platform_device *pdev) { struct device_node *np; @@ -258,23 +257,33 @@ static int __init ppc_corenet_clk_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id ppc_clk_ids[] __initconst = { +static const struct of_device_id qoriq_clk_ids[] __initconst = { { .compatible = "fsl,qoriq-clockgen-1.0", }, { .compatible = "fsl,qoriq-clockgen-2.0", }, {} }; -static struct platform_driver ppc_corenet_clk_driver = { +static struct platform_driver qoriq_corenet_clk_driver __initdata = { .driver = { - .name = "ppc_corenet_clock", + .name = "qoriq_corenet_clock", .owner = THIS_MODULE, - .of_match_table = ppc_clk_ids, + .of_match_table = qoriq_clk_ids, }, - .probe = ppc_corenet_clk_probe, + .probe = qoriq_corenet_clk_probe, }; -static int __init ppc_corenet_clk_init(void) +static int __init qoriq_corenet_clk_init(void) +{ + return platform_driver_register(&qoriq_corenet_clk_driver); +} +subsys_initcall(qoriq_corenet_clk_init); + +static void __init ls1021a_clocks_init(struct device_node *np) { - return platform_driver_register(&ppc_corenet_clk_driver); + base = of_iomap(np, 0); + if (!base) + return; + + of_clk_init(clk_match); } -subsys_initcall(ppc_corenet_clk_init); +CLK_OF_DECLARE(ls1021a, "fsl,ls1021a-clockgen", ls1021a_clocks_init); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 971d796..f60e242 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -106,6 +106,11 @@ config CLKSRC_SAMSUNG_PWM for all devicetree enabled platforms. This driver will be needed only on systems that do not have the Exynos MCT available. +config FSL_FTM_TIMER + bool + help + Support for Freescale FlexTimer Module (FTM) timer. + config VF_PIT_TIMER bool help diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 704d6d3..c3031f8 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o 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_FSL_FTM_TIMER) += fsl_ftm_timer.o obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c new file mode 100644 index 0000000..454227d --- /dev/null +++ b/drivers/clocksource/fsl_ftm_timer.c @@ -0,0 +1,367 @@ +/* + * Freescale FlexTimer Module (FTM) timer driver. + * + * Copyright 2014 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 <linux/clk.h> +#include <linux/clockchips.h> +#include <linux/clocksource.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/sched_clock.h> +#include <linux/slab.h> + +#define FTM_SC 0x00 +#define FTM_SC_CLK_SHIFT 3 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT) +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT) +#define FTM_SC_PS_MASK 0x7 +#define FTM_SC_TOIE BIT(6) +#define FTM_SC_TOF BIT(7) + +#define FTM_CNT 0x04 +#define FTM_MOD 0x08 +#define FTM_CNTIN 0x4C + +#define FTM_PS_MAX 7 + +struct ftm_clock_device { + void __iomem *clksrc_base; + void __iomem *clkevt_base; + unsigned long periodic_cyc; + unsigned long ps; + bool big_endian; +}; + +static struct ftm_clock_device *priv; + +static inline u32 ftm_readl(void __iomem *addr) +{ + if (priv->big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static inline void ftm_writel(u32 val, void __iomem *addr) +{ + if (priv->big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static inline void ftm_counter_enable(void __iomem *base) +{ + u32 val; + + /* select and enable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + val |= priv->ps | FTM_SC_CLK(1); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_counter_disable(void __iomem *base) +{ + u32 val; + + /* disable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_acknowledge(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOF; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_enable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val |= FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_disable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_reset_counter(void __iomem *base) +{ + /* + * The CNT register contains the FTM counter value. + * Reset clears the CNT register. Writing any value to COUNT + * updates the counter with its initial value, CNTIN. + */ + ftm_writel(0x00, base + FTM_CNT); +} + +static u64 ftm_read_sched_clock(void) +{ + return ftm_readl(priv->clksrc_base + FTM_CNT); +} + +static int ftm_set_next_event(unsigned long delta, + struct clock_event_device *unused) +{ + /* + * The CNNIN and MOD are all double buffer registers, writing + * to the MOD register latches the value into a buffer. The MOD + * register is updated with the value of its write buffer with + * the following scenario: + * a, the counter source clock is diabled. + */ + ftm_counter_disable(priv->clkevt_base); + + /* Force the value of CNTIN to be loaded into the FTM counter */ + ftm_reset_counter(priv->clkevt_base); + + /* + * The counter increments until the value of MOD is reached, + * at which point the counter is reloaded with the value of CNTIN. + * The TOF (the overflow flag) bit is set when the FTM counter + * changes from MOD to CNTIN. So we should using the delta - 1. + */ + ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD); + + ftm_counter_enable(priv->clkevt_base); + + ftm_irq_enable(priv->clkevt_base); + + return 0; +} + +static void ftm_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + ftm_set_next_event(priv->periodic_cyc, evt); + break; + case CLOCK_EVT_MODE_ONESHOT: + ftm_counter_disable(priv->clkevt_base); + break; + default: + return; + } +} + +static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + ftm_irq_acknowledge(priv->clkevt_base); + + if (likely(evt->mode == CLOCK_EVT_MODE_ONESHOT)) { + ftm_irq_disable(priv->clkevt_base); + ftm_counter_disable(priv->clkevt_base); + } + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct clock_event_device ftm_clockevent = { + .name = "Freescale ftm timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = ftm_set_mode, + .set_next_event = ftm_set_next_event, + .rating = 300, +}; + +static struct irqaction ftm_timer_irq = { + .name = "Freescale ftm timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = ftm_evt_interrupt, + .dev_id = &ftm_clockevent, +}; + +static int __init ftm_clockevent_init(unsigned long freq, int irq) +{ + int err; + + ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN); + ftm_writel(~0UL, priv->clkevt_base + FTM_MOD); + + ftm_reset_counter(priv->clkevt_base); + + err = setup_irq(irq, &ftm_timer_irq); + if (err) { + pr_err("ftm: setup irq failed: %d\n", err); + return err; + } + + ftm_clockevent.cpumask = cpumask_of(0); + ftm_clockevent.irq = irq; + + clockevents_config_and_register(&ftm_clockevent, + freq / (1 << priv->ps), + 1, 0xffff); + + ftm_counter_enable(priv->clkevt_base); + + return 0; +} + +static int __init ftm_clocksource_init(unsigned long freq) +{ + int err; + + ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN); + ftm_writel(~0UL, priv->clksrc_base + FTM_MOD); + + ftm_reset_counter(priv->clksrc_base); + + sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps)); + err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm", + freq / (1 << priv->ps), 300, 16, + clocksource_mmio_readl_up); + if (err) { + pr_err("ftm: init clock source mmio failed: %d\n", err); + return err; + } + + ftm_counter_enable(priv->clksrc_base); + + return 0; +} + +static int __init __ftm_clk_init(struct device_node *np, char *cnt_name, + char *ftm_name) +{ + struct clk *clk; + int err; + + clk = of_clk_get_by_name(np, cnt_name); + if (IS_ERR(clk)) { + pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) { + pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", + cnt_name, err); + return err; + } + + clk = of_clk_get_by_name(np, ftm_name); + if (IS_ERR(clk)) { + pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk)); + return PTR_ERR(clk); + } + err = clk_prepare_enable(clk); + if (err) + pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", + ftm_name, err); + + return clk_get_rate(clk); +} + +static unsigned long __init ftm_clk_init(struct device_node *np) +{ + unsigned long freq; + + freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt"); + if (freq <= 0) + return 0; + + freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src"); + if (freq <= 0) + return 0; + + return freq; +} + +static int __init ftm_calc_closest_round_cyc(unsigned long freq) +{ + priv->ps = 0; + + /* The counter register is only using the lower 16 bits, and + * if the 'freq' value is to big here, then the periodic_cyc + * may exceed 0xFFFF. + */ + do { + priv->periodic_cyc = DIV_ROUND_CLOSEST(freq, + HZ * (1 << priv->ps++)); + } while (priv->periodic_cyc > 0xFFFF); + + if (priv->ps > FTM_PS_MAX) { + pr_err("ftm: the prescaler is %lu > %d\n", + priv->ps, FTM_PS_MAX); + return -EINVAL; + } + + return 0; +} + +static void __init ftm_timer_init(struct device_node *np) +{ + unsigned long freq; + int irq; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return; + + priv->clkevt_base = of_iomap(np, 0); + if (!priv->clkevt_base) { + pr_err("ftm: unable to map event timer registers\n"); + goto err; + } + + priv->clksrc_base = of_iomap(np, 1); + if (!priv->clksrc_base) { + pr_err("ftm: unable to map source timer registers\n"); + goto err; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("ftm: unable to get IRQ from DT, %d\n", irq); + goto err; + } + + priv->big_endian = of_property_read_bool(np, "big-endian"); + + freq = ftm_clk_init(np); + if (!freq) + goto err; + + if (ftm_calc_closest_round_cyc(freq)) + goto err; + + if (ftm_clocksource_init(freq)) + goto err; + + if (ftm_clockevent_init(freq, irq)) + goto err; + + return; + +err: + kfree(priv); +} +CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init); diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 0fa204b..553a50c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -235,3 +235,11 @@ config ARM_TEGRA_CPUFREQ default y help This adds the CPUFreq driver support for TEGRA SOCs. + +config QORIQ_CPUFREQ + tristate "CPU frequency scaling driver for Freescale QorIQ SoCs" + depends on OF && COMMON_CLK + select CLK_QORIQ + help + This adds the CPUFreq driver support for Freescale QorIQ SoCs + which are capable of changing the CPU's frequency dynamically. diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc index e859a1d..1527a79 100644 --- a/drivers/cpufreq/Kconfig.powerpc +++ b/drivers/cpufreq/Kconfig.powerpc @@ -35,15 +35,13 @@ config MPC85xx_CPUFREQ have a JOG feature, which provides a dynamic mechanism to lower or raise the CPU core clock at runtime. -config PPC_CORENET_CPUFREQ - tristate "CPU frequency scaling driver for Freescale E500MC SoCs" - depends on PPC_E500MC && OF && COMMON_CLK - select CPU_FREQ_TABLE - select CLK_PPC_CORENET +config QORIQ_CPUFREQ + tristate "CPU frequency scaling driver for Freescale QorIQ SoCs" + depends on OF && COMMON_CLK + select CLK_QORIQ help - This adds the CPUFreq driver support for Freescale e500mc, - e5500 and e6500 series SoCs which are capable of changing - the CPU's frequency dynamically. + This adds the CPUFreq driver support for Freescale QorIQ SoCs + which are capable of changing the CPU's frequency dynamically. config CPU_FREQ_PMAC bool "Support for Apple PowerBooks" diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index cc1b57d..708f794 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -84,7 +84,7 @@ obj-$(CONFIG_CPU_FREQ_CBE) += ppc-cbe-cpufreq.o ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o -obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o +obj-$(CONFIG_QORIQ_CPUFREQ) += qoriq-cpufreq.o obj-$(CONFIG_MPC85xx_CPUFREQ) += mpc85xx-cpufreq.o obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index 8500627..9d0aa19 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -1,7 +1,7 @@ /* * Copyright 2013 Freescale Semiconductor, Inc. * - * CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs. + * CPU Frequency Scaling driver for Freescale QorIQ SoCs. * * 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 @@ -13,7 +13,6 @@ #include <linux/clk.h> #include <linux/cpufreq.h> #include <linux/errno.h> -#include <sysdev/fsl_soc.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> @@ -73,17 +72,7 @@ static const u32 *fmask; static DEFINE_MUTEX(cpufreq_lock); static DEFINE_PER_CPU(struct cpu_data *, cpu_data); -/* cpumask in a cluster */ -static DEFINE_PER_CPU(cpumask_var_t, cpu_mask); - -#ifndef CONFIG_SMP -static inline const struct cpumask *cpu_core_mask(int cpu) -{ - return cpumask_of(0); -} -#endif - -static unsigned int corenet_cpufreq_get_speed(unsigned int cpu) +static unsigned int qoriq_cpufreq_get_speed(unsigned int cpu) { struct cpu_data *data = per_cpu(cpu_data, cpu); @@ -140,7 +129,80 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table, } } -static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) +#if defined(CONFIG_ARM) +static int get_cpu_physical_id(int cpu) +{ + return topology_core_id(cpu); +} +#else +static int get_cpu_physical_id(int cpu) +{ + return get_hard_smp_processor_id(cpu); +} +#endif + +static u32 get_bus_freq(void) +{ + struct device_node *soc; + u32 sysfreq; + + soc = of_find_node_by_type(NULL, "soc"); + if (!soc) + return 0; + + if (of_property_read_u32(soc, "bus-frequency", &sysfreq)) + sysfreq = 0; + + of_node_put(soc); + + return sysfreq; +} + +static struct device_node *cpu_to_clk_node(int cpu) +{ + struct device_node *np, *clk_np; + + if (!cpu_present(cpu)) + return NULL; + + np = of_get_cpu_node(cpu, NULL); + if (!np) + return NULL; + + clk_np = of_parse_phandle(np, "clocks", 0); + if (!clk_np) + return NULL; + + of_node_put(np); + + return clk_np; +} + +/* traverse cpu nodes to get cpu mask of sharing clock wire */ +static void set_affected_cpus(struct cpufreq_policy *policy) +{ + struct device_node *np, *clk_np; + struct cpumask *dstp = policy->cpus; + int i; + + np = cpu_to_clk_node(policy->cpu); + if (!np) + return; + + for_each_present_cpu(i) { + clk_np = cpu_to_clk_node(i); + if (!clk_np) + continue; + + if (clk_np == np) + cpumask_set_cpu(i, dstp); + + of_node_put(clk_np); + } + of_node_put(np); +} + +static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) { struct device_node *np; int i, count, ret; @@ -181,7 +243,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) } if (fmask) - mask = fmask[get_hard_smp_processor_id(cpu)]; + mask = fmask[get_cpu_physical_id(cpu)]; else mask = 0x0; @@ -213,15 +275,15 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) per_cpu(cpu_data, cpu) = data; /* update ->cpus if we have cluster, no harm if not */ - cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu)); - for_each_cpu(i, per_cpu(cpu_mask, cpu)) + set_affected_cpus(policy); + for_each_cpu(i, policy->cpus) per_cpu(cpu_data, i) = data; /* Minimum transition latency is 12 platform clocks */ u64temp = 12ULL * NSEC_PER_SEC; - do_div(u64temp, fsl_get_sys_freq()); + do_div(u64temp, get_bus_freq()); policy->cpuinfo.transition_latency = u64temp + 1; - policy->cur = corenet_cpufreq_get_speed(policy->cpu); + policy->cur = qoriq_cpufreq_get_speed(policy->cpu); cpufreq_frequency_table_get_attr(table, cpu); of_node_put(np); @@ -241,23 +303,19 @@ err_np: return -ENODEV; } -static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy) +static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy) { struct cpu_data *data = per_cpu(cpu_data, policy->cpu); - unsigned int cpu; cpufreq_frequency_table_put_attr(policy->cpu); of_node_put(data->parent); kfree(data->table); kfree(data); - for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu)) - per_cpu(cpu_data, cpu) = NULL; - return 0; } -static int corenet_cpufreq_verify(struct cpufreq_policy *policy) +static int qoriq_cpufreq_verify(struct cpufreq_policy *policy) { struct cpufreq_frequency_table *table = per_cpu(cpu_data, policy->cpu)->table; @@ -265,7 +323,7 @@ static int corenet_cpufreq_verify(struct cpufreq_policy *policy) return cpufreq_frequency_table_verify(policy, table); } -static int corenet_cpufreq_target(struct cpufreq_policy *policy, +static int qoriq_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { struct cpufreq_freqs freqs; @@ -297,20 +355,20 @@ static int corenet_cpufreq_target(struct cpufreq_policy *policy, return ret; } -static struct freq_attr *corenet_cpufreq_attr[] = { +static struct freq_attr *qoriq_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL, }; -static struct cpufreq_driver ppc_corenet_cpufreq_driver = { - .name = "ppc_cpufreq", +static struct cpufreq_driver qoriq_cpufreq_driver = { + .name = "qoriq_cpufreq", .flags = CPUFREQ_CONST_LOOPS, - .init = corenet_cpufreq_cpu_init, - .exit = __exit_p(corenet_cpufreq_cpu_exit), - .verify = corenet_cpufreq_verify, - .target = corenet_cpufreq_target, - .get = corenet_cpufreq_get_speed, - .attr = corenet_cpufreq_attr, + .init = qoriq_cpufreq_cpu_init, + .exit = __exit_p(qoriq_cpufreq_cpu_exit), + .verify = qoriq_cpufreq_verify, + .target = qoriq_cpufreq_target, + .get = qoriq_cpufreq_get_speed, + .attr = qoriq_cpufreq_attr, }; static const struct of_device_id node_matches[] __initdata = { @@ -320,63 +378,46 @@ static const struct of_device_id node_matches[] __initdata = { { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], }, { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], }, { .compatible = "fsl,qoriq-clockgen-2.0", }, + { .compatible = "fsl,ls1021a-clockgen", }, {} }; -static int __init ppc_corenet_cpufreq_init(void) +static int __init qoriq_cpufreq_init(void) { int ret; struct device_node *np; const struct of_device_id *match; const struct soc_data *data; - unsigned int cpu; np = of_find_matching_node(NULL, node_matches); if (!np) return -ENODEV; - for_each_possible_cpu(cpu) { - if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL)) - goto err_mask; - cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu)); - } - match = of_match_node(node_matches, np); data = match->data; if (data) { if (data->flag) fmask = data->freq_mask; - min_cpufreq = fsl_get_sys_freq(); + min_cpufreq = get_bus_freq(); } else { - min_cpufreq = fsl_get_sys_freq() / 2; + min_cpufreq = get_bus_freq() / 2; } of_node_put(np); - ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver); + ret = cpufreq_register_driver(&qoriq_cpufreq_driver); if (!ret) - pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n"); + pr_info("Freescale PowerPC qoriq CPU frequency scaling driver\n"); return ret; - -err_mask: - for_each_possible_cpu(cpu) - free_cpumask_var(per_cpu(cpu_mask, cpu)); - - return -ENOMEM; } -module_init(ppc_corenet_cpufreq_init); +module_init(qoriq_cpufreq_init); -static void __exit ppc_corenet_cpufreq_exit(void) +static void __exit qoriq_cpufreq_exit(void) { - unsigned int cpu; - - for_each_possible_cpu(cpu) - free_cpumask_var(per_cpu(cpu_mask, cpu)); - - cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver); + cpufreq_unregister_driver(&qoriq_cpufreq_driver); } -module_exit(ppc_corenet_cpufreq_exit); +module_exit(qoriq_cpufreq_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>"); diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 2a4eb70..c9ca243 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -1474,7 +1474,7 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *ablkcipher, ctx->sh_desc_dec_dma = dma_map_single(jrdev, desc, desc_bytes(desc), DMA_TO_DEVICE); - if (dma_mapping_error(jrdev, ctx->sh_desc_enc_dma)) { + if (dma_mapping_error(jrdev, ctx->sh_desc_dec_dma)) { dev_err(jrdev, "unable to map shared descriptor\n"); return -ENOMEM; } @@ -2206,6 +2206,10 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, * All other - expected input sequence: AAD, IV, text */ iv_dma = dma_map_single(jrdev, req->iv, ivsize, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, iv_dma)) { + dev_err(jrdev, "unable to map IV\n"); + return ERR_PTR(-ENOMEM); + } if (is_gcm) all_contig = (!assoc_nents && iv_dma + ivsize == sg_dma_address(req->assoc) && @@ -2243,8 +2247,6 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) + desc_bytes; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); *all_contig_ptr = all_contig; sec4_sg_index = 0; @@ -2279,6 +2281,12 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, sg_to_sec4_sg_last(req->dst, dst_nents, edesc->sec4_sg + sec4_sg_index, 0); } + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return ERR_PTR(-ENOMEM); + } return edesc; } @@ -2404,8 +2412,13 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request DMA_FROM_DEVICE, dst_chained); } - /* Check if data are contiguous */ iv_dma = dma_map_single(jrdev, greq->giv, ivsize, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, iv_dma)) { + dev_err(jrdev, "unable to map IV\n"); + return ERR_PTR(-ENOMEM); + } + + /* Check if data are contiguous */ if (assoc_nents || sg_dma_address(req->assoc) + req->assoclen != iv_dma || src_nents || iv_dma + ivsize != sg_dma_address(req->src)) contig &= ~GIV_SRC_CONTIG; @@ -2444,8 +2457,6 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) + desc_bytes; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); *contig_ptr = contig; sec4_sg_index = 0; @@ -2469,6 +2480,12 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request sg_to_sec4_sg_last(req->dst, dst_nents, edesc->sec4_sg + sec4_sg_index, 0); } + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return ERR_PTR(-ENOMEM); + } return edesc; } @@ -2658,11 +2675,16 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request DMA_FROM_DEVICE, dst_chained); } + iv_dma = dma_map_single(jrdev, req->info, ivsize, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, iv_dma)) { + dev_err(jrdev, "unable to map IV\n"); + return ERR_PTR(-ENOMEM); + } + /* * Check if iv can be contiguous with source and destination. * If so, include it. If not, create scatterlist. */ - iv_dma = dma_map_single(jrdev, req->info, ivsize, DMA_TO_DEVICE); if (!src_nents && iv_dma + ivsize == sg_dma_address(req->src)) iv_contig = true; else @@ -2701,6 +2723,11 @@ static struct ablkcipher_edesc *ablkcipher_edesc_alloc(struct ablkcipher_request edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return ERR_PTR(-ENOMEM); + } + edesc->iv_dma = iv_dma; #ifdef DEBUG diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 5592fec..7728119 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -137,13 +137,20 @@ struct caam_hash_state { /* Common job descriptor seq in/out ptr routines */ /* Map state->caam_ctx, and append seq_out_ptr command that points to it */ -static inline void map_seq_out_ptr_ctx(u32 *desc, struct device *jrdev, - struct caam_hash_state *state, - int ctx_len) +static inline int map_seq_out_ptr_ctx(u32 *desc, struct device *jrdev, + struct caam_hash_state *state, + int ctx_len) { state->ctx_dma = dma_map_single(jrdev, state->caam_ctx, ctx_len, DMA_FROM_DEVICE); + if (dma_mapping_error(jrdev, state->ctx_dma)) { + dev_err(jrdev, "unable to map ctx\n"); + return -ENOMEM; + } + append_seq_out_ptr(desc, state->ctx_dma, ctx_len, 0); + + return 0; } /* Map req->result, and append seq_out_ptr command that points to it */ @@ -201,14 +208,19 @@ try_buf_map_to_sec4_sg(struct device *jrdev, struct sec4_sg_entry *sec4_sg, } /* Map state->caam_ctx, and add it to link table */ -static inline void ctx_map_to_sec4_sg(u32 *desc, struct device *jrdev, - struct caam_hash_state *state, - int ctx_len, - struct sec4_sg_entry *sec4_sg, - u32 flag) +static inline int ctx_map_to_sec4_sg(u32 *desc, struct device *jrdev, + struct caam_hash_state *state, int ctx_len, + struct sec4_sg_entry *sec4_sg, u32 flag) { state->ctx_dma = dma_map_single(jrdev, state->caam_ctx, ctx_len, flag); + if (dma_mapping_error(jrdev, state->ctx_dma)) { + dev_err(jrdev, "unable to map ctx\n"); + return -ENOMEM; + } + dma_to_sec4_sg_one(sec4_sg, state->ctx_dma, ctx_len, 0); + + return 0; } /* Common shared descriptor commands */ @@ -487,11 +499,11 @@ static int hash_digest_key(struct caam_hash_ctx *ctx, const u8 *key_in, digestsize, 1); #endif } - *keylen = digestsize; - dma_unmap_single(jrdev, src_dma, *keylen, DMA_TO_DEVICE); dma_unmap_single(jrdev, dst_dma, digestsize, DMA_FROM_DEVICE); + *keylen = digestsize; + kfree(desc); return ret; @@ -713,7 +725,7 @@ static void ahash_done_ctx_src(struct device *jrdev, u32 *desc, u32 err, dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err)); } - ahash_unmap_ctx(jrdev, edesc, req, digestsize, DMA_FROM_DEVICE); + ahash_unmap_ctx(jrdev, edesc, req, digestsize, DMA_TO_DEVICE); kfree(edesc); #ifdef DEBUG @@ -751,7 +763,7 @@ static void ahash_done_ctx_dst(struct device *jrdev, u32 *desc, u32 err, dev_err(jrdev, "%08x: %s\n", err, caam_jr_strstatus(tmp, err)); } - ahash_unmap_ctx(jrdev, edesc, req, ctx->ctx_len, DMA_TO_DEVICE); + ahash_unmap_ctx(jrdev, edesc, req, ctx->ctx_len, DMA_FROM_DEVICE); kfree(edesc); #ifdef DEBUG @@ -818,12 +830,11 @@ static int ahash_update_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, - DMA_TO_DEVICE); - ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, - edesc->sec4_sg, DMA_BIDIRECTIONAL); + ret = ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, + edesc->sec4_sg, DMA_BIDIRECTIONAL); + if (ret) + return ret; state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, @@ -849,6 +860,14 @@ static int ahash_update_ctx(struct ahash_request *req) init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } + append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + to_hash, LDST_SGF); @@ -921,23 +940,34 @@ static int ahash_final_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); edesc->src_nents = 0; - ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, edesc->sec4_sg, - DMA_TO_DEVICE); + ret = ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, + edesc->sec4_sg, DMA_TO_DEVICE); + if (ret) + return ret; state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, buf, state->buf_dma, buflen, last_buflen); (edesc->sec4_sg + sec4_sg_bytes - 1)->len |= SEC4_SG_LEN_FIN; + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } + append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + buflen, LDST_SGF); edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -999,11 +1029,11 @@ static int ahash_finup_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); - ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, edesc->sec4_sg, - DMA_TO_DEVICE); + ret = ctx_map_to_sec4_sg(desc, jrdev, state, ctx->ctx_len, + edesc->sec4_sg, DMA_TO_DEVICE); + if (ret) + return ret; state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, buf, state->buf_dma, buflen, @@ -1012,11 +1042,22 @@ static int ahash_finup_ctx(struct ahash_request *req) src_map_to_sec4_sg(jrdev, req->src, src_nents, edesc->sec4_sg + sec4_sg_src_index, chained); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } + append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + buflen + req->nbytes, LDST_SGF); edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1066,8 +1107,7 @@ static int ahash_digest(struct ahash_request *req) } edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + 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; @@ -1077,6 +1117,12 @@ static int ahash_digest(struct ahash_request *req) if (src_nents) { sg_to_sec4_sg_last(req->src, src_nents, edesc->sec4_sg, 0); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } src_dma = edesc->sec4_sg_dma; options = LDST_SGF; } else { @@ -1087,6 +1133,10 @@ static int ahash_digest(struct ahash_request *req) edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1135,11 +1185,19 @@ static int ahash_final_no_ctx(struct ahash_request *req) init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE); state->buf_dma = dma_map_single(jrdev, buf, buflen, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, state->buf_dma)) { + dev_err(jrdev, "unable to map src\n"); + return -ENOMEM; + } append_seq_in_ptr(desc, state->buf_dma, buflen, 0); edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } edesc->src_nents = 0; #ifdef DEBUG @@ -1207,9 +1265,7 @@ static int ahash_update_no_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, - DMA_TO_DEVICE); + edesc->dst_dma = 0; state->buf_dma = buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, buf, *buflen); @@ -1226,9 +1282,19 @@ static int ahash_update_no_ctx(struct ahash_request *req) init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } + append_seq_in_ptr(desc, edesc->sec4_sg_dma, to_hash, LDST_SGF); - map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + ret = map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + if (ret) + return ret; #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1307,8 +1373,6 @@ static int ahash_finup_no_ctx(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, DMA_TO_DEVICE); state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg, buf, state->buf_dma, buflen, @@ -1317,11 +1381,22 @@ static int ahash_finup_no_ctx(struct ahash_request *req) src_map_to_sec4_sg(jrdev, req->src, src_nents, edesc->sec4_sg + 1, chained); + edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, + sec4_sg_bytes, DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } + append_seq_in_ptr(desc, edesc->sec4_sg_dma, buflen + req->nbytes, LDST_SGF); edesc->dst_dma = map_seq_out_ptr_result(desc, jrdev, req->result, digestsize); + if (dma_mapping_error(jrdev, edesc->dst_dma)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1390,13 +1465,19 @@ static int ahash_update_first(struct ahash_request *req) edesc->sec4_sg_bytes = sec4_sg_bytes; edesc->sec4_sg = (void *)edesc + sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN; - edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, - sec4_sg_bytes, - DMA_TO_DEVICE); + edesc->dst_dma = 0; if (src_nents) { sg_to_sec4_sg_last(req->src, src_nents, edesc->sec4_sg, 0); + edesc->sec4_sg_dma = dma_map_single(jrdev, + edesc->sec4_sg, + sec4_sg_bytes, + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) { + dev_err(jrdev, "unable to map S/G table\n"); + return -ENOMEM; + } src_dma = edesc->sec4_sg_dma; options = LDST_SGF; } else { @@ -1414,7 +1495,9 @@ static int ahash_update_first(struct ahash_request *req) append_seq_in_ptr(desc, src_dma, to_hash, options); - map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + ret = map_seq_out_ptr_ctx(desc, jrdev, state, ctx->ctx_len); + if (ret) + return ret; #ifdef DEBUG print_hex_dump(KERN_ERR, "jobdesc@"__stringify(__LINE__)": ", @@ -1463,6 +1546,7 @@ static int ahash_init(struct ahash_request *req) state->final = ahash_final_no_ctx; state->current_buf = 0; + state->buf_dma = 0; return 0; } diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c index 466dfe5..fcafd65 100644 --- a/drivers/crypto/caam/caamrng.c +++ b/drivers/crypto/caam/caamrng.c @@ -188,7 +188,7 @@ static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait) max - copied_idx, false); } -static inline void rng_create_sh_desc(struct caam_rng_ctx *ctx) +static inline int rng_create_sh_desc(struct caam_rng_ctx *ctx) { struct device *jrdev = ctx->jrdev; u32 *desc = ctx->sh_desc; @@ -206,13 +206,18 @@ static inline void rng_create_sh_desc(struct caam_rng_ctx *ctx) ctx->sh_desc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, ctx->sh_desc_dma)) { + dev_err(jrdev, "unable to map shared descriptor\n"); + return -ENOMEM; + } #ifdef DEBUG print_hex_dump(KERN_ERR, "rng shdesc@: ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + return 0; } -static inline void rng_create_job_desc(struct caam_rng_ctx *ctx, int buf_id) +static inline int rng_create_job_desc(struct caam_rng_ctx *ctx, int buf_id) { struct device *jrdev = ctx->jrdev; struct buf_data *bd = &ctx->bufs[buf_id]; @@ -223,12 +228,17 @@ static inline void rng_create_job_desc(struct caam_rng_ctx *ctx, int buf_id) HDR_REVERSE); bd->addr = dma_map_single(jrdev, bd->buf, RN_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(jrdev, bd->addr)) { + dev_err(jrdev, "unable to map dst\n"); + return -ENOMEM; + } append_seq_out_ptr_intlen(desc, bd->addr, RN_BUF_SIZE, 0); #ifdef DEBUG print_hex_dump(KERN_ERR, "rng job desc@: ", DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), 1); #endif + return 0; } static void caam_cleanup(struct hwrng *rng) @@ -245,24 +255,44 @@ static void caam_cleanup(struct hwrng *rng) rng_unmap_ctx(rng_ctx); } -static void caam_init_buf(struct caam_rng_ctx *ctx, int buf_id) +static int caam_init_buf(struct caam_rng_ctx *ctx, int buf_id) { struct buf_data *bd = &ctx->bufs[buf_id]; + int err; + + err = rng_create_job_desc(ctx, buf_id); + if (err) + return err; - rng_create_job_desc(ctx, buf_id); atomic_set(&bd->empty, BUF_EMPTY); submit_job(ctx, buf_id == ctx->current_buf); wait_for_completion(&bd->filled); + + return 0; } -static void caam_init_rng(struct caam_rng_ctx *ctx, struct device *jrdev) +static int caam_init_rng(struct caam_rng_ctx *ctx, struct device *jrdev) { + int err; + ctx->jrdev = jrdev; - rng_create_sh_desc(ctx); + + err = rng_create_sh_desc(ctx); + if (err) + return err; + ctx->current_buf = 0; ctx->cur_buf_idx = 0; - caam_init_buf(ctx, 0); - caam_init_buf(ctx, 1); + + err = caam_init_buf(ctx, 0); + if (err) + return err; + + err = caam_init_buf(ctx, 1); + if (err) + return err; + + return 0; } static struct hwrng caam_rng = { @@ -285,6 +315,7 @@ static int __init caam_rng_init(void) struct platform_device *pdev; struct device *ctrldev; void *priv; + int err; dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0"); if (!dev_node) { @@ -316,7 +347,9 @@ static int __init caam_rng_init(void) rng_ctx = kmalloc(sizeof(struct caam_rng_ctx), GFP_DMA); if (!rng_ctx) return -ENOMEM; - caam_init_rng(rng_ctx, dev); + err = caam_init_rng(rng_ctx, dev); + if (err) + return err; dev_info(dev, "registering rng-caam\n"); return hwrng_register(&caam_rng); diff --git a/drivers/crypto/caam/compat.h b/drivers/crypto/caam/compat.h index f227922..8c79717 100644 --- a/drivers/crypto/caam/compat.h +++ b/drivers/crypto/caam/compat.h @@ -14,6 +14,8 @@ #include <linux/hash.h> #include <linux/hw_random.h> #include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/spinlock.h> diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 17805eb..17af050 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -81,27 +81,37 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, u32 *status) { struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev); - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl = ctrlpriv->ctrl; + struct caam_deco __iomem *deco = ctrlpriv->deco; unsigned int timeout = 100000; u32 deco_dbg_reg, flags; int i; - /* Set the bit to request direct access to DECO0 */ - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - setbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); - while (!(rd_reg32(&topregs->ctrl.deco_rq) & DECORR_DEN0) && + if (ctrlpriv->virt_en == 1) { + setbits32(&ctrl->deco_rsr, DECORSR_JR0); + + while (!(rd_reg32(&ctrl->deco_rsr) & DECORSR_VALID) && + --timeout) + cpu_relax(); + + timeout = 100000; + } + + setbits32(&ctrl->deco_rq, DECORR_RQD0ENABLE); + + while (!(rd_reg32(&ctrl->deco_rq) & DECORR_DEN0) && --timeout) cpu_relax(); if (!timeout) { dev_err(ctrldev, "failed to acquire DECO 0\n"); - clrbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); + clrbits32(&ctrl->deco_rq, DECORR_RQD0ENABLE); return -ENODEV; } for (i = 0; i < desc_len(desc); i++) - wr_reg32(&topregs->deco.descbuf[i], *(desc + i)); + wr_reg32(&deco->descbuf[i], *(desc + i)); flags = DECO_JQCR_WHL; /* @@ -112,11 +122,11 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, flags |= DECO_JQCR_FOUR; /* Instruct the DECO to execute it */ - wr_reg32(&topregs->deco.jr_ctl_hi, flags); + wr_reg32(&deco->jr_ctl_hi, flags); timeout = 10000000; do { - deco_dbg_reg = rd_reg32(&topregs->deco.desc_dbg); + deco_dbg_reg = rd_reg32(&deco->desc_dbg); /* * If an error occured in the descriptor, then * the DECO status field will be set to 0x0D @@ -127,11 +137,14 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, cpu_relax(); } while ((deco_dbg_reg & DESC_DBG_DECO_STAT_VALID) && --timeout); - *status = rd_reg32(&topregs->deco.op_status_hi) & + *status = rd_reg32(&deco->op_status_hi) & DECO_OP_STATUS_HI_ERR_MASK; + if (ctrlpriv->virt_en == 1) + clrbits32(&ctrl->deco_rsr, DECORSR_JR0); + /* Mark the DECO as free */ - clrbits32(&topregs->ctrl.deco_rq, DECORR_RQD0ENABLE); + clrbits32(&ctrl->deco_rq, DECORR_RQD0ENABLE); if (!timeout) return -EAGAIN; @@ -162,13 +175,13 @@ static int instantiate_rng(struct device *ctrldev, int state_handle_mask, int gen_sk) { struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev); - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl; struct rng4tst __iomem *r4tst; u32 *desc, status, rdsta_val; int ret = 0, sh_idx; - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - r4tst = &topregs->ctrl.r4tst[0]; + ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; + r4tst = &ctrl->r4tst[0]; desc = kmalloc(CAAM_CMD_SZ * 7, GFP_KERNEL); if (!desc) @@ -198,12 +211,11 @@ static int instantiate_rng(struct device *ctrldev, int state_handle_mask, * CAAM eras), then try again. */ rdsta_val = - rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & RDSTA_IFMASK; + rd_reg32(&ctrl->r4tst[0].rdsta) & RDSTA_IFMASK; if (status || !(rdsta_val & (1 << sh_idx))) ret = -EAGAIN; if (ret) break; - dev_info(ctrldev, "Instantiated RNG4 SH%d\n", sh_idx); /* Clear the contents before recreating the descriptor */ memset(desc, 0x00, CAAM_CMD_SZ * 7); @@ -271,12 +283,12 @@ static int caam_remove(struct platform_device *pdev) { struct device *ctrldev; struct caam_drv_private *ctrlpriv; - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl; int ring, ret = 0; ctrldev = &pdev->dev; ctrlpriv = dev_get_drvdata(ctrldev); - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; + ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; /* Remove platform devices for JobRs */ for (ring = 0; ring < ctrlpriv->total_jobrs; ring++) { @@ -298,7 +310,7 @@ static int caam_remove(struct platform_device *pdev) #endif /* Unmap controller region */ - iounmap(&topregs->ctrl); + iounmap(&ctrl); kfree(ctrlpriv->jrpdev); kfree(ctrlpriv); @@ -315,12 +327,12 @@ static int caam_remove(struct platform_device *pdev) static void kick_trng(struct device *dev, int ent_delay) { struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); - struct caam_full __iomem *topregs; + struct caam_ctrl __iomem *ctrl; struct rng4tst __iomem *r4tst; u32 val; - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - r4tst = &topregs->ctrl.r4tst[0]; + ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; + r4tst = &ctrl->r4tst[0]; /* put RNG4 into program mode */ setbits32(&r4tst->rtmctl, RTMCTL_PRGM); @@ -375,19 +387,19 @@ static int caam_rng_init(struct device *dev) { int gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN; struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); - u64 cha_vid; - struct caam_full __iomem *topregs; + u64 cha_vid_ls; + struct caam_ctrl __iomem *ctrl; int ret = 0; - topregs = (struct caam_full __iomem *)ctrlpriv->ctrl; - cha_vid = rd_reg64(&topregs->ctrl.perfmon.cha_id); + ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl; + cha_vid_ls = rd_reg32(&ctrl->perfmon.cha_id_ls); /* * If SEC has RNG version >= 4 and RNG state handle has not been * already instantiated, do RNG instantiation */ - if ((cha_vid & CHA_ID_RNG_MASK) >> CHA_ID_RNG_SHIFT >= 4) { + if ((cha_vid_ls & CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT >= 4) { ctrlpriv->rng4_sh_init = - rd_reg32(&topregs->ctrl.r4tst[0].rdsta); + rd_reg32(&ctrl->r4tst[0].rdsta); /* * If the secure keys (TDKEK, JDKEK, TDSK), were already * generated, signal this to the function that is instantiating @@ -398,7 +410,7 @@ static int caam_rng_init(struct device *dev) ctrlpriv->rng4_sh_init &= RDSTA_IFMASK; do { int inst_handles = - rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & + rd_reg32(&ctrl->r4tst[0].rdsta) & RDSTA_IFMASK; /* * If either SH were instantiated by somebody else @@ -433,7 +445,7 @@ static int caam_rng_init(struct device *dev) ctrlpriv->rng4_sh_init = ~ctrlpriv->rng4_sh_init & RDSTA_IFMASK; /* Enable RDB bit so that RNG works faster */ - setbits32(&topregs->ctrl.scfgr, SCFGR_RDBENABLE); + setbits32(&ctrl->scfgr, SCFGR_RDBENABLE); } return 0; } @@ -446,11 +458,13 @@ static int caam_probe(struct platform_device *pdev) struct device *dev; struct device_node *nprop, *np; struct caam_ctrl __iomem *ctrl; - struct caam_full __iomem *topregs; struct caam_drv_private *ctrlpriv; #ifdef CONFIG_DEBUG_FS struct caam_perfmon *perfmon; #endif + u32 scfgr, comp_params; + int pg_size; + int BLOCK_OFFSET = 0; ctrlpriv = kzalloc(sizeof(struct caam_drv_private), GFP_KERNEL); if (!ctrlpriv) @@ -468,10 +482,27 @@ static int caam_probe(struct platform_device *pdev) dev_err(dev, "caam: of_iomap() failed\n"); return -ENOMEM; } - ctrlpriv->ctrl = (struct caam_ctrl __force *)ctrl; + /* Finding the page size for using the CTPR_MS register */ + comp_params = rd_reg32(&ctrl->perfmon.comp_parms_ms); + pg_size = (comp_params & CTPR_MS_PG_SZ_MASK) >> CTPR_MS_PG_SZ_SHIFT; - /* topregs used to derive pointers to CAAM sub-blocks only */ - topregs = (struct caam_full __iomem *)ctrl; + /* Allocating the BLOCK_OFFSET based on the supported page size on + * the platform + */ + if (pg_size == 0) + BLOCK_OFFSET = PG_SIZE_4K; + else + BLOCK_OFFSET = PG_SIZE_64K; + + ctrlpriv->ctrl = (struct caam_ctrl __force *)ctrl; + ctrlpriv->assure = (struct caam_assurance __force *) + ((uint8_t *)ctrl + + BLOCK_OFFSET * ASSURE_BLOCK_NUMBER + ); + ctrlpriv->deco = (struct caam_deco __force *) + ((uint8_t *)ctrl + + BLOCK_OFFSET * DECO_BLOCK_NUMBER + ); /* Get the IRQ of the controller (for security violations only) */ ctrlpriv->secvio_irq = of_irq_to_resource(nprop, 0, NULL); @@ -480,9 +511,35 @@ static int caam_probe(struct platform_device *pdev) * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel, * long pointers in master configuration register */ - setbits32(&topregs->ctrl.mcr, MCFGR_WDENABLE | + setbits32(&ctrl->mcr, MCFGR_WDENABLE | (sizeof(dma_addr_t) == sizeof(u64) ? MCFGR_LONG_PTR : 0)); + /* + * Read the Compile Time paramters and SCFGR to determine + * if Virtualization is enabled for this platform + */ + scfgr = rd_reg32(&ctrl->scfgr); + + ctrlpriv->virt_en = 0; + if (comp_params & CTPR_MS_VIRT_EN_INCL) { + /* VIRT_EN_INCL = 1 & VIRT_EN_POR = 1 or + * VIRT_EN_INCL = 1 & VIRT_EN_POR = 0 & SCFGR_VIRT_EN = 1 + */ + if ((comp_params & CTPR_MS_VIRT_EN_POR) || + (!(comp_params & CTPR_MS_VIRT_EN_POR) && + (scfgr & SCFGR_VIRT_EN))) + ctrlpriv->virt_en = 1; + } else { + /* VIRT_EN_INCL = 0 && VIRT_EN_POR_VALUE = 1 */ + if (comp_params & CTPR_MS_VIRT_EN_POR) + ctrlpriv->virt_en = 1; + } + + if (ctrlpriv->virt_en == 1) + setbits32(&ctrl->jrstart, JRSTART_JR0_START | + JRSTART_JR1_START | JRSTART_JR2_START | + JRSTART_JR3_START); + 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)); @@ -508,7 +565,7 @@ static int caam_probe(struct platform_device *pdev) ctrlpriv->jrpdev = kzalloc(sizeof(struct platform_device *) * rspec, GFP_KERNEL); if (ctrlpriv->jrpdev == NULL) { - iounmap(&topregs->ctrl); + iounmap(&ctrl); return -ENOMEM; } @@ -521,6 +578,11 @@ static int caam_probe(struct platform_device *pdev) pr_warn("JR%d Platform device creation error\n", ring); continue; } + ctrlpriv->jr[ring] = (struct caam_job_ring __force *) + ((uint8_t *)ctrl + + (ring + JR_BLOCK_NUMBER) * + BLOCK_OFFSET + ); ctrlpriv->total_jobrs++; ring++; } @@ -539,12 +601,16 @@ static int caam_probe(struct platform_device *pdev) } /* Check to see if QI present. If so, enable */ - ctrlpriv->qi_present = !!(rd_reg64(&topregs->ctrl.perfmon.comp_parms) & - CTPR_QI_MASK); + ctrlpriv->qi_present = + !!(rd_reg32(&ctrl->perfmon.comp_parms_ms) & + CTPR_MS_QI_MASK); if (ctrlpriv->qi_present) { - ctrlpriv->qi = (struct caam_queue_if __force *)&topregs->qi; + ctrlpriv->qi = (struct caam_queue_if __force *) + ((uint8_t *)ctrl + + BLOCK_OFFSET * QI_BLOCK_NUMBER + ); /* This is all that's required to physically enable QI */ - wr_reg32(&topregs->qi.qi_control_lo, QICTL_DQEN); + wr_reg32(&ctrlpriv->qi->qi_control_lo, QICTL_DQEN); /* If QMAN driver is present, init CAAM-QI backend */ #ifdef CONFIG_FSL_QMAN @@ -568,8 +634,9 @@ static int caam_probe(struct platform_device *pdev) /* NOTE: RTIC detection ought to go here, around Si time */ - caam_id = rd_reg64(&topregs->ctrl.perfmon.caam_id); ctrlpriv->era = caam_get_era(); + caam_id = (u64)rd_reg32(&ctrl->perfmon.caam_id_ms) << 32 | + (u64)rd_reg32(&ctrl->perfmon.caam_id_ls); /* Report "alive" for developer to see */ dev_info(dev, "device ID = 0x%016llx (Era %d)\n", caam_id, @@ -661,11 +728,11 @@ static int caam_probe(struct platform_device *pdev) } #ifdef CONFIG_PM -static int caam_stop_qi(struct caam_full __iomem *topregs) +static int caam_stop_qi(struct caam_queue_if __iomem *qi) { int qi_stopped, loop = 0; - setbits32(&topregs->qi.qi_control_lo, QICTL_STOP); + setbits32(&qi->qi_control_lo, QICTL_STOP); /* * Wait till QI Job's in Holding tank/deco are completed. @@ -673,10 +740,10 @@ static int caam_stop_qi(struct caam_full __iomem *topregs) * reenabled. */ while (loop <= 100000) { - qi_stopped = rd_reg32(&topregs->qi.qi_status) & + qi_stopped = rd_reg32(&qi->qi_status) & QISTA_STOPD; if (qi_stopped) { - wr_reg32(&topregs->qi.qi_control_lo, + wr_reg32(&qi->qi_control_lo, QICTL_STOP); return 0; } @@ -684,7 +751,7 @@ static int caam_stop_qi(struct caam_full __iomem *topregs) } /* Failed to stop QI interface. Reenable QI Interface */ - wr_reg32(&topregs->qi.qi_control_lo, QICTL_DQEN); + wr_reg32(&qi->qi_control_lo, QICTL_DQEN); return -EBUSY; } @@ -692,22 +759,22 @@ static int caam_stop_qi(struct caam_full __iomem *topregs) static int caam_suspend(struct device *dev) { struct caam_drv_private *caam_priv; - struct caam_full __iomem *topregs; struct caam_ctrl __iomem *ctrl; + struct caam_queue_if __iomem *qi; int ret = 0; caam_priv = dev_get_drvdata(dev); ctrl = caam_priv->ctrl; - topregs = (struct caam_full __iomem *)ctrl; + qi = caam_priv->qi; /* QI Interface graceful stoppping during suspend */ if (caam_priv->qi_present) { int qi_dqen; - qi_dqen = rd_reg32(&topregs->qi.qi_control_lo) & + qi_dqen = rd_reg32(&qi->qi_control_lo) & QICTL_DQEN; if (qi_dqen) - ret = caam_stop_qi(topregs); + ret = caam_stop_qi(qi); } return ret; @@ -717,23 +784,23 @@ static int caam_suspend(struct device *dev) static int caam_resume(struct device *dev) { struct caam_drv_private *caam_priv; - struct caam_full __iomem *topregs; struct caam_ctrl __iomem *ctrl; + struct caam_queue_if __iomem *qi; int ret; caam_priv = dev_get_drvdata(dev); ctrl = caam_priv->ctrl; - topregs = (struct caam_full __iomem *)ctrl; + qi = caam_priv->qi; /* * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel, * long pointers in master configuration register */ - setbits32(&topregs->ctrl.mcr, MCFGR_WDENABLE | + setbits32(&ctrl->mcr, MCFGR_WDENABLE | (sizeof(dma_addr_t) == sizeof(u64) ? MCFGR_LONG_PTR : 0)); /* Enable QI interface of SEC */ if (caam_priv->qi_present) - wr_reg32(&topregs->qi.qi_control_lo, QICTL_DQEN); + wr_reg32(&qi->qi_control_lo, QICTL_DQEN); ret = caam_rng_init(dev); diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h index ed0351d..423886a 100644 --- a/drivers/crypto/caam/intern.h +++ b/drivers/crypto/caam/intern.h @@ -78,10 +78,11 @@ struct caam_drv_private { struct platform_device *pdev; /* Physical-presence section */ - struct caam_ctrl *ctrl; /* controller region */ - struct caam_deco **deco; /* DECO/CCB views */ - struct caam_assurance *ac; - struct caam_queue_if *qi; /* QI control region */ + struct caam_ctrl __iomem *ctrl; /* controller region */ + struct caam_deco __iomem *deco; /* DECO/CCB views */ + struct caam_assurance __iomem *assure; + struct caam_queue_if __iomem *qi; /* QI control region */ + struct caam_job_ring __iomem *jr[4]; /* JobR's register space */ /* * Detected geometry block. Filled in from device tree if powerpc, @@ -94,6 +95,7 @@ struct caam_drv_private { struct list_head pkc_list; int era; /* SEC Era */ + int virt_en; /* Virtualization enabled in CAAM */ #define RNG4_MAX_HANDLES 2 /* RNG4 block */ diff --git a/drivers/crypto/caam/pdb.h b/drivers/crypto/caam/pdb.h index 0680b30..faab55b 100644 --- a/drivers/crypto/caam/pdb.h +++ b/drivers/crypto/caam/pdb.h @@ -66,9 +66,15 @@ struct ipsec_encap_ctr { struct ipsec_encap_ccm { u32 salt; /* lower 24 bits */ +#ifdef CONFIG_CPU_BIG_ENDIAN u8 b0_flags; u8 ctr_flags; u16 ctr_initial; +#else + u16 ctr_initial; + u8 ctr_flags; + u8 b0_flags; +#endif u32 iv[2]; }; @@ -79,10 +85,17 @@ struct ipsec_encap_gcm { }; struct ipsec_encap_pdb { +#ifdef CONFIG_CPU_BIG_ENDIAN u8 hmo_rsvd; u8 ip_nh; u8 ip_nh_offset; u8 options; +#else + u8 options; + u8 ip_nh_offset; + u8 ip_nh; + u8 hmo_rsvd; +#endif u32 seq_num_ext_hi; u32 seq_num; union { @@ -92,8 +105,13 @@ struct ipsec_encap_pdb { struct ipsec_encap_gcm gcm; }; u32 spi; +#ifdef CONFIG_CPU_BIG_ENDIAN u16 rsvd1; u16 ip_hdr_len; +#else + u16 ip_hdr_len; + u16 rsvd1; +#endif u32 ip_hdr[0]; /* optional IP Header content */ }; @@ -108,9 +126,15 @@ struct ipsec_decap_ctr { struct ipsec_decap_ccm { u32 salt; +#ifdef CONFIG_CPU_BIG_ENDIAN u8 iv_flags; u8 ctr_flags; u16 ctr_initial; +#else + u16 ctr_initial; + u8 ctr_flags; + u8 iv_flags; +#endif }; struct ipsec_decap_gcm { @@ -119,9 +143,15 @@ struct ipsec_decap_gcm { }; struct ipsec_decap_pdb { +#ifdef CONFIG_CPU_BIG_ENDIAN u16 hmo_ip_hdr_len; u8 ip_nh_offset; u8 options; +#else + u8 options; + u8 ip_nh_offset; + u16 hmo_ip_hdr_len; +#endif union { struct ipsec_decap_cbc cbc; struct ipsec_decap_ctr ctr; diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index d50174f..79d2bf3 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -74,16 +74,23 @@ #endif #else #ifdef __LITTLE_ENDIAN -#define wr_reg32(reg, data) __raw_writel(reg, data) +#define wr_reg32(reg, data) __raw_writel(data, reg) #define rd_reg32(reg) __raw_readl(reg) #ifdef CONFIG_64BIT -#define wr_reg64(reg, data) __raw_writeq(reg, data) +#define wr_reg64(reg, data) __raw_writeq(data, reg) #define rd_reg64(reg) __raw_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 +#ifdef __BIG_ENDIAN static inline void wr_reg64(u64 __iomem *reg, u64 data) { wr_reg32((u32 __iomem *)reg, (data & 0xffffffff00000000ull) >> 32); @@ -95,6 +102,21 @@ static inline u64 rd_reg64(u64 __iomem *reg) return (((u64)rd_reg32((u32 __iomem *)reg)) << 32) | ((u64)rd_reg32((u32 __iomem *)reg + 1)); } +#else +#ifdef __LITTLE_ENDIAN +static inline void wr_reg64(u64 __iomem *reg, u64 data) +{ + wr_reg32((u32 __iomem *)reg + 1, (data & 0xffffffff00000000ull) >> 32); + wr_reg32((u32 __iomem *)reg, data & 0x00000000ffffffffull); +} + +static inline u64 rd_reg64(u64 __iomem *reg) +{ + return (((u64)rd_reg32((u32 __iomem *)reg + 1)) << 32) | + ((u64)rd_reg32((u32 __iomem *)reg)); +} +#endif +#endif #endif /* @@ -114,45 +136,45 @@ struct jr_outentry { */ /* Number of DECOs */ -#define CHA_NUM_DECONUM_SHIFT 56 -#define CHA_NUM_DECONUM_MASK (0xfull << CHA_NUM_DECONUM_SHIFT) +#define CHA_NUM_MS_DECONUM_SHIFT 24 +#define CHA_NUM_MS_DECONUM_MASK (0xfull << CHA_NUM_MS_DECONUM_SHIFT) /* CHA Version IDs */ -#define CHA_ID_AES_SHIFT 0 -#define CHA_ID_AES_MASK (0xfull << CHA_ID_AES_SHIFT) +#define CHA_ID_LS_AES_SHIFT 0 +#define CHA_ID_LS_AES_MASK (0xfull << CHA_ID_LS_AES_SHIFT) -#define CHA_ID_DES_SHIFT 4 -#define CHA_ID_DES_MASK (0xfull << CHA_ID_DES_SHIFT) +#define CHA_ID_LS_DES_SHIFT 4 +#define CHA_ID_LS_DES_MASK (0xfull << CHA_ID_LS_DES_SHIFT) -#define CHA_ID_ARC4_SHIFT 8 -#define CHA_ID_ARC4_MASK (0xfull << CHA_ID_ARC4_SHIFT) +#define CHA_ID_LS_ARC4_SHIFT 8 +#define CHA_ID_LS_ARC4_MASK (0xfull << CHA_ID_LS_ARC4_SHIFT) -#define CHA_ID_MD_SHIFT 12 -#define CHA_ID_MD_MASK (0xfull << CHA_ID_MD_SHIFT) +#define CHA_ID_LS_MD_SHIFT 12 +#define CHA_ID_LS_MD_MASK (0xfull << CHA_ID_LS_MD_SHIFT) -#define CHA_ID_RNG_SHIFT 16 -#define CHA_ID_RNG_MASK (0xfull << CHA_ID_RNG_SHIFT) +#define CHA_ID_LS_RNG_SHIFT 16 +#define CHA_ID_LS_RNG_MASK (0xfull << CHA_ID_LS_RNG_SHIFT) -#define CHA_ID_SNW8_SHIFT 20 -#define CHA_ID_SNW8_MASK (0xfull << CHA_ID_SNW8_SHIFT) +#define CHA_ID_LS_SNW8_SHIFT 20 +#define CHA_ID_LS_SNW8_MASK (0xfull << CHA_ID_LS_SNW8_SHIFT) -#define CHA_ID_KAS_SHIFT 24 -#define CHA_ID_KAS_MASK (0xfull << CHA_ID_KAS_SHIFT) +#define CHA_ID_LS_KAS_SHIFT 24 +#define CHA_ID_LS_KAS_MASK (0xfull << CHA_ID_LS_KAS_SHIFT) -#define CHA_ID_PK_SHIFT 28 -#define CHA_ID_PK_MASK (0xfull << CHA_ID_PK_SHIFT) +#define CHA_ID_LS_PK_SHIFT 28 +#define CHA_ID_LS_PK_MASK (0xfull << CHA_ID_LS_PK_SHIFT) -#define CHA_ID_CRC_SHIFT 32 -#define CHA_ID_CRC_MASK (0xfull << CHA_ID_CRC_SHIFT) +#define CHA_ID_MS_CRC_SHIFT 0 +#define CHA_ID_MS_CRC_MASK (0xfull << CHA_ID_MS_CRC_SHIFT) -#define CHA_ID_SNW9_SHIFT 36 -#define CHA_ID_SNW9_MASK (0xfull << CHA_ID_SNW9_SHIFT) +#define CHA_ID_MS_SNW9_SHIFT 4 +#define CHA_ID_MS_SNW9_MASK (0xfull << CHA_ID_MS_SNW9_SHIFT) -#define CHA_ID_DECO_SHIFT 56 -#define CHA_ID_DECO_MASK (0xfull << CHA_ID_DECO_SHIFT) +#define CHA_ID_MS_DECO_SHIFT 24 +#define CHA_ID_MS_DECO_MASK (0xfull << CHA_ID_MS_DECO_SHIFT) -#define CHA_ID_JR_SHIFT 60 -#define CHA_ID_JR_MASK (0xfull << CHA_ID_JR_SHIFT) +#define CHA_ID_MS_JR_SHIFT 28 +#define CHA_ID_MS_JR_MASK (0xfull << CHA_ID_MS_JR_SHIFT) struct sec_vid { u16 ip_id; @@ -172,10 +194,16 @@ struct caam_perfmon { u64 rsvd[13]; /* CAAM Hardware Instantiation Parameters fa0-fbf */ - u64 cha_rev; /* CRNR - CHA Revision Number */ -#define CTPR_QI_SHIFT 57 -#define CTPR_QI_MASK (0x1ull << CTPR_QI_SHIFT) - u64 comp_parms; /* CTPR - Compile Parameters Register */ + u32 cha_rev_ms; /* CRNR - CHA Rev No. Most significant half*/ + u32 cha_rev_ls; /* CRNR - CHA Rev No. Least significant half*/ +#define CTPR_MS_QI_SHIFT 25 +#define CTPR_MS_QI_MASK (0x1ull << CTPR_MS_QI_SHIFT) +#define CTPR_MS_VIRT_EN_INCL 0x00000001 +#define CTPR_MS_VIRT_EN_POR 0x00000002 +#define CTPR_MS_PG_SZ_MASK 0x10 +#define CTPR_MS_PG_SZ_SHIFT 4 + u32 comp_parms_ms; /* CTPR - Compile Parameters Register */ + u32 comp_parms_ls; /* CTPR - Compile Parameters Register */ u64 rsvd1[2]; /* CAAM Global Status fc0-fdf */ @@ -189,9 +217,12 @@ struct caam_perfmon { /* Component Instantiation Parameters fe0-fff */ u32 rtic_id; /* RVID - RTIC Version ID */ u32 ccb_id; /* CCBVID - CCB Version ID */ - u64 cha_id; /* CHAVID - CHA Version ID */ - u64 cha_num; /* CHANUM - CHA Number */ - u64 caam_id; /* CAAMVID - CAAM Version ID */ + u32 cha_id_ms; /* CHAVID - CHA Version ID Most Significant*/ + u32 cha_id_ls; /* CHAVID - CHA Version ID Least Significant*/ + u32 cha_num_ms; /* CHANUM - CHA Number Most Significant */ + u32 cha_num_ls; /* CHANUM - CHA Number Least Significant*/ + u32 caam_id_ms; /* CAAMVID - CAAM Version ID MS */ + u32 caam_id_ls; /* CAAMVID - CAAM Version ID LS */ }; /* LIODN programming for DMA configuration */ @@ -304,9 +335,12 @@ struct caam_ctrl { /* Bus Access Configuration Section 010-11f */ /* Read/Writable */ struct masterid jr_mid[4]; /* JRxLIODNR - JobR LIODN setup */ - u32 rsvd3[12]; + u32 rsvd3[11]; + u32 jrstart; /* JRSTART - Job Ring Start Register */ struct masterid rtic_mid[4]; /* RTICxLIODNR - RTIC LIODN setup */ - u32 rsvd4[7]; + u32 rsvd4[5]; + u32 deco_rsr; /* DECORSR - Deco Request Source */ + u32 rsvd11; u32 deco_rq; /* DECORR - DECO Request */ struct partid deco_mid[5]; /* DECOxLIODNR - 1 per DECO */ u32 rsvd5[22]; @@ -347,7 +381,10 @@ struct caam_ctrl { #define MCFGR_DMA_RESET 0x10000000 #define MCFGR_LONG_PTR 0x00010000 /* Use >32-bit desc addressing */ #define SCFGR_RDBENABLE 0x00000400 +#define SCFGR_VIRT_EN 0x00008000 #define DECORR_RQD0ENABLE 0x00000001 /* Enable DECO0 for direct access */ +#define DECORSR_JR0 0x00000001 /* JR to supply TZ, SDID, ICID */ +#define DECORSR_VALID 0x80000000 #define DECORR_DEN0 0x00010000 /* DECO0 available for access*/ /* AXI read cache control */ @@ -365,6 +402,12 @@ struct caam_ctrl { #define MCFGR_AXIPRI 0x00000008 /* Assert AXI priority sideband */ #define MCFGR_BURST_64 0x00000001 /* Max burst size */ +/* JRSTART register offsets */ +#define JRSTART_JR0_START 0x00000001 /* Start Job ring 0 */ +#define JRSTART_JR1_START 0x00000002 /* Start Job ring 1 */ +#define JRSTART_JR2_START 0x00000004 /* Start Job ring 2 */ +#define JRSTART_JR3_START 0x00000008 /* Start Job ring 3 */ + /* * caam_job_ring - direct job ring setup * 1-4 possible per instantiation, base + 1000/2000/3000/4000 @@ -723,34 +766,11 @@ struct caam_deco { #define DECO_JQCR_WHL 0x20000000 #define DECO_JQCR_FOUR 0x10000000 -/* - * Current top-level view of memory map is: - * - * 0x0000 - 0x0fff - CAAM Top-Level Control - * 0x1000 - 0x1fff - Job Ring 0 - * 0x2000 - 0x2fff - Job Ring 1 - * 0x3000 - 0x3fff - Job Ring 2 - * 0x4000 - 0x4fff - Job Ring 3 - * 0x5000 - 0x5fff - (unused) - * 0x6000 - 0x6fff - Assurance Controller - * 0x7000 - 0x7fff - Queue Interface - * 0x8000 - 0x8fff - DECO-CCB 0 - * 0x9000 - 0x9fff - DECO-CCB 1 - * 0xa000 - 0xafff - DECO-CCB 2 - * 0xb000 - 0xbfff - DECO-CCB 3 - * 0xc000 - 0xcfff - DECO-CCB 4 - * - * caam_full describes the full register view of CAAM if useful, - * although many configurations may choose to implement parts of - * the register map separately, in differing privilege regions - */ -struct caam_full { - struct caam_ctrl __iomem ctrl; - struct caam_job_ring jr[4]; - u64 rsvd[512]; - struct caam_assurance assure; - struct caam_queue_if qi; - struct caam_deco deco; -}; - -#endif /* REGS_H */ +#define JR_BLOCK_NUMBER 1 +#define ASSURE_BLOCK_NUMBER 6 +#define QI_BLOCK_NUMBER 7 +#define DECO_BLOCK_NUMBER 8 +#define PG_SIZE_4K 0x1000 +#define PG_SIZE_64K 0x10000 +#endif +/* REGS_H */ diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 03d3975..9fb8108 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -342,6 +342,16 @@ config FSL_RAID the capability to offload RAID5/RAID6 operations from CPU. RAID5 is XOR and memcpy. RAID6 is P/Q and memcpy +config FSL_EDMA + tristate "Freescale eDMA engine support" + depends on OF + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support the Freescale eDMA engine with programmable channel + multiplexing capability for DMA request sources(slot). + This module can be found on Freescale Vybrid and LS-1 SoCs. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 2d8b4f5..3121300 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -43,3 +43,4 @@ obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o obj-$(CONFIG_TI_CPPI41) += cppi41.o obj-$(CONFIG_K3_DMA) += k3dma.o +obj-$(CONFIG_FSL_EDMA) += fsl-edma.o diff --git a/drivers/dma/bestcomm/Kconfig b/drivers/dma/bestcomm/Kconfig index 29e4270..9bb1bf8 100644 --- a/drivers/dma/bestcomm/Kconfig +++ b/drivers/dma/bestcomm/Kconfig @@ -6,7 +6,7 @@ config PPC_BESTCOMM tristate "Bestcomm DMA engine support" depends on PPC_MPC52xx default n - select PPC_LIB_RHEAP + select LIB_RHEAP help BestComm is the name of the communication coprocessor found on the Freescale MPC5200 family of processor. Its usage is diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c new file mode 100644 index 0000000..eadb155 --- /dev/null +++ b/drivers/dma/fsl-edma.c @@ -0,0 +1,1079 @@ +/* + * drivers/dma/fsl-edma.c + * + * Copyright 2013-2014 Freescale Semiconductor, Inc. + * + * Driver for the Freescale eDMA engine with flexible channel multiplexing + * capability for DMA request sources. The eDMA block can be found on some + * Vybrid and Layerscape SoCs. + * + * 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 <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_dma.h> + +#include "virt-dma.h" + +#define EDMA_CR 0x00 +#define EDMA_ES 0x04 +#define EDMA_ERQ 0x0C +#define EDMA_EEI 0x14 +#define EDMA_SERQ 0x1B +#define EDMA_CERQ 0x1A +#define EDMA_SEEI 0x19 +#define EDMA_CEEI 0x18 +#define EDMA_CINT 0x1F +#define EDMA_CERR 0x1E +#define EDMA_SSRT 0x1D +#define EDMA_CDNE 0x1C +#define EDMA_INTR 0x24 +#define EDMA_ERR 0x2C + +#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x)) +#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x)) +#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x)) +#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x)) +#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x)) +#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x)) +#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x)) +#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x)) +#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x)) +#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x)) +#define EDMA_TCD_CSR(x) (0x101C + 32 * (x)) +#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x)) +#define EDMA_TCD_BITER(x) (0x101E + 32 * (x)) + +#define EDMA_CR_EDBG BIT(1) +#define EDMA_CR_ERCA BIT(2) +#define EDMA_CR_ERGA BIT(3) +#define EDMA_CR_HOE BIT(4) +#define EDMA_CR_HALT BIT(5) +#define EDMA_CR_CLM BIT(6) +#define EDMA_CR_EMLM BIT(7) +#define EDMA_CR_ECX BIT(16) +#define EDMA_CR_CX BIT(17) + +#define EDMA_SEEI_SEEI(x) ((x) & 0x1F) +#define EDMA_CEEI_CEEI(x) ((x) & 0x1F) +#define EDMA_CINT_CINT(x) ((x) & 0x1F) +#define EDMA_CERR_CERR(x) ((x) & 0x1F) + +#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007)) +#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3) +#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8) +#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11) +#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000) +#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100) +#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200) +#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300) +#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500) +#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000) +#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001) +#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002) +#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003) +#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005) + +#define EDMA_TCD_SOFF_SOFF(x) (x) +#define EDMA_TCD_NBYTES_NBYTES(x) (x) +#define EDMA_TCD_SLAST_SLAST(x) (x) +#define EDMA_TCD_DADDR_DADDR(x) (x) +#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF) +#define EDMA_TCD_DOFF_DOFF(x) (x) +#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x) +#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF) + +#define EDMA_TCD_CSR_START BIT(0) +#define EDMA_TCD_CSR_INT_MAJOR BIT(1) +#define EDMA_TCD_CSR_INT_HALF BIT(2) +#define EDMA_TCD_CSR_D_REQ BIT(3) +#define EDMA_TCD_CSR_E_SG BIT(4) +#define EDMA_TCD_CSR_E_LINK BIT(5) +#define EDMA_TCD_CSR_ACTIVE BIT(6) +#define EDMA_TCD_CSR_DONE BIT(7) + +#define EDMAMUX_CHCFG_DIS 0x0 +#define EDMAMUX_CHCFG_ENBL 0x80 +#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F) + +#define DMAMUX_NR 2 + +#define FSL_EDMA_BUSWIDTHS BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) + +struct fsl_edma_hw_tcd { + u32 saddr; + u16 soff; + u16 attr; + u32 nbytes; + u32 slast; + u32 daddr; + u16 doff; + u16 citer; + u32 dlast_sga; + u16 csr; + u16 biter; +}; + +struct fsl_edma_sw_tcd { + dma_addr_t ptcd; + struct fsl_edma_hw_tcd *vtcd; +}; + +struct fsl_edma_slave_config { + enum dma_transfer_direction dir; + enum dma_slave_buswidth addr_width; + u32 dev_addr; + u32 burst; + u32 attr; +}; + +struct fsl_edma_chan { + struct virt_dma_chan vchan; + enum dma_status status; + u32 slave_id; + struct fsl_edma_engine *edma; + struct fsl_edma_desc *edesc; + struct fsl_edma_slave_config fsc; + struct dma_pool *tcd_pool; +}; + +struct fsl_edma_desc { + struct virt_dma_desc vdesc; + struct fsl_edma_chan *echan; + bool iscyclic; + unsigned int n_tcds; + struct fsl_edma_sw_tcd tcd[]; +}; + +struct fsl_edma_engine { + struct dma_device dma_dev; + void __iomem *membase; + void __iomem *muxbase[DMAMUX_NR]; + struct clk *muxclk[DMAMUX_NR]; + struct mutex fsl_edma_mutex; + u32 n_chans; + int txirq; + int errirq; + bool big_endian; + struct fsl_edma_chan chans[]; +}; + +/* + * R/W functions for big- or little-endian registers + * the eDMA controller's endian is independent of the CPU core's endian. + * for the big-endian IP module, the offset for 8-bit or 16-bit register + * should also be swapped oposite to that in little-endian IP. + */ + +static u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr) +{ + u32 dst; + /* swap the reg offset for that in big-endian mode*/ + if (edma->big_endian) { + dst = ((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x2); + return ioread16be((void __iomem *)dst); + } else { + return ioread16(addr); + } +} + +static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) +{ + if (edma->big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr) +{ + u32 dst; + /* swap the reg offset for that in big-endian mode*/ + if (edma->big_endian) { + dst = ((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x3); + iowrite8(val, (void __iomem *)dst); + } else { + iowrite8(val, addr); + } +} + +static void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr) +{ + u32 dst; + /* swap the reg offset for that in big-endian mode*/ + if (edma->big_endian) { + dst = ((u32)addr & ~0x3) | (((u32)addr & 0x3) ^ 0x2); + iowrite16be(val, (void __iomem *)dst); + } else { + iowrite16(val, addr); + } +} + +static void edma_writel(struct fsl_edma_engine *edma, u32 val, void __iomem *addr) +{ + if (edma->big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct fsl_edma_chan, vchan.chan); +} + +static struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd) +{ + return container_of(vd, struct fsl_edma_desc, vdesc); +} + +static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) +{ + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI); + edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ); +} + +static void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) +{ + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ); + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI); +} + +static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, + unsigned int slot, bool enable) +{ + u32 ch = fsl_chan->vchan.chan.chan_id; + void __iomem *muxaddr; + unsigned chans_per_mux, ch_off; + + chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR; + ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux; + muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; + + if (enable) + writeb(EDMAMUX_CHCFG_ENBL | EDMAMUX_CHCFG_SOURCE(slot), + muxaddr + ch_off); + else + writeb(EDMAMUX_CHCFG_DIS, muxaddr + ch_off); +} + +static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) +{ + switch (addr_width) { + case 1: + return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT; + case 2: + return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT; + case 4: + return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; + case 8: + return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT; + default: + return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; + } +} + +static void fsl_edma_free_desc(struct virt_dma_desc *vdesc) +{ + struct fsl_edma_desc *fsl_desc; + int i; + + fsl_desc = to_fsl_edma_desc(vdesc); + for (i = 0; i < fsl_desc->n_tcds; i++) + dma_pool_free(fsl_desc->echan->tcd_pool, + fsl_desc->tcd[i].vtcd, + fsl_desc->tcd[i].ptcd); + kfree(fsl_desc); +} + +static int fsl_edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct dma_slave_config *cfg = (void *)arg; + unsigned long flags; + LIST_HEAD(head); + + switch (cmd) { + case DMA_TERMINATE_ALL: + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + fsl_edma_disable_request(fsl_chan); + fsl_chan->edesc = NULL; + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + return 0; + + case DMA_SLAVE_CONFIG: + fsl_chan->fsc.dir = cfg->direction; + if (cfg->direction == DMA_DEV_TO_MEM) { + fsl_chan->fsc.dev_addr = cfg->src_addr; + fsl_chan->fsc.addr_width = cfg->src_addr_width; + fsl_chan->fsc.burst = cfg->src_maxburst; + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width); + } else if (cfg->direction == DMA_MEM_TO_DEV) { + fsl_chan->fsc.dev_addr = cfg->dst_addr; + fsl_chan->fsc.addr_width = cfg->dst_addr_width; + fsl_chan->fsc.burst = cfg->dst_maxburst; + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width); + } else { + return -EINVAL; + } + return 0; + + case DMA_PAUSE: + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (fsl_chan->edesc) { + fsl_edma_disable_request(fsl_chan); + fsl_chan->status = DMA_PAUSED; + } + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + return 0; + + case DMA_RESUME: + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (fsl_chan->edesc) { + fsl_edma_enable_request(fsl_chan); + fsl_chan->status = DMA_IN_PROGRESS; + } + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + return 0; + + default: + return -ENXIO; + } +} + +static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, + struct virt_dma_desc *vdesc, bool in_progress) +{ + struct fsl_edma_desc *edesc = fsl_chan->edesc; + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + enum dma_transfer_direction dir = fsl_chan->fsc.dir; + dma_addr_t cur_addr, dma_addr; + size_t len, size; + int i; + + /* calculate the total size in this desc */ + for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) + len += edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes)) + * edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter)); + + if (!in_progress) + return len; + + if (dir == DMA_MEM_TO_DEV) + cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_SADDR(ch)); + else + cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_DADDR(ch)); + + /* figure out the finished and calculate the residue */ + for (i = 0; i < fsl_chan->edesc->n_tcds; i++) { + size = edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes)) + * edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter)); + if (dir == DMA_MEM_TO_DEV) + dma_addr = edma_readl(fsl_chan->edma, + &(edesc->tcd[i].vtcd->saddr)); + else + dma_addr = edma_readl(fsl_chan->edma, + &(edesc->tcd[i].vtcd->daddr)); + + len -= size; + if (cur_addr > dma_addr && cur_addr < dma_addr + size) { + len += dma_addr + size - cur_addr; + break; + } + } + + return len; +} + +static enum dma_status fsl_edma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + + status = dma_cookie_status(chan, cookie, txstate); + if (status == DMA_COMPLETE) + return status; + + if (!txstate) + return fsl_chan->status; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + vdesc = vchan_find_desc(&fsl_chan->vchan, cookie); + if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie) + txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, true); + else if (vdesc) + txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, false); + else + txstate->residue = 0; + + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + return fsl_chan->status; +} + +static void fsl_edma_set_tcd_params(struct fsl_edma_chan *fsl_chan, + u32 src, u32 dst, u16 attr, u16 soff, u32 nbytes, + u32 slast, u16 citer, u16 biter, u32 doff, u32 dlast_sga, + u16 csr) +{ + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + /* + * TCD parameters should be swapped according the eDMA + * engine requirement. + */ + edma_writew(fsl_chan->edma, 0, addr + EDMA_TCD_CSR(ch)); + edma_writel(fsl_chan->edma, src, addr + EDMA_TCD_SADDR(ch)); + edma_writel(fsl_chan->edma, dst, addr + EDMA_TCD_DADDR(ch)); + + edma_writew(fsl_chan->edma, attr, addr + EDMA_TCD_ATTR(ch)); + edma_writew(fsl_chan->edma, soff, addr + EDMA_TCD_SOFF(ch)); + + edma_writel(fsl_chan->edma, nbytes, addr + EDMA_TCD_NBYTES(ch)); + edma_writel(fsl_chan->edma, slast, addr + EDMA_TCD_SLAST(ch)); + + edma_writew(fsl_chan->edma, citer, addr + EDMA_TCD_CITER(ch)); + edma_writew(fsl_chan->edma, biter, addr + EDMA_TCD_BITER(ch)); + edma_writew(fsl_chan->edma, doff, addr + EDMA_TCD_DOFF(ch)); + + edma_writel(fsl_chan->edma, dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch)); + + edma_writew(fsl_chan->edma, csr, addr + EDMA_TCD_CSR(ch)); +} + +static void fill_tcd_params(struct fsl_edma_engine *edma, + struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, + u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer, + u16 biter, u16 doff, u32 dlast_sga, bool major_int, + bool disable_req, bool enable_sg) +{ + u16 csr = 0; + + /* + * eDMA hardware SGs requires the TCDs to be auto loaded + * in the same endian as the core whenver the eDAM engine's + * register endian. So we don't swap the value, waitting + * for fsl_set_tcd_params doing the swap. + */ + writel(src, &(tcd->saddr)); + writel(dst, &(tcd->daddr)); + + writew(attr, &(tcd->attr)); + + writew(EDMA_TCD_SOFF_SOFF(soff), &(tcd->soff)); + + writel(EDMA_TCD_NBYTES_NBYTES(nbytes), &(tcd->nbytes)); + writel(EDMA_TCD_SLAST_SLAST(slast), &(tcd->slast)); + + writew(EDMA_TCD_CITER_CITER(citer), &(tcd->citer)); + writew(EDMA_TCD_DOFF_DOFF(doff), &(tcd->doff)); + + writel(EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga), &(tcd->dlast_sga)); + + writew(EDMA_TCD_BITER_BITER(biter), &(tcd->biter)); + if (major_int) + csr |= EDMA_TCD_CSR_INT_MAJOR; + + if (disable_req) + csr |= EDMA_TCD_CSR_D_REQ; + + if (enable_sg) + csr |= EDMA_TCD_CSR_E_SG; + + writew(csr, &(tcd->csr)); +} + +static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, + int sg_len) +{ + struct fsl_edma_desc *fsl_desc; + int i; + + fsl_desc = kzalloc(sizeof(*fsl_desc) + sizeof(struct fsl_edma_sw_tcd) * sg_len, + GFP_NOWAIT); + if (!fsl_desc) + return NULL; + + fsl_desc->echan = fsl_chan; + fsl_desc->n_tcds = sg_len; + for (i = 0; i < sg_len; i++) { + fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool, + GFP_NOWAIT, &fsl_desc->tcd[i].ptcd); + if (!fsl_desc->tcd[i].vtcd) + goto err; + } + return fsl_desc; + +err: + while (--i >= 0) + dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd, + fsl_desc->tcd[i].ptcd); + kfree(fsl_desc); + return NULL; +} + +static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct fsl_edma_desc *fsl_desc; + dma_addr_t dma_buf_next; + int sg_len, i; + u32 src_addr, dst_addr, last_sg, nbytes; + u16 soff, doff, iter; + + if (!is_slave_direction(fsl_chan->fsc.dir)) + return NULL; + + sg_len = buf_len / period_len; + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); + if (!fsl_desc) + return NULL; + fsl_desc->iscyclic = true; + + dma_buf_next = dma_addr; + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; + iter = period_len / nbytes; + + for (i = 0; i < sg_len; i++) { + if (dma_buf_next >= dma_addr + buf_len) + dma_buf_next = dma_addr; + + /* get next sg's physical address */ + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; + + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { + src_addr = dma_buf_next; + dst_addr = fsl_chan->fsc.dev_addr; + soff = fsl_chan->fsc.addr_width; + doff = 0; + } else { + src_addr = fsl_chan->fsc.dev_addr; + dst_addr = dma_buf_next; + soff = 0; + doff = fsl_chan->fsc.addr_width; + } + + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr, + dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0, + iter, iter, doff, last_sg, true, false, true); + dma_buf_next += period_len; + } + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct fsl_edma_desc *fsl_desc; + struct scatterlist *sg; + u32 src_addr, dst_addr, last_sg, nbytes; + u16 soff, doff, iter; + int i; + + if (!is_slave_direction(fsl_chan->fsc.dir)) + return NULL; + + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); + if (!fsl_desc) + return NULL; + fsl_desc->iscyclic = false; + + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; + for_each_sg(sgl, sg, sg_len, i) { + /* get next sg's physical address */ + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; + + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { + src_addr = sg_dma_address(sg); + dst_addr = fsl_chan->fsc.dev_addr; + soff = fsl_chan->fsc.addr_width; + doff = 0; + } else { + src_addr = fsl_chan->fsc.dev_addr; + dst_addr = sg_dma_address(sg); + soff = 0; + doff = fsl_chan->fsc.addr_width; + } + + iter = sg_dma_len(sg) / nbytes; + if (i < sg_len - 1) { + last_sg = fsl_desc->tcd[(i + 1)].ptcd; + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, + src_addr, dst_addr, fsl_chan->fsc.attr, + soff, nbytes, 0, iter, iter, doff, last_sg, + false, false, true); + } else { + last_sg = 0; + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, + src_addr, dst_addr, fsl_chan->fsc.attr, + soff, nbytes, 0, iter, iter, doff, last_sg, + true, true, false); + } + } + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor * +fsl_edma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long tx_flags) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct fsl_edma_desc *fsl_desc; + + fsl_desc = fsl_edma_alloc_desc(fsl_chan, 1); + if (!fsl_desc) + return NULL; + fsl_desc->iscyclic = false; + + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[0].vtcd, src, dst, + EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT, + 0x4, len, 0, 1, 1, 0x4, 0, + true, true, false); + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, tx_flags); + +} + +static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) +{ + struct fsl_edma_hw_tcd *tcd; + struct virt_dma_desc *vdesc; + + vdesc = vchan_next_desc(&fsl_chan->vchan); + if (!vdesc) + return; + fsl_chan->edesc = to_fsl_edma_desc(vdesc); + tcd = fsl_chan->edesc->tcd[0].vtcd; + fsl_edma_set_tcd_params(fsl_chan, tcd->saddr, tcd->daddr, tcd->attr, + tcd->soff, tcd->nbytes, tcd->slast, tcd->citer, + tcd->biter, tcd->doff, tcd->dlast_sga, tcd->csr); + fsl_edma_enable_request(fsl_chan); + fsl_chan->status = DMA_IN_PROGRESS; +} + +static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) +{ + struct fsl_edma_engine *fsl_edma = dev_id; + unsigned int intr, ch; + void __iomem *base_addr; + struct fsl_edma_chan *fsl_chan; + + base_addr = fsl_edma->membase; + + intr = edma_readl(fsl_edma, base_addr + EDMA_INTR); + if (!intr) + return IRQ_NONE; + + for (ch = 0; ch < fsl_edma->n_chans; ch++) { + if (intr & (0x1 << ch)) { + edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), + base_addr + EDMA_CINT); + + fsl_chan = &fsl_edma->chans[ch]; + + spin_lock(&fsl_chan->vchan.lock); + if (!fsl_chan->edesc->iscyclic) { + list_del(&fsl_chan->edesc->vdesc.node); + vchan_cookie_complete(&fsl_chan->edesc->vdesc); + fsl_chan->edesc = NULL; + fsl_chan->status = DMA_COMPLETE; + } else { + vchan_cyclic_callback(&fsl_chan->edesc->vdesc); + } + + if (!fsl_chan->edesc) + fsl_edma_xfer_desc(fsl_chan); + + spin_unlock(&fsl_chan->vchan.lock); + } + } + return IRQ_HANDLED; +} + +static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id) +{ + struct fsl_edma_engine *fsl_edma = dev_id; + unsigned int err, ch; + + err = edma_readl(fsl_edma, fsl_edma->membase + EDMA_ERR); + if (!err) + return IRQ_NONE; + + for (ch = 0; ch < fsl_edma->n_chans; ch++) { + if (err & (0x1 << ch)) { + fsl_edma_disable_request(&fsl_edma->chans[ch]); + edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), + fsl_edma->membase + EDMA_CERR); + fsl_edma->chans[ch].status = DMA_ERROR; + } + } + return IRQ_HANDLED; +} + +static irqreturn_t fsl_edma_irq_handler(int irq, void *dev_id) +{ + if (fsl_edma_tx_handler(irq, dev_id) == IRQ_HANDLED) + return IRQ_HANDLED; + + return fsl_edma_err_handler(irq, dev_id); +} + +static void fsl_edma_issue_pending(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + + if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc) + fsl_edma_xfer_desc(fsl_chan); + + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); +} + +static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data; + struct dma_chan *chan, *_chan; + struct fsl_edma_chan *fsl_chan; + unsigned long chans_per_mux = fsl_edma->n_chans / DMAMUX_NR; + + if (dma_spec->args_count != 2) + return NULL; + + mutex_lock(&fsl_edma->fsl_edma_mutex); + list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) { + if (chan->client_count) + continue; + if ((chan->chan_id / chans_per_mux) == dma_spec->args[0]) { + chan = dma_get_slave_channel(chan); + if (chan) { + chan->device->privatecnt++; + fsl_chan = to_fsl_edma_chan(chan); + fsl_chan->slave_id = dma_spec->args[1]; + fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, + true); + mutex_unlock(&fsl_edma->fsl_edma_mutex); + return chan; + } + } + } + mutex_unlock(&fsl_edma->fsl_edma_mutex); + return NULL; +} + +static int fsl_edma_alloc_chan_resources(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + + fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev, + sizeof(struct fsl_edma_hw_tcd), + 32, 0); + return 0; +} + +static void fsl_edma_free_chan_resources(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + fsl_edma_disable_request(fsl_chan); + fsl_edma_chan_mux(fsl_chan, 0, false); + fsl_chan->slave_id = 0; + fsl_chan->edesc = NULL; + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + dma_pool_destroy(fsl_chan->tcd_pool); + fsl_chan->tcd_pool = NULL; +} + +static int fsl_dma_device_slave_caps(struct dma_chan *dchan, + struct dma_slave_caps *caps) +{ + caps->src_addr_widths = FSL_EDMA_BUSWIDTHS; + caps->dstn_addr_widths = FSL_EDMA_BUSWIDTHS; + caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + caps->cmd_pause = true; + caps->cmd_terminate = true; + + return 0; +} + +static int +fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) +{ + int ret; + + fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx"); + if (fsl_edma->txirq < 0) { + dev_err(&pdev->dev, "Can't get edma-tx irq.\n"); + return fsl_edma->txirq; + } + + fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err"); + if (fsl_edma->errirq < 0) { + dev_err(&pdev->dev, "Can't get edma-err irq.\n"); + return fsl_edma->errirq; + } + + if (fsl_edma->txirq == fsl_edma->errirq) { + ret = devm_request_irq(&pdev->dev, fsl_edma->txirq, + fsl_edma_irq_handler, 0, "eDMA", fsl_edma); + if (ret) { + dev_err(&pdev->dev, "Can't register eDMA IRQ.\n"); + return ret; + } + } else { + ret = devm_request_irq(&pdev->dev, fsl_edma->txirq, + fsl_edma_tx_handler, 0, "eDMA tx", fsl_edma); + if (ret) { + dev_err(&pdev->dev, "Can't register eDMA tx IRQ.\n"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, fsl_edma->errirq, + fsl_edma_err_handler, 0, "eDMA err", fsl_edma); + if (ret) { + dev_err(&pdev->dev, "Can't register eDMA err IRQ.\n"); + return ret; + } + } + + return 0; +} + +static int fsl_edma_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_edma_engine *fsl_edma; + struct fsl_edma_chan *fsl_chan; + struct resource *res; + int len, chans; + int ret, i; + + ret = of_property_read_u32(np, "dma-channels", &chans); + if (ret) { + dev_err(&pdev->dev, "Can't get dma-channels.\n"); + return ret; + } + + len = sizeof(*fsl_edma) + sizeof(*fsl_chan) * chans; + fsl_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_edma) + return -ENOMEM; + + fsl_edma->n_chans = chans; + mutex_init(&fsl_edma->fsl_edma_mutex); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fsl_edma->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_edma->membase)) + return PTR_ERR(fsl_edma->membase); + + for (i = 0; i < DMAMUX_NR; i++) { + char clkname[32]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i); + fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_edma->muxbase[i])) + return PTR_ERR(fsl_edma->muxbase[i]); + + sprintf(clkname, "dmamux%d", i); + fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname); + if (IS_ERR(fsl_edma->muxclk[i])) { + dev_err(&pdev->dev, "Missing DMAMUX block clock.\n"); + return PTR_ERR(fsl_edma->muxclk[i]); + } + + ret = clk_prepare_enable(fsl_edma->muxclk[i]); + if (ret) { + dev_err(&pdev->dev, "DMAMUX clk block failed.\n"); + return ret; + } + + } + + ret = fsl_edma_irq_init(pdev, fsl_edma); + if (ret) + return ret; + + fsl_edma->big_endian = of_property_read_bool(np, "big-endian"); + + INIT_LIST_HEAD(&fsl_edma->dma_dev.channels); + for (i = 0; i < fsl_edma->n_chans; i++) { + struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i]; + + fsl_chan->edma = fsl_edma; + + fsl_chan->vchan.desc_free = fsl_edma_free_desc; + vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); + + edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i)); + fsl_edma_chan_mux(fsl_chan, 0, false); + } + + dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask); + dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask); + dma_cap_set(DMA_CYCLIC, fsl_edma->dma_dev.cap_mask); + dma_cap_set(DMA_MEMCPY, fsl_edma->dma_dev.cap_mask); + + fsl_edma->dma_dev.dev = &pdev->dev; + fsl_edma->dma_dev.device_alloc_chan_resources + = fsl_edma_alloc_chan_resources; + fsl_edma->dma_dev.device_free_chan_resources + = fsl_edma_free_chan_resources; + fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status; + fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg; + fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic; + fsl_edma->dma_dev.device_control = fsl_edma_control; + fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; + fsl_edma->dma_dev.device_slave_caps = fsl_dma_device_slave_caps; + fsl_edma->dma_dev.device_prep_dma_memcpy = fsl_edma_prep_memcpy; + + platform_set_drvdata(pdev, fsl_edma); + + ret = dma_async_device_register(&fsl_edma->dma_dev); + if (ret) { + dev_err(&pdev->dev, "Can't register Freescale eDMA engine.\n"); + return ret; + } + + ret = of_dma_controller_register(np, fsl_edma_xlate, fsl_edma); + if (ret) { + dev_err(&pdev->dev, "Can't register Freescale eDMA of_dma.\n"); + dma_async_device_unregister(&fsl_edma->dma_dev); + return ret; + } + + /* enable round robin arbitration */ + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, fsl_edma->membase + EDMA_CR); + + return 0; +} + +static int fsl_edma_remove(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev); + int i; + + of_dma_controller_free(np); + dma_async_device_unregister(&fsl_edma->dma_dev); + + for (i = 0; i < DMAMUX_NR; i++) + clk_disable_unprepare(fsl_edma->muxclk[i]); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fsl_edma_suspend(struct device *dev) +{ + struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); + struct fsl_edma_chan *fsl_chan; + int i; + + for (i = 0; i < fsl_edma->n_chans; i++) { + fsl_chan = &fsl_edma->chans[i]; + fsl_edma_chan_mux(fsl_chan, 0, false); + } + + return 0; +} + +static int fsl_edma_resume(struct device *dev) +{ + struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); + struct fsl_edma_chan *fsl_chan; + int i; + + for (i = 0; i < fsl_edma->n_chans; i++) { + fsl_chan = &fsl_edma->chans[i]; + edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i)); + fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true); + } + + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, + fsl_edma->membase + EDMA_CR); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(fsl_edma_pm_ops, fsl_edma_suspend, fsl_edma_resume); + +static const struct of_device_id fsl_edma_dt_ids[] = { + { .compatible = "fsl,vf610-edma", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); + +static struct platform_driver fsl_edma_driver = { + .driver = { + .name = "fsl-edma", + .owner = THIS_MODULE, + .of_match_table = fsl_edma_dt_ids, + .pm = &fsl_edma_pm_ops, + }, + .probe = fsl_edma_probe, + .remove = fsl_edma_remove, +}; + +static int __init fsl_edma_init(void) +{ + return platform_driver_register(&fsl_edma_driver); +} +subsys_initcall(fsl_edma_init); + +static void __exit fsl_edma_exit(void) +{ + platform_driver_unregister(&fsl_edma_driver); +} +module_exit(fsl_edma_exit); + +MODULE_ALIAS("platform:fsl-edma"); +MODULE_DESCRIPTION("Freescale eDMA engine driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b6ed304..80a674d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -163,10 +163,10 @@ config GPIO_MPC5200 config GPIO_MPC8XXX bool "MPC512x/MPC8xxx GPIO support" depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \ - FSL_SOC_BOOKE || PPC_86xx + FSL_SOC_BOOKE || PPC_86xx || ARCH_LAYERSCAPE help Say Y here if you're going to use hardware that connects to the - MPC512x/831x/834x/837x/8572/8610 GPIOs. + MPC512x/831x/834x/837x/8572/8610/LS1021A GPIOs. config GPIO_MSM_V1 tristate "Qualcomm MSM GPIO v1" @@ -193,7 +193,7 @@ config GPIO_MVEBU config GPIO_MXC def_bool y - depends on ARCH_MXC + depends on ARCH_MXC && !ARCH_LAYERSCAPE select GPIO_GENERIC select GENERIC_IRQ_CHIP diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 2aa3ca2..6333085 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -1,5 +1,5 @@ /* - * GPIOs on MPC512x/8349/8572/8610 and compatible + * GPIOs on MPC512x/8349/8572/8610/LS1020A and compatible * * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk> * @@ -14,9 +14,11 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/of_irq.h> #include <linux/gpio.h> #include <linux/slab.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #define MPC8XXX_GPIO_PINS 32 @@ -56,9 +58,17 @@ static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); - mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT); + mpc8xxx_gc->data = ioread32be(mm->regs + GPIO_DAT); } +/* Generic set and clear bits accessor ports */ +#define bgpio_setbits32(_addr, _v) \ + iowrite32be(ioread32be(_addr) | (_v), (_addr)) +#define bgpio_clrbits32(_addr, _v) \ + iowrite32be(ioread32be(_addr) & ~(_v), (_addr)) +#define bgpio_clrsetbits32(addr, clear, set) \ + iowrite32be((ioread32be(addr) & ~(clear)) | (set), (addr)) + /* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs * defined as output cannot be determined by reading GPDAT register, * so we use shadow data register instead. The status of input pins @@ -71,9 +81,9 @@ static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio) struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm); u32 out_mask, out_shadow; - out_mask = in_be32(mm->regs + GPIO_DIR); + out_mask = ioread32be(mm->regs + GPIO_DIR); - val = in_be32(mm->regs + GPIO_DAT) & ~out_mask; + val = ioread32be(mm->regs + GPIO_DAT) & ~out_mask; out_shadow = mpc8xxx_gc->data & out_mask; return (val | out_shadow) & mpc8xxx_gpio2mask(gpio); @@ -83,7 +93,7 @@ static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio) { struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc); - return in_be32(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio); + return ioread32be(mm->regs + GPIO_DAT) & mpc8xxx_gpio2mask(gpio); } static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) @@ -99,7 +109,7 @@ static void mpc8xxx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) else mpc8xxx_gc->data &= ~mpc8xxx_gpio2mask(gpio); - out_be32(mm->regs + GPIO_DAT, mpc8xxx_gc->data); + iowrite32be(mpc8xxx_gc->data, mm->regs + GPIO_DAT); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); } @@ -112,7 +122,7 @@ static int mpc8xxx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); + bgpio_clrbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); @@ -129,7 +139,7 @@ static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); + bgpio_setbits32(mm->regs + GPIO_DIR, mpc8xxx_gpio2mask(gpio)); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); @@ -163,7 +173,8 @@ static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc) struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; unsigned int mask; - mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR); + mask = ioread32be(mm->regs + GPIO_IER) + & ioread32be(mm->regs + GPIO_IMR); if (mask) generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, 32 - ffs(mask))); @@ -179,7 +190,8 @@ static void mpc8xxx_irq_unmask(struct irq_data *d) spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + bgpio_setbits32(mm->regs + GPIO_IMR, + mpc8xxx_gpio2mask(irqd_to_hwirq(d))); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); } @@ -192,7 +204,8 @@ static void mpc8xxx_irq_mask(struct irq_data *d) spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + bgpio_clrbits32(mm->regs + GPIO_IMR, + mpc8xxx_gpio2mask(irqd_to_hwirq(d))); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); } @@ -202,7 +215,7 @@ static void mpc8xxx_irq_ack(struct irq_data *d) struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc; - out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); + iowrite32be(mpc8xxx_gpio2mask(irqd_to_hwirq(d)), mm->regs + GPIO_IER); } static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) @@ -214,14 +227,14 @@ static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) switch (flow_type) { case IRQ_TYPE_EDGE_FALLING: spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - setbits32(mm->regs + GPIO_ICR, + bgpio_setbits32(mm->regs + GPIO_ICR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; case IRQ_TYPE_EDGE_BOTH: spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(mm->regs + GPIO_ICR, + bgpio_clrbits32(mm->regs + GPIO_ICR, mpc8xxx_gpio2mask(irqd_to_hwirq(d))); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; @@ -254,20 +267,20 @@ static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrsetbits_be32(reg, 3 << shift, 2 << shift); + bgpio_clrsetbits32(reg, 3 << shift, 2 << shift); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_LEVEL_HIGH: spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrsetbits_be32(reg, 3 << shift, 1 << shift); + bgpio_clrsetbits32(reg, 3 << shift, 1 << shift); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; case IRQ_TYPE_EDGE_BOTH: spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - clrbits32(reg, 3 << shift); + bgpio_clrbits32(reg, 3 << shift); spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; @@ -312,6 +325,7 @@ static struct of_device_id mpc8xxx_gpio_ids[] __initdata = { { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, }, { .compatible = "fsl,pq3-gpio", }, { .compatible = "fsl,qoriq-gpio", }, + { .compatible = "fsl,ls1021a-gpio", }, {} }; @@ -363,8 +377,8 @@ static void __init mpc8xxx_add_controller(struct device_node *np) mpc8xxx_gc->of_dev_id_data = id->data; /* ack and mask all irqs */ - out_be32(mm_gc->regs + GPIO_IER, 0xffffffff); - out_be32(mm_gc->regs + GPIO_IMR, 0); + iowrite32be(0xffffffff, mm_gc->regs + GPIO_IER); + iowrite32be(0, mm_gc->regs + GPIO_IMR); irq_set_handler_data(hwirq, mpc8xxx_gc); irq_set_chained_handler(hwirq, mpc8xxx_gpio_irq_cascade); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b3ab9d4..4201c7e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -774,6 +774,18 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_LTC2945 + tristate "Linear Technology LTC2945" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC2945 + I2C System Monitor. + + This driver can also be built as a module. If so, the module will + be called ltc2945. + config SENSORS_LTC4151 tristate "Linear Technology LTC4151" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index ec7cde0..eacd2ee 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95234) += lm95234.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o +obj-$(CONFIG_SENSORS_LTC2945) += ltc2945.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 646314f..a26c385 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -15,45 +15,138 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/slab.h> #include <linux/kdev_t.h> #include <linux/idr.h> #include <linux/hwmon.h> #include <linux/gfp.h> #include <linux/spinlock.h> #include <linux/pci.h> +#include <linux/string.h> #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" -static struct class *hwmon_class; +struct hwmon_device { + const char *name; + struct device dev; +}; +#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct attribute *hwmon_dev_attrs[] = { + &dev_attr_name.attr, + NULL +}; + +static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + if (to_hwmon_device(dev)->name == NULL) + return 0; + + return attr->mode; +} + +static struct attribute_group hwmon_dev_attr_group = { + .attrs = hwmon_dev_attrs, + .is_visible = hwmon_dev_name_is_visible, +}; + +static const struct attribute_group *hwmon_dev_attr_groups[] = { + &hwmon_dev_attr_group, + NULL +}; + +static void hwmon_dev_release(struct device *dev) +{ + kfree(to_hwmon_device(dev)); +} + +static struct class hwmon_class = { + .name = "hwmon", + .owner = THIS_MODULE, + .dev_groups = hwmon_dev_attr_groups, + .dev_release = hwmon_dev_release, +}; static DEFINE_IDA(hwmon_ida); /** - * hwmon_device_register - register w/ hwmon - * @dev: the device to register + * hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create * * hwmon_device_unregister() must be called when the device is no * longer needed. * * Returns the pointer to the new device. */ -struct device *hwmon_device_register(struct device *dev) +struct device * +hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) { - struct device *hwdev; - int id; + struct hwmon_device *hwdev; + int err, id; + + /* Do not accept invalid characters in hwmon name attribute */ + if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) + return ERR_PTR(-EINVAL); id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); - hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, - HWMON_ID_FORMAT, id); + hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); + if (hwdev == NULL) { + err = -ENOMEM; + goto ida_remove; + } - if (IS_ERR(hwdev)) - ida_simple_remove(&hwmon_ida, id); + hwdev->name = name; + hwdev->dev.class = &hwmon_class; + hwdev->dev.parent = dev; + hwdev->dev.groups = groups; + hwdev->dev.of_node = dev ? dev->of_node : NULL; + dev_set_drvdata(&hwdev->dev, drvdata); + dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); + err = device_register(&hwdev->dev); + if (err) + goto free; - return hwdev; + return &hwdev->dev; + +free: + kfree(hwdev); +ida_remove: + ida_simple_remove(&hwmon_ida, id); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); + +/** + * hwmon_device_register - register w/ hwmon + * @dev: the device to register + * + * hwmon_device_unregister() must be called when the device is no + * longer needed. + * + * Returns the pointer to the new device. + */ +struct device *hwmon_device_register(struct device *dev) +{ + return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); } EXPORT_SYMBOL_GPL(hwmon_device_register); @@ -75,6 +168,69 @@ void hwmon_device_unregister(struct device *dev) } EXPORT_SYMBOL_GPL(hwmon_device_unregister); +static void devm_hwmon_release(struct device *dev, void *res) +{ + struct device *hwdev = *(struct device **)res; + + hwmon_device_unregister(hwdev); +} + +/** + * devm_hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create + * + * Returns the pointer to the new device. The new device is automatically + * unregistered with the parent device. + */ +struct device * +devm_hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) +{ + struct device **ptr, *hwdev; + + if (!dev) + return ERR_PTR(-EINVAL); + + ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); + if (IS_ERR(hwdev)) + goto error; + + *ptr = hwdev; + devres_add(dev, ptr); + return hwdev; + +error: + devres_free(ptr); + return hwdev; +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); + +static int devm_hwmon_match(struct device *dev, void *res, void *data) +{ + struct device **hwdev = res; + + return *hwdev == data; +} + +/** + * devm_hwmon_device_unregister - removes a previously registered hwmon device + * + * @dev: the parent device of the device to unregister + */ +void devm_hwmon_device_unregister(struct device *dev) +{ + WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); + static void __init hwmon_pci_quirks(void) { #if defined CONFIG_X86 && defined CONFIG_PCI @@ -105,19 +261,21 @@ static void __init hwmon_pci_quirks(void) static int __init hwmon_init(void) { + int err; + hwmon_pci_quirks(); - hwmon_class = class_create(THIS_MODULE, "hwmon"); - if (IS_ERR(hwmon_class)) { - pr_err("couldn't create sysfs class\n"); - return PTR_ERR(hwmon_class); + err = class_register(&hwmon_class); + if (err) { + pr_err("couldn't register hwmon sysfs class\n"); + return err; } return 0; } static void __exit hwmon_exit(void) { - class_destroy(hwmon_class); + class_unregister(&hwmon_class); } subsys_initcall(hwmon_init); diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c new file mode 100644 index 0000000..3701b32 --- /dev/null +++ b/drivers/hwmon/ltc2945.c @@ -0,0 +1,519 @@ +/* + * Driver for Linear Technology LTC2945 I2C Power Monitor + * + * Copyright (c) 2014 Guenter Roeck + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> + +/* chip registers */ +#define LTC2945_CONTROL 0x00 +#define LTC2945_ALERT 0x01 +#define LTC2945_STATUS 0x02 +#define LTC2945_FAULT 0x03 +#define LTC2945_POWER_H 0x05 +#define LTC2945_MAX_POWER_H 0x08 +#define LTC2945_MIN_POWER_H 0x0b +#define LTC2945_MAX_POWER_THRES_H 0x0e +#define LTC2945_MIN_POWER_THRES_H 0x11 +#define LTC2945_SENSE_H 0x14 +#define LTC2945_MAX_SENSE_H 0x16 +#define LTC2945_MIN_SENSE_H 0x18 +#define LTC2945_MAX_SENSE_THRES_H 0x1a +#define LTC2945_MIN_SENSE_THRES_H 0x1c +#define LTC2945_VIN_H 0x1e +#define LTC2945_MAX_VIN_H 0x20 +#define LTC2945_MIN_VIN_H 0x22 +#define LTC2945_MAX_VIN_THRES_H 0x24 +#define LTC2945_MIN_VIN_THRES_H 0x26 +#define LTC2945_ADIN_H 0x28 +#define LTC2945_MAX_ADIN_H 0x2a +#define LTC2945_MIN_ADIN_H 0x2c +#define LTC2945_MAX_ADIN_THRES_H 0x2e +#define LTC2945_MIN_ADIN_THRES_H 0x30 +#define LTC2945_MIN_ADIN_THRES_L 0x31 + +/* Fault register bits */ + +#define FAULT_ADIN_UV (1 << 0) +#define FAULT_ADIN_OV (1 << 1) +#define FAULT_VIN_UV (1 << 2) +#define FAULT_VIN_OV (1 << 3) +#define FAULT_SENSE_UV (1 << 4) +#define FAULT_SENSE_OV (1 << 5) +#define FAULT_POWER_UV (1 << 6) +#define FAULT_POWER_OV (1 << 7) + +/* Control register bits */ + +#define CONTROL_MULT_SELECT (1 << 0) +#define CONTROL_TEST_MODE (1 << 4) + +static inline bool is_power_reg(u8 reg) +{ + return reg < LTC2945_SENSE_H; +} + +/* Return the value from the given register in uW, mV, or mA */ +static long long ltc2945_reg_to_val(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + u8 buf[3]; + long long val; + int ret; + + ret = regmap_bulk_read(regmap, reg, buf, + is_power_reg(reg) ? 3 : 2); + if (ret < 0) + return ret; + + if (is_power_reg(reg)) { + /* power */ + val = (buf[0] << 16) + (buf[1] << 8) + buf[2]; + } else { + /* current, voltage */ + val = (buf[0] << 4) + (buf[1] >> 4); + } + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to uW by assuming current is measured with + * an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val *= 625LL; + } else { + /* 0.5 mV * 25 uV = 0.0125 uV resolution. */ + val = (val * 25LL) >> 1; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. Convert to mV. */ + val *= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. Convert to mV. */ + val = val >> 1; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val *= 25; + break; + default: + return -EINVAL; + } + return val; +} + +static int ltc2945_val_to_reg(struct device *dev, u8 reg, + unsigned long val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + int ret; + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to register value by assuming current is measured + * with an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power, which in turn + * determines register calculations. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val = DIV_ROUND_CLOSEST(val, 625); + } else { + /* + * 0.5 mV * 25 uV = 0.0125 uV resolution. + * Divide first to avoid overflow; + * accept loss of accuracy. + */ + val = DIV_ROUND_CLOSEST(val, 25) * 2; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. */ + val /= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. */ + val *= 2; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = DIV_ROUND_CLOSEST(val, 25); + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t ltc2945_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + long long value; + + value = ltc2945_reg_to_val(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%lld\n", value); +} + +static ssize_t ltc2945_set_value(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + unsigned long val; + u8 regbuf[3]; + int num_regs; + int regval; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + /* convert to register value, then clamp and write result */ + regval = ltc2945_val_to_reg(dev, reg, val); + if (is_power_reg(reg)) { + regval = clamp_val(regval, 0, 0xffffff); + regbuf[0] = regval >> 16; + regbuf[1] = (regval >> 8) & 0xff; + regbuf[2] = regval; + num_regs = 3; + } else { + regval = clamp_val(regval, 0, 0xfff) << 4; + regbuf[0] = regval >> 8; + regbuf[1] = regval & 0xff; + num_regs = 2; + } + ret = regmap_bulk_write(regmap, reg, regbuf, num_regs); + return ret < 0 ? ret : count; +} + +static ssize_t ltc2945_reset_history(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + int num_regs = is_power_reg(reg) ? 3 : 2; + u8 buf_min[3] = { 0xff, 0xff, 0xff }; + u8 buf_max[3] = { 0, 0, 0 }; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val != 1) + return -EINVAL; + + ret = regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, + CONTROL_TEST_MODE); + + /* Reset minimum */ + ret = regmap_bulk_write(regmap, reg, buf_min, num_regs); + if (ret) + return ret; + + switch (reg) { + case LTC2945_MIN_POWER_H: + reg = LTC2945_MAX_POWER_H; + break; + case LTC2945_MIN_SENSE_H: + reg = LTC2945_MAX_SENSE_H; + break; + case LTC2945_MIN_VIN_H: + reg = LTC2945_MAX_VIN_H; + break; + case LTC2945_MIN_ADIN_H: + reg = LTC2945_MAX_ADIN_H; + break; + default: + WARN_ONCE(1, "Bad register: 0x%x\n", reg); + return -EINVAL; + } + /* Reset maximum */ + ret = regmap_bulk_write(regmap, reg, buf_max, num_regs); + + /* Try resetting test mode even if there was an error */ + regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, 0); + + return ret ? : count; +} + +static ssize_t ltc2945_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, LTC2945_FAULT, &fault); + if (ret < 0) + return ret; + + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Input voltages */ + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_VIN_H); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_VIN_H); +static SENSOR_DEVICE_ATTR(in1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_VIN_H); +static SENSOR_DEVICE_ATTR(in1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_VIN_H); + +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_ADIN_H); + +/* Voltage alarms */ + +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_OV); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_UV); +static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_OV); + +/* Currents (via sense resistor) */ + +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_SENSE_H); + +/* Current alarms */ + +static SENSOR_DEVICE_ATTR(curr1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_UV); +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_OV); + +/* Power */ + +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_POWER_H); +static SENSOR_DEVICE_ATTR(power1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_input_lowest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MIN_POWER_H); +static SENSOR_DEVICE_ATTR(power1_input_highest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MAX_POWER_H); +static SENSOR_DEVICE_ATTR(power1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_POWER_H); + +/* Power alarms */ + +static SENSOR_DEVICE_ATTR(power1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_UV); +static SENSOR_DEVICE_ATTR(power1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_OV); + +static struct attribute *ltc2945_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_lowest.dev_attr.attr, + &sensor_dev_attr_in1_highest.dev_attr.attr, + &sensor_dev_attr_in1_reset_history.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_lowest.dev_attr.attr, + &sensor_dev_attr_in2_highest.dev_attr.attr, + &sensor_dev_attr_in2_reset_history.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_min.dev_attr.attr, + &sensor_dev_attr_curr1_max.dev_attr.attr, + &sensor_dev_attr_curr1_lowest.dev_attr.attr, + &sensor_dev_attr_curr1_highest.dev_attr.attr, + &sensor_dev_attr_curr1_reset_history.dev_attr.attr, + &sensor_dev_attr_curr1_min_alarm.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_min.dev_attr.attr, + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power1_input_lowest.dev_attr.attr, + &sensor_dev_attr_power1_input_highest.dev_attr.attr, + &sensor_dev_attr_power1_reset_history.dev_attr.attr, + &sensor_dev_attr_power1_min_alarm.dev_attr.attr, + &sensor_dev_attr_power1_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc2945); + +static struct regmap_config ltc2945_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC2945_MIN_ADIN_THRES_L, +}; + +static int ltc2945_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c2945_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC2945_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc2945_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc2945_id[] = { + {"ltc2945", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc2945_id); + +static struct i2c_driver ltc2945_driver = { + .driver = { + .name = "ltc2945", + }, + .probe = ltc2945_probe, + .id_table = ltc2945_id, +}; + +module_i2c_driver(ltc2945_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LTC2945 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index bad5b84..687ecb3 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -41,6 +41,7 @@ #include <linux/device.h> #include <linux/i2c.h> #include <linux/i2c-mux.h> +#include <linux/pm.h> #include <linux/i2c/pca954x.h> @@ -274,9 +275,23 @@ static int pca954x_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int pca954x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pca954x *data = i2c_get_clientdata(client); + + data->last_chan = 0; + return i2c_smbus_write_byte(client, 0); +} +#endif + +static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume); + static struct i2c_driver pca954x_driver = { .driver = { .name = "pca954x", + .pm = &pca954x_pm, .owner = THIS_MODULE, }, .probe = pca954x_probe, diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 29a11db..57721ed 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -50,4 +50,12 @@ config TEGRA30_MC analysis, especially for IOMMU/SMMU(System Memory Management Unit) module. +config FSL_IFC + bool "Freescale Integrated Flash Controller" + depends on FSL_SOC + help + This driver is for the Integrated Flash Controller(IFC) module + available in Freescale SoCs. This controller allows to handle + devices such as NOR, NAND, FPGA and ASIC etc. + endif diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 969d923..d1961c5 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -6,6 +6,7 @@ ifeq ($(CONFIG_DDR),y) obj-$(CONFIG_OF) += of_memory.o endif obj-$(CONFIG_TI_EMIF) += emif.o +obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o diff --git a/arch/powerpc/sysdev/fsl_ifc.c b/drivers/memory/fsl_ifc.c index 592e029..93a8cac 100644 --- a/arch/powerpc/sysdev/fsl_ifc.c +++ b/drivers/memory/fsl_ifc.c @@ -29,10 +29,14 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/sched.h> +#include <linux/irqdomain.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/fsl_ifc.h> #include <asm/prom.h> -#include <asm/fsl_ifc.h> struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev; EXPORT_SYMBOL(fsl_ifc_ctrl_dev); @@ -66,7 +70,8 @@ int fsl_ifc_find(phys_addr_t addr_base) return -ENODEV; for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) { - u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); + u32 cspr = ifc_in32( + &fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); if (cspr & CSPR_V && (cspr & CSPR_BA) == convert_ifc_address(addr_base)) return i; @@ -83,16 +88,16 @@ static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) /* * Clear all the common status and event registers */ - if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) - out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); + if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) + ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); /* enable all error and events */ - out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN); + ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en); /* enable all error and event interrupts */ - out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN); - out_be32(&ifc->cm_erattr0, 0x0); - out_be32(&ifc->cm_erattr1, 0x0); + ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en); + ifc_out32(0x0, &ifc->cm_erattr0); + ifc_out32(0x0, &ifc->cm_erattr1); return 0; } @@ -131,9 +136,9 @@ static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) spin_lock_irqsave(&nand_irq_lock, flags); - stat = in_be32(&ifc->ifc_nand.nand_evter_stat); + stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat); if (stat) { - out_be32(&ifc->ifc_nand.nand_evter_stat, stat); + ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat); ctrl->nand_stat = stat; wake_up(&ctrl->nand_wait); } @@ -165,36 +170,37 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) irqreturn_t ret = IRQ_NONE; /* read for chip select error */ - cs_err = in_be32(&ifc->cm_evter_stat); + cs_err = ifc_in32(&ifc->cm_evter_stat); if (cs_err) { - dev_err(ctrl->dev, "transaction sent to IFC is not mapped to" - "any memory bank 0x%08X\n", cs_err); + dev_err(ctrl->dev, "transaction sent to IFC is not mapped to"); + dev_err(ctrl->dev, " any memory bank 0x%08X\n", cs_err); + /* clear the chip select error */ - out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); + ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); /* read error attribute registers print the error information */ - status = in_be32(&ifc->cm_erattr0); - err_addr = in_be32(&ifc->cm_erattr1); - - if (status & IFC_CM_ERATTR0_ERTYP_READ) - dev_err(ctrl->dev, "Read transaction error" - "CM_ERATTR0 0x%08X\n", status); - else - dev_err(ctrl->dev, "Write transaction error" - "CM_ERATTR0 0x%08X\n", status); - + status = ifc_in32(&ifc->cm_erattr0); + err_addr = ifc_in32(&ifc->cm_erattr1); + + if (status & IFC_CM_ERATTR0_ERTYP_READ) { + dev_err(ctrl->dev, "Read transaction error"); + dev_err(ctrl->dev, " CM_ERATTR0 0x%08X\n", status); + } else { + dev_err(ctrl->dev, "Write transaction error"); + dev_err(ctrl->dev, " CM_ERATTR0 0x%08X\n", status); + } err_axiid = (status & IFC_CM_ERATTR0_ERAID) >> IFC_CM_ERATTR0_ERAID_SHIFT; - dev_err(ctrl->dev, "AXI ID of the error" - "transaction 0x%08X\n", err_axiid); + dev_err(ctrl->dev, "AXI ID of the erro"); + dev_err(ctrl->dev, " transaction 0x%08X\n", err_axiid); err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >> IFC_CM_ERATTR0_ESRCID_SHIFT; - dev_err(ctrl->dev, "SRC ID of the error" - "transaction 0x%08X\n", err_srcid); + dev_err(ctrl->dev, "SRC ID of the error"); + dev_err(ctrl->dev, " transaction 0x%08X\n", err_srcid); - dev_err(ctrl->dev, "Transaction Address corresponding to error" - "ERADDR 0x%08X\n", err_addr); + dev_err(ctrl->dev, "Transaction Address corresponding to error"); + dev_err(ctrl->dev, " ERADDR 0x%08X\n", err_addr); ret = IRQ_HANDLED; } @@ -235,11 +241,19 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) goto err; } + if (of_property_read_bool(dev->dev.of_node, "little-endian")) + fsl_ifc_ctrl_dev->little_endian = true; + else + fsl_ifc_ctrl_dev->little_endian = false; + + dev_dbg(&dev->dev, "IFC REGISTERS are %s endian\n", + fsl_ifc_ctrl_dev->little_endian ? "LITTLE" : "BIG"); /* get the Controller level irq */ fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); if (fsl_ifc_ctrl_dev->irq == NO_IRQ) { - dev_err(&dev->dev, "failed to get irq resource " - "for IFC\n"); + dev_err(&dev->dev, "failed to get irq resource "); + dev_err(&dev->dev, "for IFC\n"); + ret = -ENODEV; goto err; } @@ -316,15 +330,18 @@ static int fsl_ifc_resume(struct device *dev) ctrl->saved_regs = NULL; } - ver = in_be32(&ctrl->regs->ifc_rev); - ncfgr = in_be32(&ifc->ifc_nand.ncfgr); + ver = ifc_in32(&ctrl->regs->ifc_rev); + ncfgr = ifc_in32(&ifc->ifc_nand.ncfgr); if (ver >= FSL_IFC_V1_3_0) { - out_be32(&ifc->ifc_nand.ncfgr, ncfgr | IFC_NAND_SRAM_INIT_EN); + ifc_out32(&ifc->ifc_nand.ncfgr, + ncfgr | IFC_NAND_SRAM_INIT_EN); /* wait for SRAM_INIT bit to be clear or timeout */ - status = spin_event_timeout(!(in_be32(&ifc->ifc_nand.ncfgr) - & IFC_NAND_SRAM_INIT_EN), - IFC_TIMEOUT_MSECS, 0); + status = spin_event_timeout( + !(ifc_in32(&ifc->ifc_nand.ncfgr) + & IFC_NAND_SRAM_INIT_EN), + IFC_TIMEOUT_MSECS, 0); + if (!status) dev_err(ctrl->dev, "Timeout waiting for IFC SRAM INIT"); } diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 7fc5099..414184d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -107,7 +107,7 @@ config MMC_SDHCI_PLTFM config MMC_SDHCI_OF_ESDHC tristate "SDHCI OF support for the Freescale eSDHC controller" depends on MMC_SDHCI_PLTFM - depends on PPC_OF + depends on PPC_OF || ARCH_MXC select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER help This selects the Freescale eSDHC controller support. diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0f9f36b..244c42a 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -21,18 +21,23 @@ #include <linux/mmc/host.h> #include "sdhci-pltfm.h" #include "sdhci-esdhc.h" + +#if defined CONFIG_PPC_OF #include <asm/mpc85xx.h> +#endif #define VENDOR_V_22 0x12 #define VENDOR_V_23 0x13 +#if defined CONFIG_PPC_OF static u32 svr; +#endif static u32 esdhc_readl(struct sdhci_host *host, int reg) { u32 ret; - ret = in_be32(host->ioaddr + reg); + ret = sdhci_32bs_readl(host, reg); /* * The bit of ADMA flag in eSDHC is not compatible with standard * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is @@ -44,7 +49,7 @@ static u32 esdhc_readl(struct sdhci_host *host, int reg) * the verdor version number, oxFE is SDHCI_HOST_VERSION. */ if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) { - u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + u32 tmp = sdhci_32bs_readl(host, SDHCI_SLOT_INT_STATUS); tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; if (tmp > VENDOR_V_22) ret |= SDHCI_CAN_DO_ADMA2; @@ -68,15 +73,17 @@ static u16 esdhc_readw(struct sdhci_host *host, int reg) int shift = (reg & 0x2) * 8; if (unlikely(reg == SDHCI_HOST_VERSION)) - ret = in_be32(host->ioaddr + base) & 0xffff; + ret = sdhci_32bs_readl(host, base) & 0xffff; else - ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; + ret = (sdhci_32bs_readl(host, base) >> shift) & 0xffff; +#if defined CONFIG_PPC_OF /* T4240-R1.0-R2.0 had a incorrect vendor version and spec version */ if ((reg == SDHCI_HOST_VERSION) && ((SVR_SOC_VER(svr) == SVR_T4240) && (SVR_REV(svr) <= 0x20))) ret = (VENDOR_V_23 << SDHCI_VENDOR_VER_SHIFT) | SDHCI_SPEC_200; +#endif return ret; } @@ -85,7 +92,10 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) { int base = reg & ~0x3; int shift = (reg & 0x3) * 8; - u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; + u32 ret; + u8 val; + + ret = sdhci_32bs_readl(host, base); /* * "DMA select" locates at offset 0x28 in SD specification, but on @@ -94,16 +104,18 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) if (reg == SDHCI_HOST_CONTROL) { u32 dma_bits; - dma_bits = in_be32(host->ioaddr + reg); /* DMA select is 22,23 bits in Protocol Control Register */ - dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; + dma_bits = (ret >> 5) & SDHCI_CTRL_DMA_MASK; /* fixup the result */ ret &= ~SDHCI_CTRL_DMA_MASK; ret |= dma_bits; + val = (ret & 0xff); } - return ret; + val = (ret >> shift) & 0xff; + + return val; } static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) @@ -115,11 +127,28 @@ static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) */ if (reg == SDHCI_INT_ENABLE) val |= SDHCI_INT_BLK_GAP; - sdhci_be32bs_writel(host, val, reg); + sdhci_32bs_writel(host, val, reg); } static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + switch (reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. + */ + pltfm_host->xfer_mode_shadow = val; + return; + case SDHCI_COMMAND: + sdhci_32bs_writel(host, val << 16 | + pltfm_host->xfer_mode_shadow, + SDHCI_TRANSFER_MODE); + return; + } + if (reg == SDHCI_BLOCK_SIZE) { /* * Two last DMA bits are reserved, and first one is used for @@ -128,7 +157,7 @@ static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) */ val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); } - sdhci_be32bs_writew(host, val, reg); + sdhci_clrsetbits(host, 0xffff, val, reg); } static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) @@ -149,10 +178,10 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) /* DMA select is 22,23 bits in Protocol Control Register */ dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; - clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, - dma_bits); + sdhci_clrsetbits(host, SDHCI_CTRL_DMA_MASK << 5, dma_bits, + SDHCI_HOST_CONTROL); val &= ~SDHCI_CTRL_DMA_MASK; - val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; + val |= sdhci_32bs_readl(host, reg) & SDHCI_CTRL_DMA_MASK; } /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ @@ -174,7 +203,7 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) temp &= ~ESDHC_CLOCK_CRDEN; esdhc_writel(host, temp, ESDHC_SYSTEM_CONTROL); - sdhci_be32bs_writeb(host, val, reg); + sdhci_32bs_writeb(host, val, reg); temp |= ESDHC_CLOCK_CRDEN; esdhc_writel(host, temp, ESDHC_SYSTEM_CONTROL); @@ -187,19 +216,21 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) if (!host->pwr || !val) return; +#if defined CONFIG_PPC_OF if (SVR_SOC_VER(svr) == SVR_T4240) { u8 vol; - vol = sdhci_be32bs_readb(host, reg); + vol = sdhci_32bs_readb(host, reg); if (host->pwr == SDHCI_POWER_180) vol &= ~ESDHC_VOL_SEL; else vol |= ESDHC_VOL_SEL; } else return; +#endif } - sdhci_be32bs_writeb(host, val, reg); + sdhci_clrsetbits(host, 0xff, val, reg); } /* @@ -216,7 +247,7 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) dma_addr_t dmastart; dma_addr_t dmanow; - tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + tmp = esdhc_readl(host, SDHCI_SLOT_INT_STATUS); tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; applicable = (intmask & SDHCI_INT_DATA_END) && @@ -234,11 +265,12 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + SDHCI_DEFAULT_BOUNDARY_SIZE; host->data->bytes_xfered = dmanow - dmastart; - sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); + esdhc_writel(host, dmanow, SDHCI_DMA_ADDRESS); return; } +#if defined CONFIG_PPC_OF /* * Check for A-004388: eSDHC DMA might not stop if error * occurs on system transaction @@ -263,6 +295,7 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) ((SVR_SOC_VER(svr) == SVR_P5021) && (SVR_REV(svr) <= 0x21)) || ((SVR_SOC_VER(svr) == SVR_P5040) && (SVR_REV(svr) <= 0x21)))) return; +#endif sdhci_reset(host, SDHCI_RESET_DATA); @@ -316,7 +349,8 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) static int esdhc_of_enable_dma(struct sdhci_host *host) { - setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); + esdhc_writel(host, esdhc_readl(host, ESDHC_DMA_SYSCTL) + | ESDHC_DMA_SNOOP, ESDHC_DMA_SYSCTL); return 0; } @@ -352,12 +386,12 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) static u32 esdhc_proctl; static void esdhc_of_suspend(struct sdhci_host *host) { - esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); + esdhc_proctl = esdhc_readl(host, SDHCI_HOST_CONTROL); } static void esdhc_of_resume(struct sdhci_host *host) { - sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); + esdhc_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); } #endif @@ -389,8 +423,10 @@ static void esdhc_of_platform_init(struct sdhci_host *host) { u32 vvn; +#if defined CONFIG_PPC_OF svr = mfspr(SPRN_SVR); - vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); +#endif + vvn = esdhc_readl(host, SDHCI_SLOT_INT_STATUS); vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; if (vvn == VENDOR_V_22) host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; @@ -398,6 +434,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host) if (vvn > VENDOR_V_22) host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; +#if defined CONFIG_PPC_OF /* * Check for A-005055: A glitch is generated on the card clock * due to software reset or a clock change @@ -424,6 +461,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host) ((SVR_SOC_VER(svr) == SVR_P1014) && (SVR_REV(svr) == 0x10)) || ((SVR_SOC_VER(svr) == SVR_P1010) && (SVR_REV(svr) == 0x10))) host->quirks2 |= SDHCI_QUIRK2_DISABLE_CLOCK_BEFORE_RESET; +#endif } /* Return: 1 - the card is present; 0 - card is absent */ @@ -437,18 +475,18 @@ static int esdhc_of_get_cd(struct sdhci_host *host) if (host->quirks2 & SDHCI_QUIRK2_FORCE_CMD13_DETECT_CARD) return -ENOSYS; - sysctl = sdhci_be32bs_readl(host, SDHCI_CLOCK_CONTROL); + sysctl = sdhci_32bs_readl(host, SDHCI_CLOCK_CONTROL); /* Enable the controller clock to update the present state */ - sdhci_be32bs_writel(host, sysctl | SDHCI_CLOCK_INT_EN, + sdhci_32bs_writel(host, sysctl | SDHCI_CLOCK_INT_EN, SDHCI_CLOCK_CONTROL); /* Detect the card present or absent */ - present = sdhci_be32bs_readl(host, SDHCI_PRESENT_STATE); + present = sdhci_32bs_readl(host, SDHCI_PRESENT_STATE); present &= (SDHCI_CARD_PRESENT | SDHCI_CARD_CDPL); /* Resave the previous to System control register */ - sdhci_be32bs_writel(host, sysctl, SDHCI_CLOCK_CONTROL); + sdhci_32bs_writel(host, sysctl, SDHCI_CLOCK_CONTROL); return !!present; } @@ -486,8 +524,8 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) break; } - clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, - ESDHC_CTRL_BUSWIDTH_MASK, ctrl); + sdhci_clrsetbits(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl, + SDHCI_HOST_CONTROL); return 0; } @@ -528,19 +566,18 @@ static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { .ops = &sdhci_esdhc_ops, }; -static int sdhci_esdhc_probe(struct platform_device *pdev) +static void esdhc_get_property(struct platform_device *pdev) { - struct sdhci_host *host; - struct device_node *np; - int ret; - - host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); - if (IS_ERR(host)) - return PTR_ERR(host); + struct device_node *np = pdev->dev.of_node; + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); sdhci_get_of_property(pdev); - np = pdev->dev.of_node; + /* call to generic mmc_of_parse to support additional capabilities */ + mmc_of_parse(host->mmc); + mmc_of_parse_voltage(np, &host->ocr_mask); + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { /* * Freescale messed up with P2020 as it has a non-standard @@ -549,10 +586,27 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL; } - /* call to generic mmc_of_parse to support additional capabilities */ - mmc_of_parse(host->mmc); - mmc_of_parse_voltage(np, &host->ocr_mask); + if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + + if (!pltfm_host->clock) { + pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); + pltfm_host->clock = clk_get_rate(pltfm_host->clk); + clk_prepare_enable(pltfm_host->clk); + } +} + +static int sdhci_esdhc_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + int ret; + + + host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); + if (IS_ERR(host)) + return PTR_ERR(host); + esdhc_get_property(pdev); ret = sdhci_add_host(host); if (ret) sdhci_pltfm_free(pdev); diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 57c514a..3605665 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -35,26 +35,26 @@ static void sdhci_hlwd_writel(struct sdhci_host *host, u32 val, int reg) { - sdhci_be32bs_writel(host, val, reg); + sdhci_32bs_writel(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static void sdhci_hlwd_writew(struct sdhci_host *host, u16 val, int reg) { - sdhci_be32bs_writew(host, val, reg); + sdhci_32bs_writew(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static void sdhci_hlwd_writeb(struct sdhci_host *host, u8 val, int reg) { - sdhci_be32bs_writeb(host, val, reg); + sdhci_32bs_writeb(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static const struct sdhci_ops sdhci_hlwd_ops = { - .read_l = sdhci_be32bs_readl, - .read_w = sdhci_be32bs_readw, - .read_b = sdhci_be32bs_readb, + .read_l = sdhci_32bs_readl, + .read_w = sdhci_32bs_readw, + .read_b = sdhci_32bs_readb, .write_l = sdhci_hlwd_writel, .write_w = sdhci_hlwd_writew, .write_b = sdhci_hlwd_writeb, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index f0b2030..2512c11 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -135,6 +135,7 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, { struct sdhci_host *host; struct device_node *np = pdev->dev.of_node; + struct sdhci_pltfm_host *pltfm_host; struct resource *iomem; int ret; @@ -160,6 +161,14 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, goto err; } + pltfm_host = sdhci_priv(host); + pltfm_host->endian_mode = BIG_ENDIAN_MODE; + +#ifdef CONFIG_OF + if (of_get_property(np, "little-endian", NULL)) + pltfm_host->endian_mode = LITTLE_ENDIAN_MODE; +#endif /* CONFIG_OF */ + host->hw_name = dev_name(&pdev->dev); if (pdata && pdata->ops) host->ops = pdata->ops; @@ -245,7 +254,7 @@ EXPORT_SYMBOL_GPL(sdhci_pltfm_register); int sdhci_pltfm_unregister(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); + int dead = (sdhci_readl(host, SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); sdhci_pltfm_free(pdev); diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index e15ced79..c31aa94 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -28,6 +28,10 @@ struct sdhci_pltfm_host { /* migrate from sdhci_of_host */ unsigned int clock; u16 xfer_mode_shadow; + enum endian_mode { + LITTLE_ENDIAN_MODE, + BIG_ENDIAN_MODE, + } endian_mode; unsigned long private[0] ____cacheline_aligned; }; @@ -37,33 +41,61 @@ struct sdhci_pltfm_host { * These accessors are designed for big endian hosts doing I/O to * little endian controllers incorporating a 32-bit hardware byte swapper. */ -static inline u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg) +static inline void sdhci_clrsetbits(struct sdhci_host *host, u32 mask, + u32 val, int reg) { - return in_be32(host->ioaddr + reg); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + void __iomem *base = host->ioaddr + (reg & ~0x3); + u32 shift = (reg & 0x3) * 8; + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(((ioread32be(base) & ~(mask << shift)) | + (val << shift)), base); + else + iowrite32(((ioread32(base) & ~(mask << shift)) | + (val << shift)), base); } -static inline u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg) +static inline u32 sdhci_32bs_readl(struct sdhci_host *host, int reg) { - return in_be16(host->ioaddr + (reg ^ 0x2)); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + return ioread32be(host->ioaddr + reg); + else + return ioread32(host->ioaddr + reg); } -static inline u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg) +static inline u16 sdhci_32bs_readw(struct sdhci_host *host, int reg) { - return in_8(host->ioaddr + (reg ^ 0x3)); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + return ioread16be(host->ioaddr + (reg ^ 0x2)); + else + return ioread16(host->ioaddr + (reg ^ 0x2)); } -static inline void sdhci_be32bs_writel(struct sdhci_host *host, - u32 val, int reg) +static inline u8 sdhci_32bs_readb(struct sdhci_host *host, int reg) { - out_be32(host->ioaddr + reg, val); + return ioread8(host->ioaddr + (reg ^ 0x3)); } -static inline void sdhci_be32bs_writew(struct sdhci_host *host, +static inline void sdhci_32bs_writel(struct sdhci_host *host, + u32 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(val, host->ioaddr + reg); + else + iowrite32(val, host->ioaddr + reg); +} + +static inline void sdhci_32bs_writew(struct sdhci_host *host, u16 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - int base = reg & ~0x3; - int shift = (reg & 0x2) * 8; switch (reg) { case SDHCI_TRANSFER_MODE: @@ -74,20 +106,20 @@ static inline void sdhci_be32bs_writew(struct sdhci_host *host, pltfm_host->xfer_mode_shadow = val; return; case SDHCI_COMMAND: - sdhci_be32bs_writel(host, - val << 16 | pltfm_host->xfer_mode_shadow, - SDHCI_TRANSFER_MODE); + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(val << 16 | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); + else + iowrite32(val << 16 | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); return; } - clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift); + sdhci_clrsetbits(host, 0xffff, val, reg); } -static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg) +static inline void sdhci_32bs_writeb(struct sdhci_host *host, u8 val, int reg) { - int base = reg & ~0x3; - int shift = (reg & 0x3) * 8; - - clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift); + sdhci_clrsetbits(host, 0xff, val, reg); } #endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */ diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5fab4e6e..8adb5af 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -320,6 +320,8 @@ source "drivers/mtd/onenand/Kconfig" source "drivers/mtd/lpddr/Kconfig" +source "drivers/mtd/spi-nor/Kconfig" + source "drivers/mtd/ubi/Kconfig" endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4cfb31e..40fd153 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ +obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor/ obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 74ab4b7..004b17b 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP config MTD_M25P80 tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" - depends on SPI_MASTER + depends on SPI_MASTER && MTD_SPI_NOR_BASE help This enables access to most modern SPI flash chips, used for program and data storage. Series supported include Atmel AT26DF, @@ -95,13 +95,6 @@ config MTD_M25P80 if you want to specify device partitioning or to use a device which doesn't support the JEDEC ID instruction. -config M25PXX_USE_FAST_READ - bool "Use FAST_READ OPCode allowing SPI CLK >= 50MHz" - depends on MTD_M25P80 - default y - help - This option enables FAST_READ access supported by ST M25Pxx. - config MTD_SPEAR_SMI tristate "SPEAR MTD NOR Support through SMI controller" depends on PLAT_SPEAR diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index dfa5af13..92a14fb 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -15,922 +15,178 @@ * */ -#include <linux/init.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/mutex.h> -#include <linux/math64.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/mod_devicetable.h> -#include <linux/mtd/cfi.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> #include <linux/spi/spi.h> #include <linux/spi/flash.h> +#include <linux/mtd/spi-nor.h> -/* Flash opcodes. */ -#define OPCODE_WREN 0x06 /* Write enable */ -#define OPCODE_RDSR 0x05 /* Read status register */ -#define OPCODE_WRSR 0x01 /* Write status register 1 byte */ -#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ -#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ -#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ -#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ -#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ -#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ -#define OPCODE_RDID 0x9f /* Read JEDEC ID */ - -/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ -#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ -#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ -#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ - -/* Used for SST flashes only. */ -#define OPCODE_BP 0x02 /* Byte program */ -#define OPCODE_WRDI 0x04 /* Write disable */ -#define OPCODE_AAI_WP 0xad /* Auto address increment word program */ - -/* Used for Macronix and Winbond flashes. */ -#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ -#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ - -/* Used for Spansion flashes only. */ -#define OPCODE_BRWR 0x17 /* Bank register write */ - -/* Status Register bits. */ -#define SR_WIP 1 /* Write in progress */ -#define SR_WEL 2 /* Write enable latch */ -/* meaning of other SR_* bits may differ between vendors */ -#define SR_BP0 4 /* Block protect 0 */ -#define SR_BP1 8 /* Block protect 1 */ -#define SR_BP2 0x10 /* Block protect 2 */ -#define SR_SRWD 0x80 /* SR write protect */ - -/* Define max times to check status register before we give up. */ -#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ #define MAX_CMD_SIZE 6 - -#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) - -/****************************************************************************/ - struct m25p { struct spi_device *spi; - struct mutex lock; + struct spi_nor spi_nor; struct mtd_info mtd; - u16 page_size; - u16 addr_width; - u8 erase_opcode; - u8 read_opcode; - u8 program_opcode; - u8 *command; - bool fast_read; + u8 command[MAX_CMD_SIZE]; }; -static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) +static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) { - return container_of(mtd, struct m25p, mtd); -} - -/****************************************************************************/ - -/* - * Internal helper functions - */ - -/* - * Read the status register, returning its value in the location - * Return the status register value. - * Returns negative if error occurred. - */ -static int read_sr(struct m25p *flash) -{ - ssize_t retval; - u8 code = OPCODE_RDSR; - u8 val; - - retval = spi_write_then_read(flash->spi, &code, 1, &val, 1); + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; + int ret; - if (retval < 0) { - dev_err(&flash->spi->dev, "error %d reading SR\n", - (int) retval); - return retval; - } - - return val; -} - -/* - * Write status register 1 byte - * Returns negative if error occurred. - */ -static int write_sr(struct m25p *flash, u8 val) -{ - flash->command[0] = OPCODE_WRSR; - flash->command[1] = val; - - return spi_write(flash->spi, flash->command, 2); -} - -/* - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. - */ -static inline int write_enable(struct m25p *flash) -{ - u8 code = OPCODE_WREN; - - return spi_write_then_read(flash->spi, &code, 1, NULL, 0); -} - -/* - * Send write disble instruction to the chip. - */ -static inline int write_disable(struct m25p *flash) -{ - u8 code = OPCODE_WRDI; + ret = spi_write_then_read(spi, &code, 1, val, len); + if (ret < 0) + dev_err(&spi->dev, "error %d reading %x\n", ret, code); - return spi_write_then_read(flash->spi, &code, 1, NULL, 0); + return ret; } -/* - * Enable/disable 4-byte addressing mode. - */ -static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) +static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd) { - int status; - bool need_wren = false; - - switch (JEDEC_MFR(jedec_id)) { - case CFI_MFR_ST: /* Micron, actually */ - /* Some Micron need WREN command; all will accept it */ - need_wren = true; - case CFI_MFR_MACRONIX: - case 0xEF /* winbond */: - if (need_wren) - write_enable(flash); - - flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; - status = spi_write(flash->spi, flash->command, 1); - - if (need_wren) - write_disable(flash); - - return status; - default: - /* Spansion style */ - flash->command[0] = OPCODE_BRWR; - flash->command[1] = enable << 7; - return spi_write(flash->spi, flash->command, 2); - } + /* opcode is in cmd[0] */ + cmd[1] = addr >> (nor->addr_width * 8 - 8); + cmd[2] = addr >> (nor->addr_width * 8 - 16); + cmd[3] = addr >> (nor->addr_width * 8 - 24); + cmd[4] = addr >> (nor->addr_width * 8 - 32); } -/* - * Service routine to read status register until ready, or timeout occurs. - * Returns non-zero if error. - */ -static int wait_till_ready(struct m25p *flash) +static int m25p_cmdsz(struct spi_nor *nor) { - unsigned long deadline; - int sr; - - deadline = jiffies + MAX_READY_WAIT_JIFFIES; - - do { - if ((sr = read_sr(flash)) < 0) - break; - else if (!(sr & SR_WIP)) - return 0; - - cond_resched(); - - } while (!time_after_eq(jiffies, deadline)); - - return 1; + return 1 + nor->addr_width; } -/* - * Erase the whole flash memory - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_chip(struct m25p *flash) +static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int wr_en) { - pr_debug("%s: %s %lldKiB\n", dev_name(&flash->spi->dev), __func__, - (long long)(flash->mtd.size >> 10)); + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) - return 1; - - /* Send write enable, then erase commands. */ - write_enable(flash); - - /* Set up command buffer. */ - flash->command[0] = OPCODE_CHIP_ERASE; - - spi_write(flash->spi, flash->command, 1); + flash->command[0] = opcode; + if (buf) + memcpy(&flash->command[1], buf, len); - return 0; + return spi_write(spi, flash->command, len + 1); } -static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) +static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, + size_t *retlen, const u_char *buf) { - /* opcode is in cmd[0] */ - cmd[1] = addr >> (flash->addr_width * 8 - 8); - cmd[2] = addr >> (flash->addr_width * 8 - 16); - cmd[3] = addr >> (flash->addr_width * 8 - 24); - cmd[4] = addr >> (flash->addr_width * 8 - 32); -} + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; + struct spi_transfer t[2] = {}; + struct spi_message m; + int cmd_sz = m25p_cmdsz(nor); -static int m25p_cmdsz(struct m25p *flash) -{ - return 1 + flash->addr_width; -} + spi_message_init(&m); -/* - * Erase one sector of flash memory at offset ``offset'' which is any - * address within the sector which should be erased. - * - * Returns 0 if successful, non-zero otherwise. - */ -static int erase_sector(struct m25p *flash, u32 offset) -{ - pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev), - __func__, flash->mtd.erasesize / 1024, offset); + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) + cmd_sz = 1; - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) - return 1; + flash->command[0] = nor->program_opcode; + m25p_addr2cmd(nor, to, flash->command); - /* Send write enable, then erase commands. */ - write_enable(flash); + t[0].tx_buf = flash->command; + t[0].len = cmd_sz; + spi_message_add_tail(&t[0], &m); - /* Set up command buffer. */ - flash->command[0] = flash->erase_opcode; - m25p_addr2cmd(flash, offset, flash->command); + t[1].tx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); - spi_write(flash->spi, flash->command, m25p_cmdsz(flash)); + spi_sync(spi, &m); - return 0; + *retlen += m.actual_length - cmd_sz; } -/****************************************************************************/ - -/* - * MTD implementation - */ - -/* - * Erase an address range on the flash chip. The address range may extend - * one or more erase sectors. Return an error is there is a problem erasing. - */ -static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) +static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor) { - struct m25p *flash = mtd_to_m25p(mtd); - u32 addr,len; - uint32_t rem; - - pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev), - __func__, (long long)instr->addr, - (long long)instr->len); - - div_u64_rem(instr->len, mtd->erasesize, &rem); - if (rem) - return -EINVAL; - - addr = instr->addr; - len = instr->len; - - mutex_lock(&flash->lock); - - /* whole-chip erase? */ - if (len == flash->mtd.size) { - if (erase_chip(flash)) { - instr->state = MTD_ERASE_FAILED; - mutex_unlock(&flash->lock); - return -EIO; - } - - /* REVISIT in some cases we could speed up erasing large regions - * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up - * to use "small sector erase", but that's not always optimal. - */ - - /* "sector"-at-a-time erase */ - } else { - while (len) { - if (erase_sector(flash, addr)) { - instr->state = MTD_ERASE_FAILED; - mutex_unlock(&flash->lock); - return -EIO; - } - - addr += mtd->erasesize; - len -= mtd->erasesize; - } + switch (nor->flash_read) { + case SPI_NOR_DUAL: + return 2; + case SPI_NOR_QUAD: + return 4; + default: + return 0; } - - mutex_unlock(&flash->lock); - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; } /* - * Read an address range from the flash chip. The address range + * Read an address range from the nor chip. The address range * may be any size provided it is within the physical boundaries. */ -static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, + size_t *retlen, u_char *buf) { - struct m25p *flash = mtd_to_m25p(mtd); + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; struct spi_transfer t[2]; struct spi_message m; - uint8_t opcode; - - pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)from, len); - - spi_message_init(&m); - memset(t, 0, (sizeof t)); - - /* NOTE: - * OPCODE_FAST_READ (if available) is faster. - * Should add 1 byte DUMMY_BYTE. - */ - t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0); - spi_message_add_tail(&t[0], &m); - - t[1].rx_buf = buf; - t[1].len = len; - spi_message_add_tail(&t[1], &m); + unsigned int dummy = nor->read_dummy; + int ret; - mutex_lock(&flash->lock); + /* convert the dummy cycles to the number of bytes */ + dummy /= 8; /* Wait till previous write/erase is done. */ - if (wait_till_ready(flash)) { - /* REVISIT status return?? */ - mutex_unlock(&flash->lock); - return 1; - } - - /* FIXME switch to OPCODE_FAST_READ. It's required for higher - * clocks; and at this writing, every chip this driver handles - * supports that opcode. - */ - - /* Set up the write data buffer. */ - opcode = flash->read_opcode; - flash->command[0] = opcode; - m25p_addr2cmd(flash, from, flash->command); - - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash) - - (flash->fast_read ? 1 : 0); - - mutex_unlock(&flash->lock); - - return 0; -} - -/* - * Write an address range to the flash chip. Data must be written in - * FLASH_PAGESIZE chunks. The address range may be any size provided - * it is within the physical boundaries. - */ -static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct m25p *flash = mtd_to_m25p(mtd); - u32 page_offset, page_size; - struct spi_transfer t[2]; - struct spi_message m; - - pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)to, len); + ret = nor->wait_till_ready(nor); + if (ret) + return ret; spi_message_init(&m); memset(t, 0, (sizeof t)); + flash->command[0] = nor->read_opcode; + m25p_addr2cmd(nor, from, flash->command); + t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash); + t[0].len = m25p_cmdsz(nor) + dummy; spi_message_add_tail(&t[0], &m); - t[1].tx_buf = buf; + t[1].rx_buf = buf; + t[1].rx_nbits = m25p80_rx_nbits(nor); + t[1].len = len; spi_message_add_tail(&t[1], &m); - mutex_lock(&flash->lock); - - /* Wait until finished previous write command. */ - if (wait_till_ready(flash)) { - mutex_unlock(&flash->lock); - return 1; - } - - write_enable(flash); - - /* Set up the opcode in the write buffer. */ - flash->command[0] = flash->program_opcode; - m25p_addr2cmd(flash, to, flash->command); - - page_offset = to & (flash->page_size - 1); - - /* do all the bytes fit onto one page? */ - if (page_offset + len <= flash->page_size) { - t[1].len = len; - - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash); - } else { - u32 i; - - /* the size of data remaining on the first page */ - page_size = flash->page_size - page_offset; - - t[1].len = page_size; - spi_sync(flash->spi, &m); - - *retlen = m.actual_length - m25p_cmdsz(flash); - - /* write everything in flash->page_size chunks */ - for (i = page_size; i < len; i += page_size) { - page_size = len - i; - if (page_size > flash->page_size) - page_size = flash->page_size; - - /* write the next page to flash */ - m25p_addr2cmd(flash, to + i, flash->command); - - t[1].tx_buf = buf + i; - t[1].len = page_size; - - wait_till_ready(flash); - - write_enable(flash); - - spi_sync(flash->spi, &m); - - *retlen += m.actual_length - m25p_cmdsz(flash); - } - } - - mutex_unlock(&flash->lock); + spi_sync(spi, &m); + *retlen = m.actual_length - m25p_cmdsz(nor) - dummy; return 0; } -static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +static int m25p80_erase(struct spi_nor *nor, loff_t offset) { - struct m25p *flash = mtd_to_m25p(mtd); - struct spi_transfer t[2]; - struct spi_message m; - size_t actual; - int cmd_sz, ret; - - pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev), - __func__, (u32)to, len); + struct m25p *flash = nor->priv; + int ret; - spi_message_init(&m); - memset(t, 0, (sizeof t)); - - t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash); - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - spi_message_add_tail(&t[1], &m); - - mutex_lock(&flash->lock); + dev_dbg(nor->dev, "%dKiB at 0x%08x\n", + flash->mtd.erasesize / 1024, (u32)offset); /* Wait until finished previous write command. */ - ret = wait_till_ready(flash); + ret = nor->wait_till_ready(nor); if (ret) - goto time_out; - - write_enable(flash); - - actual = to % 2; - /* Start write from odd address. */ - if (actual) { - flash->command[0] = OPCODE_BP; - m25p_addr2cmd(flash, to, flash->command); + return ret; - /* write one byte. */ - t[1].len = 1; - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - m25p_cmdsz(flash); - } - to += actual; - - flash->command[0] = OPCODE_AAI_WP; - m25p_addr2cmd(flash, to, flash->command); - - /* Write out most of the data here. */ - cmd_sz = m25p_cmdsz(flash); - for (; actual < len - 1; actual += 2) { - t[0].len = cmd_sz; - /* write two bytes. */ - t[1].len = 2; - t[1].tx_buf = buf + actual; - - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - cmd_sz; - cmd_sz = 1; - to += 2; - } - write_disable(flash); - ret = wait_till_ready(flash); + /* Send write enable, then erase commands. */ + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); if (ret) - goto time_out; - - /* Write out trailing byte if it exists. */ - if (actual != len) { - write_enable(flash); - flash->command[0] = OPCODE_BP; - m25p_addr2cmd(flash, to, flash->command); - t[0].len = m25p_cmdsz(flash); - t[1].len = 1; - t[1].tx_buf = buf + actual; - - spi_sync(flash->spi, &m); - ret = wait_till_ready(flash); - if (ret) - goto time_out; - *retlen += m.actual_length - m25p_cmdsz(flash); - write_disable(flash); - } - -time_out: - mutex_unlock(&flash->lock); - return ret; -} - -static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct m25p *flash = mtd_to_m25p(mtd); - uint32_t offset = ofs; - uint8_t status_old, status_new; - int res = 0; - - mutex_lock(&flash->lock); - /* Wait until finished previous command */ - if (wait_till_ready(flash)) { - res = 1; - goto err; - } - - status_old = read_sr(flash); - - if (offset < flash->mtd.size-(flash->mtd.size/2)) - status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/4)) - status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; - else if (offset < flash->mtd.size-(flash->mtd.size/8)) - status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/16)) - status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; - else if (offset < flash->mtd.size-(flash->mtd.size/32)) - status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; - else if (offset < flash->mtd.size-(flash->mtd.size/64)) - status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; - else - status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; - - /* Only modify protection if it will not unlock other areas */ - if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) > - (status_old&(SR_BP2|SR_BP1|SR_BP0))) { - write_enable(flash); - if (write_sr(flash, status_new) < 0) { - res = 1; - goto err; - } - } - -err: mutex_unlock(&flash->lock); - return res; -} - -static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct m25p *flash = mtd_to_m25p(mtd); - uint32_t offset = ofs; - uint8_t status_old, status_new; - int res = 0; - - mutex_lock(&flash->lock); - /* Wait until finished previous command */ - if (wait_till_ready(flash)) { - res = 1; - goto err; - } - - status_old = read_sr(flash); - - if (offset+len > flash->mtd.size-(flash->mtd.size/64)) - status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0); - else if (offset+len > flash->mtd.size-(flash->mtd.size/32)) - status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; - else if (offset+len > flash->mtd.size-(flash->mtd.size/16)) - status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; - else if (offset+len > flash->mtd.size-(flash->mtd.size/8)) - status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; - else if (offset+len > flash->mtd.size-(flash->mtd.size/4)) - status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; - else if (offset+len > flash->mtd.size-(flash->mtd.size/2)) - status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; - else - status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; - - /* Only modify protection if it will not lock other areas */ - if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) < - (status_old&(SR_BP2|SR_BP1|SR_BP0))) { - write_enable(flash); - if (write_sr(flash, status_new) < 0) { - res = 1; - goto err; - } - } - -err: mutex_unlock(&flash->lock); - return res; -} - -/****************************************************************************/ - -/* - * SPI device driver setup and teardown - */ - -struct flash_info { - /* JEDEC id zero means "no ID" (most older chips); otherwise it has - * a high byte of zero plus three data bytes: the manufacturer id, - * then a two byte device id. - */ - u32 jedec_id; - u16 ext_id; - - /* The size listed here is what works with OPCODE_SE, which isn't - * necessarily called a "sector" by the vendor. - */ - unsigned sector_size; - u16 n_sectors; - - u16 page_size; - u16 addr_width; - - u16 flags; -#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ -#define M25P_NO_ERASE 0x02 /* No erase command needed */ -#define SST_WRITE 0x04 /* use SST byte programming */ -#define M25P_NO_FR 0x08 /* Can't do fastread */ -#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ -}; - -#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ - .jedec_id = (_jedec_id), \ - .ext_id = (_ext_id), \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ - .flags = (_flags), \ - }) - -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ - ((kernel_ulong_t)&(struct flash_info) { \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = (_page_size), \ - .addr_width = (_addr_width), \ - .flags = (_flags), \ - }) + return ret; -/* NOTE: double check command sets and memory organization when you add - * more flash chips. This current list focusses on newer chips, which - * have been converging on command sets which including JEDEC ID. - */ -static const struct spi_device_id m25p_ids[] = { - /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, - { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, - - { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, - { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, - { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, - - { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, - { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, - { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, - { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, - - { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, - - /* EON -- en25xxx */ - { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, - { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, - { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, - { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, - { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, - { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, 0) }, - { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, - - /* Everspin */ - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, - - /* GigaDevice */ - { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, - { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, - - /* Intel/Numonyx -- xxxs33b */ - { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, - { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, - { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, - - /* Macronix */ - { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, - { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, - { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, - { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, - { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, - { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, - { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, - { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, - { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, - { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, - { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, 0) }, - - /* Micron */ - { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, - { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, - { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, - { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, - { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, - { "n25q512a", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K) }, - - /* PMC */ - { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, - { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, - { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, - - /* Spansion -- single (large) sector size only, at least - * for the chips listed here (without boot sectors). - */ - { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, - { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, - { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, - { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, 0) }, - { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) }, - { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, - { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, - { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, - { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, - { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, - { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, - { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, - { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, - { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, - { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, - { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, - { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - - /* SST -- large erase sizes are "overlays", "sectors" are 4K */ - { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, - { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, - { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, - { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, - { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, - { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, - { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, - { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, - { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, - - /* ST Microelectronics -- newer production may have feature updates */ - { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, - { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, - { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, - { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, - { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, - { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, - { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, - { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, - { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, - { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, - - { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, - { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, - { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, - { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, - { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, - { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, - { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, - { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, - { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, - - { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, - { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, - { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, - - { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, - { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, - { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, - - { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, - - /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ - { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, - { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, - { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, - { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, - { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, - { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, - { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, - { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, - - /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) }, - { }, -}; -MODULE_DEVICE_TABLE(spi, m25p_ids); - -static const struct spi_device_id *jedec_probe(struct spi_device *spi) -{ - int tmp; - u8 code = OPCODE_RDID; - u8 id[5]; - u32 jedec; - u16 ext_jedec; - struct flash_info *info; - - /* JEDEC also defines an optional "extended device information" - * string for after vendor-specific data, after the three bytes - * we use here. Supporting some chips might require using it. - */ - tmp = spi_write_then_read(spi, &code, 1, id, 5); - if (tmp < 0) { - pr_debug("%s: error %d reading JEDEC ID\n", - dev_name(&spi->dev), tmp); - return ERR_PTR(tmp); - } - jedec = id[0]; - jedec = jedec << 8; - jedec |= id[1]; - jedec = jedec << 8; - jedec |= id[2]; + /* Set up command buffer. */ + flash->command[0] = nor->erase_opcode; + m25p_addr2cmd(nor, offset, flash->command); - ext_jedec = id[3] << 8 | id[4]; + spi_write(flash->spi, flash->command, m25p_cmdsz(nor)); - for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { - info = (void *)m25p_ids[tmp].driver_data; - if (info->jedec_id == jedec) { - if (info->ext_id != 0 && info->ext_id != ext_jedec) - continue; - return &m25p_ids[tmp]; - } - } - dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); - return ERR_PTR(-ENODEV); + return 0; } - /* * board specific setup should have ensured the SPI clock used here * matches what the READ command supports, at least until this driver @@ -938,205 +194,46 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi) */ static int m25p_probe(struct spi_device *spi) { - const struct spi_device_id *id = spi_get_device_id(spi); - struct flash_platform_data *data; - struct m25p *flash; - struct flash_info *info; - unsigned i, ret; struct mtd_part_parser_data ppdata; - struct device_node __maybe_unused *np = spi->dev.of_node; - struct resource res; - struct device_node *mnp = spi->master->dev.of_node; - -#ifdef CONFIG_MTD_OF_PARTS - if (!of_device_is_available(np)) - return -ENODEV; -#endif - - /* Platform data helps sort out which chip type we have, as - * well as how this board partitions it. If we don't have - * a chip ID, try the JEDEC id commands; they'll work for most - * newer chips, even if we don't recognize the particular chip. - */ - data = dev_get_platdata(&spi->dev); - if (data && data->type) { - const struct spi_device_id *plat_id; - - for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) { - plat_id = &m25p_ids[i]; - if (strcmp(data->type, plat_id->name)) - continue; - break; - } - - if (i < ARRAY_SIZE(m25p_ids) - 1) - id = plat_id; - else - dev_warn(&spi->dev, "unrecognized id %s\n", data->type); - } - - info = (void *)id->driver_data; - - if (info->jedec_id) { - const struct spi_device_id *jid; - - jid = jedec_probe(spi); - if (IS_ERR(jid)) { - return PTR_ERR(jid); - } else if (jid != id) { - /* - * JEDEC knows better, so overwrite platform ID. We - * can't trust partitions any longer, but we'll let - * mtd apply them anyway, since some partitions may be - * marked read-only, and we don't want to lose that - * information, even if it's not 100% accurate. - */ - dev_warn(&spi->dev, "found %s, expected %s\n", - jid->name, id->name); - id = jid; - info = (void *)jid->driver_data; - } - } + struct flash_platform_data *data; + struct m25p *flash; + struct spi_nor *nor; + enum read_mode mode = SPI_NOR_NORMAL; + int ret; flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); if (!flash) return -ENOMEM; - flash->command = devm_kzalloc(&spi->dev, MAX_CMD_SIZE, GFP_KERNEL); - if (!flash->command) - return -ENOMEM; + nor = &flash->spi_nor; - flash->spi = spi; - mutex_init(&flash->lock); - spi_set_drvdata(spi, flash); - - /* - * Atmel, SST and Intel/Numonyx serial flash tend to power - * up with the software protection bits set - */ - - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || - JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || - JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { - write_enable(flash); - write_sr(flash, 0); - } - - if (data && data->name) - flash->mtd.name = data->name; - else { - ret = of_address_to_resource(mnp, 0, &res); - if (ret) { - dev_err(&spi->dev, "failed to get spi master resource\n"); - return ret; - } - flash->mtd.name = kasprintf(GFP_KERNEL, "spi%x.%d", - (unsigned)res.start, spi->chip_select); - if (!flash->mtd.name) - return -ENOMEM; - } - - flash->mtd.type = MTD_NORFLASH; - flash->mtd.writesize = 1; - flash->mtd.flags = MTD_CAP_NORFLASH; - flash->mtd.size = info->sector_size * info->n_sectors; - flash->mtd._erase = m25p80_erase; - flash->mtd._read = m25p80_read; - - /* flash protection support for STmicro chips */ - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { - flash->mtd._lock = m25p80_lock; - flash->mtd._unlock = m25p80_unlock; - } + /* install the hooks */ + nor->read = m25p80_read; + nor->write = m25p80_write; + nor->erase = m25p80_erase; + nor->write_reg = m25p80_write_reg; + nor->read_reg = m25p80_read_reg; - /* sst flash chips use AAI word program */ - if (info->flags & SST_WRITE) - flash->mtd._write = sst_write; - else - flash->mtd._write = m25p80_write; + nor->dev = &spi->dev; + nor->np = spi->dev.of_node; + nor->mtd = &flash->mtd; + nor->priv = flash; - /* prefer "small sector" erase if possible */ - if (info->flags & SECT_4K) { - flash->erase_opcode = OPCODE_BE_4K; - flash->mtd.erasesize = 4096; - } else if (info->flags & SECT_4K_PMC) { - flash->erase_opcode = OPCODE_BE_4K_PMC; - flash->mtd.erasesize = 4096; - } else { - flash->erase_opcode = OPCODE_SE; - flash->mtd.erasesize = info->sector_size; - } + spi_set_drvdata(spi, flash); + flash->mtd.priv = nor; + flash->spi = spi; - if (info->flags & M25P_NO_ERASE) - flash->mtd.flags |= MTD_NO_ERASE; + if (spi->mode & SPI_RX_QUAD) + mode = SPI_NOR_QUAD; + else if (spi->mode & SPI_RX_DUAL) + mode = SPI_NOR_DUAL; + ret = spi_nor_scan(nor, spi_get_device_id(spi), mode); + if (ret) + return ret; + data = dev_get_platdata(&spi->dev); ppdata.of_node = spi->dev.of_node; - flash->mtd.dev.parent = &spi->dev; - flash->page_size = info->page_size; - flash->mtd.writebufsize = flash->page_size; - - flash->fast_read = false; - if (np && of_property_read_bool(np, "m25p,fast-read")) - flash->fast_read = true; - -#ifdef CONFIG_M25PXX_USE_FAST_READ - flash->fast_read = true; -#endif - if (info->flags & M25P_NO_FR) - flash->fast_read = false; - - /* Default commands */ - if (flash->fast_read) - flash->read_opcode = OPCODE_FAST_READ; - else - flash->read_opcode = OPCODE_NORM_READ; - - flash->program_opcode = OPCODE_PP; - if (info->addr_width) - flash->addr_width = info->addr_width; - else if (flash->mtd.size > 0x1000000) { - /* enable 4-byte addressing if the device exceeds 16MiB */ - flash->addr_width = 4; - if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { - /* Dedicated 4-byte command set */ - flash->read_opcode = flash->fast_read ? - OPCODE_FAST_READ_4B : - OPCODE_NORM_READ_4B; - flash->program_opcode = OPCODE_PP_4B; - /* No small sector erase for 4-byte command set */ - flash->erase_opcode = OPCODE_SE_4B; - flash->mtd.erasesize = info->sector_size; - } else - set_4byte(flash, info->jedec_id, 1); - } else { - flash->addr_width = 3; - } - - dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, - (long long)flash->mtd.size >> 10); - - pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) " - ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", - flash->mtd.name, - (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), - flash->mtd.erasesize, flash->mtd.erasesize / 1024, - flash->mtd.numeraseregions); - - if (flash->mtd.numeraseregions) - for (i = 0; i < flash->mtd.numeraseregions; i++) - pr_debug("mtd.eraseregions[%d] = { .offset = 0x%llx, " - ".erasesize = 0x%.8x (%uKiB), " - ".numblocks = %d }\n", - i, (long long)flash->mtd.eraseregions[i].offset, - flash->mtd.eraseregions[i].erasesize, - flash->mtd.eraseregions[i].erasesize / 1024, - flash->mtd.eraseregions[i].numblocks); - - - /* partitions should match sector boundaries; and it may be good to - * use readonly partitions for writeprotected sectors (BP2..BP0). - */ return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, data ? data->parts : NULL, data ? data->nr_parts : 0); @@ -1148,9 +245,7 @@ static int m25p_remove(struct spi_device *spi) struct m25p *flash = spi_get_drvdata(spi); /* Clean up MTD stuff. */ - mtd_device_unregister(&flash->mtd); - - return 0; + return mtd_device_unregister(&flash->mtd); } @@ -1159,7 +254,7 @@ static struct spi_driver m25p80_driver = { .name = "m25p80", .owner = THIS_MODULE, }, - .id_table = m25p_ids, + .id_table = spi_nor_ids, .probe = m25p_probe, .remove = m25p_remove, diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 8639a42..d9341d4 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -29,7 +29,8 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <linux/mtd/nand_ecc.h> -#include <asm/fsl_ifc.h> +#include <linux/of_address.h> +#include <linux/fsl_ifc.h> #define FSL_IFC_V1_1_0 0x01010000 #define ERR_BYTE 0xFF /* Value returned for read @@ -239,8 +240,9 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) ifc_nand_ctrl->page = page_addr; /* Program ROW0/COL0 */ - iowrite32be(page_addr, &ifc->ifc_nand.row0); - iowrite32be((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); + ifc_out32(page_addr, &ifc->ifc_nand.row0); + ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, + &ifc->ifc_nand.col0); buf_num = page_addr & priv->bufnum_mask; @@ -302,19 +304,20 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) int i; /* set the chip select for NAND Transaction */ - iowrite32be(priv->bank << IFC_NAND_CSEL_SHIFT, + ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); dev_vdbg(priv->dev, "%s: fir0=%08x fcr0=%08x\n", __func__, - ioread32be(&ifc->ifc_nand.nand_fir0), - ioread32be(&ifc->ifc_nand.nand_fcr0)); + ifc_in32(&ifc->ifc_nand.nand_fir0), + ifc_in32(&ifc->ifc_nand.nand_fcr0)); ctrl->nand_stat = 0; /* start read/write seq */ - iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, + &ifc->ifc_nand.nandseq_strt); /* wait for command complete flag or timeout */ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, @@ -337,7 +340,8 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) int sector_end = sector + chip->ecc.steps - 1; for (i = sector / 4; i <= sector_end / 4; i++) - eccstat[i] = ioread32be(&ifc->ifc_nand.nand_eccstat[i]); + eccstat[i] = ifc_in32( + &ifc->ifc_nand.nand_eccstat[i]); for (i = sector; i <= sector_end; i++) { errors = check_read_ecc(mtd, ctrl, eccstat, i); @@ -377,31 +381,31 @@ static void fsl_ifc_do_read(struct nand_chip *chip, /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ if (mtd->writesize > 512) { - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); + ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); - iowrite32be((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), &ifc->ifc_nand.nand_fcr0); } else { - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); + ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); if (oob) - iowrite32be(NAND_CMD_READOOB << + ifc_out32(NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT, &ifc->ifc_nand.nand_fcr0); else - iowrite32be(NAND_CMD_READ0 << + ifc_out32(NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT, &ifc->ifc_nand.nand_fcr0); } @@ -423,7 +427,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, switch (command) { /* READ0 read the entire buffer to use hardware ECC. */ case NAND_CMD_READ0: - iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); set_addr(mtd, 0, page_addr, 0); ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; @@ -438,7 +442,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* READOOB reads only the OOB because no ECC is performed. */ case NAND_CMD_READOOB: - iowrite32be(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); + ifc_out32(mtd->oobsize - column, + &ifc->ifc_nand.nand_fbcr); set_addr(mtd, column, page_addr, 1); ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; @@ -454,19 +459,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, if (command == NAND_CMD_PARAM) timing = IFC_FIR_OP_RBCD; - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | (timing << IFC_NAND_FIR0_OP2_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be(command << IFC_NAND_FCR0_CMD0_SHIFT, + ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, &ifc->ifc_nand.nand_fcr0); - iowrite32be(column, &ifc->ifc_nand.row3); + ifc_out32(column, &ifc->ifc_nand.row3); /* * although currently it's 8 bytes for READID, we always read * the maximum 256 bytes(for PARAM) */ - iowrite32be(256, &ifc->ifc_nand.nand_fbcr); + ifc_out32(256, &ifc->ifc_nand.nand_fbcr); ifc_nand_ctrl->read_bytes = 256; set_addr(mtd, 0, 0, 0); @@ -481,16 +486,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* ERASE2 uses the block and page address from ERASE1 */ case NAND_CMD_ERASE2: - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | + ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), &ifc->ifc_nand.nand_fcr0); - iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); ifc_nand_ctrl->read_bytes = 0; fsl_ifc_run_command(mtd); return; @@ -507,14 +512,14 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); - iowrite32be( + ifc_out32( (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be( + ifc_out32( (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | @@ -528,14 +533,14 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD3_SHIFT)); - iowrite32be( + ifc_out32( (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be( + ifc_out32( (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | (IFC_FIR_OP_RDSTAT << @@ -556,7 +561,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, column -= mtd->writesize; ifc_nand_ctrl->oob = 1; } - iowrite32be(nand_fcr0, &ifc->ifc_nand.nand_fcr0); + ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); return; } @@ -564,11 +569,11 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ case NAND_CMD_PAGEPROG: { if (ifc_nand_ctrl->oob) { - iowrite32be(ifc_nand_ctrl->index - + ifc_out32(ifc_nand_ctrl->index - ifc_nand_ctrl->column, &ifc->ifc_nand.nand_fbcr); } else { - iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); } fsl_ifc_run_command(mtd); @@ -576,12 +581,12 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, } case NAND_CMD_STATUS: - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, + ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, &ifc->ifc_nand.nand_fcr0); - iowrite32be(1, &ifc->ifc_nand.nand_fbcr); + ifc_out32(1, &ifc->ifc_nand.nand_fbcr); set_addr(mtd, 0, 0, 0); ifc_nand_ctrl->read_bytes = 1; @@ -591,13 +596,14 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, * The chip always seems to report that it is * write-protected, even when it is not. */ - setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP); + ifc_out8((ifc_in8(ifc_nand_ctrl->addr) | (NAND_STATUS_WP)), + ifc_nand_ctrl->addr); return; case NAND_CMD_RESET: - iowrite32be(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, + ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, &ifc->ifc_nand.nand_fir0); - iowrite32be(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, + ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, &ifc->ifc_nand.nand_fcr0); fsl_ifc_run_command(mtd); return; @@ -654,7 +660,8 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) * next byte. */ if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) - return in_8(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]); + return ifc_in8( + &ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]); dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); return ERR_BYTE; @@ -675,7 +682,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) * next byte. */ if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { - data = in_be16((uint16_t __iomem *)&ifc_nand_ctrl-> + data = ifc_in16((uint16_t __iomem *)&ifc_nand_ctrl-> addr[ifc_nand_ctrl->index]); ifc_nand_ctrl->index += 2; return (uint8_t) data; @@ -722,18 +729,18 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) u32 nand_fsr; /* Use READ_STATUS command, but wait for the device to be ready */ - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, + ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, &ifc->ifc_nand.nand_fcr0); - iowrite32be(1, &ifc->ifc_nand.nand_fbcr); + ifc_out32(1, &ifc->ifc_nand.nand_fbcr); set_addr(mtd, 0, 0, 0); ifc_nand_ctrl->read_bytes = 1; fsl_ifc_run_command(mtd); - nand_fsr = ioread32be(&ifc->ifc_nand.nand_fsr); + nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); /* * The chip always seems to report that it is @@ -827,34 +834,36 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) uint32_t cs = priv->bank; /* Save CSOR and CSOR_ext */ - csor = ioread32be(&ifc->csor_cs[cs].csor); - csor_ext = ioread32be(&ifc->csor_cs[cs].csor_ext); + csor = ifc_in32(&ifc->csor_cs[cs].csor); + csor_ext = ifc_in32(&ifc->csor_cs[cs].csor_ext); /* chage PageSize 8K and SpareSize 1K*/ csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; - iowrite32be(csor_8k, &ifc->csor_cs[cs].csor); - iowrite32be(0x0000400, &ifc->csor_cs[cs].csor_ext); + ifc_out32(csor_8k, &ifc->csor_cs[cs].csor); + ifc_out32(0x0000400, &ifc->csor_cs[cs].csor_ext); /* READID */ - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, + ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, &ifc->ifc_nand.nand_fcr0); - iowrite32be(0x0, &ifc->ifc_nand.row3); + ifc_out32(0x0, &ifc->ifc_nand.row3); - iowrite32be(0x0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0x0, &ifc->ifc_nand.nand_fbcr); /* Program ROW0/COL0 */ - iowrite32be(0x0, &ifc->ifc_nand.row0); - iowrite32be(0x0, &ifc->ifc_nand.col0); + ifc_out32(0x0, &ifc->ifc_nand.row0); + ifc_out32(0x0, &ifc->ifc_nand.col0); /* set the chip select for NAND Transaction */ - iowrite32be(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); + ifc_out32(cs << IFC_NAND_CSEL_SHIFT, + &ifc->ifc_nand.nand_csel); /* start read seq */ - iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, + &ifc->ifc_nand.nandseq_strt); /* wait for command complete flag or timeout */ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, @@ -864,8 +873,8 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); /* Restore CSOR and CSOR_ext */ - iowrite32be(csor, &ifc->csor_cs[cs].csor); - iowrite32be(csor_ext, &ifc->csor_cs[cs].csor_ext); + ifc_out32(csor, &ifc->csor_cs[cs].csor); + ifc_out32(csor_ext, &ifc->csor_cs[cs].csor_ext); } static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) @@ -882,7 +891,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* fill in nand_chip structure */ /* set up function call table */ - if ((ioread32be(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) + if ((ifc_in32(&ifc->cspr_cs[priv->bank].cspr)) & + CSPR_PORT_SIZE_16) chip->read_byte = fsl_ifc_read_byte16; else chip->read_byte = fsl_ifc_read_byte; @@ -896,13 +906,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; - iowrite32be(0x0, &ifc->ifc_nand.ncfgr); + ifc_out32(0x0, &ifc->ifc_nand.ncfgr); /* set up nand options */ chip->bbt_options = NAND_BBT_USE_FLASH; chip->options = NAND_NO_SUBPAGE_WRITE; - if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { + if (ifc_in32(&ifc->cspr_cs[priv->bank].cspr) + & CSPR_PORT_SIZE_16) { chip->read_byte = fsl_ifc_read_byte16; chip->options |= NAND_BUSWIDTH_16; } else { @@ -915,7 +926,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->ecc.read_page = fsl_ifc_read_page; chip->ecc.write_page = fsl_ifc_write_page; - csor = ioread32be(&ifc->csor_cs[priv->bank].csor); + csor = ifc_in32(&ifc->csor_cs[priv->bank].csor); /* Hardware generates ECC per 512 Bytes */ chip->ecc.size = 512; @@ -981,7 +992,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->ecc.mode = NAND_ECC_SOFT; } - ver = ioread32be(&ifc->ifc_rev); + ver = ifc_in32(&ifc->ifc_rev); if (ver == FSL_IFC_V1_1_0) fsl_ifc_sram_init(priv); @@ -1006,7 +1017,7 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, phys_addr_t addr) { - u32 cspr = ioread32be(&ifc->cspr_cs[bank].cspr); + u32 cspr = ifc_in32(&ifc->cspr_cs[bank].cspr); if (!(cspr & CSPR_V)) return 0; @@ -1093,13 +1104,13 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) dev_set_drvdata(priv->dev, priv); - iowrite32be(IFC_NAND_EVTER_EN_OPC_EN | + ifc_out32(IFC_NAND_EVTER_EN_OPC_EN | IFC_NAND_EVTER_EN_FTOER_EN | IFC_NAND_EVTER_EN_WPER_EN, &ifc->ifc_nand.nand_evter_en); /* enable NAND Machine Interrupts */ - iowrite32be(IFC_NAND_EVTER_INTR_OPCIR_EN | + ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN | IFC_NAND_EVTER_INTR_FTOERIR_EN | IFC_NAND_EVTER_INTR_WPERIR_EN, &ifc->ifc_nand.nand_evter_intr_en); diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig new file mode 100644 index 0000000..64cfc39 --- /dev/null +++ b/drivers/mtd/spi-nor/Kconfig @@ -0,0 +1,12 @@ +config MTD_SPI_NOR_BASE + bool "the framework for SPI-NOR support" + depends on MTD + help + This is the framework for the SPI NOR which can be used by the SPI + device drivers and the SPI-NOR device driver. +config SPI_FSL_QUADSPI + tristate "Freescale Quad SPI controller" + depends on ARCH_MXC && MTD_SPI_NOR_BASE + help + This enables support for the Quad SPI controller in master mode. + We only connect the NOR to this controller now. diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile new file mode 100644 index 0000000..51f9d8b --- /dev/null +++ b/drivers/mtd/spi-nor/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MTD_SPI_NOR_BASE) += spi-nor.o +obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c new file mode 100644 index 0000000..2dd29c3 --- /dev/null +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -0,0 +1,1098 @@ +/* + * Freescale QuadSPI driver. + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/timer.h> +#include <linux/jiffies.h> +#include <linux/completion.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/spi-nor.h> + +/* The registers */ +#define QUADSPI_MCR 0x00 +#define MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_SHIFT 29 +#define MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK \ + (1 << MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_SHIFT) +#define QUADSPI_MCR_RESERVED_SHIFT 16 +#define QUADSPI_MCR_RESERVED_MASK (0xF << QUADSPI_MCR_RESERVED_SHIFT) +#define QUADSPI_MCR_MDIS_SHIFT 14 +#define QUADSPI_MCR_MDIS_MASK (1 << QUADSPI_MCR_MDIS_SHIFT) +#define QUADSPI_MCR_CLR_TXF_SHIFT 11 +#define QUADSPI_MCR_CLR_TXF_MASK (1 << QUADSPI_MCR_CLR_TXF_SHIFT) +#define QUADSPI_MCR_CLR_RXF_SHIFT 10 +#define QUADSPI_MCR_CLR_RXF_MASK (1 << QUADSPI_MCR_CLR_RXF_SHIFT) +#define QUADSPI_MCR_DDR_EN_SHIFT 7 +#define QUADSPI_MCR_DDR_EN_MASK (1 << QUADSPI_MCR_DDR_EN_SHIFT) +#define QUADSPI_MCR_END_CFG_SHIFT 2 +#define QUADSPI_MCR_END_CFG_MASK (3 << QUADSPI_MCR_END_CFG_SHIFT) +#define QUADSPI_MCR_SWRSTHD_SHIFT 1 +#define QUADSPI_MCR_SWRSTHD_MASK (1 << QUADSPI_MCR_SWRSTHD_SHIFT) +#define QUADSPI_MCR_SWRSTSD_SHIFT 0 +#define QUADSPI_MCR_SWRSTSD_MASK (1 << QUADSPI_MCR_SWRSTSD_SHIFT) + +#define QUADSPI_IPCR 0x08 +#define QUADSPI_IPCR_SEQID_SHIFT 24 +#define QUADSPI_IPCR_SEQID_MASK (0xF << QUADSPI_IPCR_SEQID_SHIFT) + +#define QUADSPI_BUF0CR 0x10 +#define QUADSPI_BUF1CR 0x14 +#define QUADSPI_BUF2CR 0x18 +#define QUADSPI_BUFXCR_INVALID_MSTRID 0xe + +#define QUADSPI_BUF3CR 0x1c +#define QUADSPI_BUF3CR_ALLMST_SHIFT 31 +#define QUADSPI_BUF3CR_ALLMST (1 << QUADSPI_BUF3CR_ALLMST_SHIFT) + +#define QUADSPI_BFGENCR 0x20 +#define QUADSPI_BFGENCR_PAR_EN_SHIFT 16 +#define QUADSPI_BFGENCR_PAR_EN_MASK (1 << (QUADSPI_BFGENCR_PAR_EN_SHIFT)) +#define QUADSPI_BFGENCR_SEQID_SHIFT 12 +#define QUADSPI_BFGENCR_SEQID_MASK (0xF << QUADSPI_BFGENCR_SEQID_SHIFT) + +#define QUADSPI_BUF0IND 0x30 +#define QUADSPI_BUF1IND 0x34 +#define QUADSPI_BUF2IND 0x38 +#define QUADSPI_SFAR 0x100 + +#define QUADSPI_SMPR 0x108 +#define QUADSPI_SMPR_DDRSMP_SHIFT 16 +#define QUADSPI_SMPR_DDRSMP_MASK (7 << QUADSPI_SMPR_DDRSMP_SHIFT) +#define QUADSPI_SMPR_FSDLY_SHIFT 6 +#define QUADSPI_SMPR_FSDLY_MASK (1 << QUADSPI_SMPR_FSDLY_SHIFT) +#define QUADSPI_SMPR_FSPHS_SHIFT 5 +#define QUADSPI_SMPR_FSPHS_MASK (1 << QUADSPI_SMPR_FSPHS_SHIFT) +#define QUADSPI_SMPR_HSENA_SHIFT 0 +#define QUADSPI_SMPR_HSENA_MASK (1 << QUADSPI_SMPR_HSENA_SHIFT) + +#define QUADSPI_RBSR 0x10c +#define QUADSPI_RBSR_RDBFL_SHIFT 8 +#define QUADSPI_RBSR_RDBFL_MASK (0x3F << QUADSPI_RBSR_RDBFL_SHIFT) + +#define QUADSPI_RBCT 0x110 +#define QUADSPI_RBCT_WMRK_MASK 0x1F +#define QUADSPI_RBCT_RXBRD_SHIFT 8 +#define QUADSPI_RBCT_RXBRD_USEIPS (0x1 << QUADSPI_RBCT_RXBRD_SHIFT) + +#define QUADSPI_TBSR 0x150 +#define QUADSPI_TBDR 0x154 +#define QUADSPI_SR 0x15c +#define QUADSPI_SR_IP_ACC_SHIFT 1 +#define QUADSPI_SR_IP_ACC_MASK (0x1 << QUADSPI_SR_IP_ACC_SHIFT) +#define QUADSPI_SR_AHB_ACC_SHIFT 2 +#define QUADSPI_SR_AHB_ACC_MASK (0x1 << QUADSPI_SR_AHB_ACC_SHIFT) + +#define QUADSPI_FR 0x160 +#define QUADSPI_FR_TFF_MASK 0x1 + +#define QUADSPI_SFA1AD 0x180 +#define QUADSPI_SFA2AD 0x184 +#define QUADSPI_SFB1AD 0x188 +#define QUADSPI_SFB2AD 0x18c +#define QUADSPI_RBDR 0x200 + +#define QUADSPI_LUTKEY 0x300 +#define QUADSPI_LUTKEY_VALUE 0x5AF05AF0 + +#define QUADSPI_LCKCR 0x304 +#define QUADSPI_LCKER_LOCK 0x1 +#define QUADSPI_LCKER_UNLOCK 0x2 + +#define QUADSPI_RSER 0x164 +#define QUADSPI_RSER_TFIE (0x1 << 0) + +#define QUADSPI_LUT_BASE 0x310 + +/* + * The definition of the LUT register shows below: + * + * --------------------------------------------------- + * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 | + * --------------------------------------------------- + */ +#define OPRND0_SHIFT 0 +#define PAD0_SHIFT 8 +#define INSTR0_SHIFT 10 +#define OPRND1_SHIFT 16 + +/* Instruction set for the LUT register. */ +#define LUT_STOP 0 +#define LUT_CMD 1 +#define LUT_ADDR 2 +#define LUT_DUMMY 3 +#define LUT_MODE 4 +#define LUT_MODE2 5 +#define LUT_MODE4 6 +#define LUT_READ 7 +#define LUT_WRITE 8 +#define LUT_JMP_ON_CS 9 +#define LUT_ADDR_DDR 10 +#define LUT_MODE_DDR 11 +#define LUT_MODE2_DDR 12 +#define LUT_MODE4_DDR 13 +#define LUT_READ_DDR 14 +#define LUT_WRITE_DDR 15 +#define LUT_DATA_LEARN 16 + +/* + * The PAD definitions for LUT register. + * + * The pad stands for the lines number of IO[0:3]. + * For example, the Quad read need four IO lines, so you should + * set LUT_PAD4 which means we use four IO lines. + */ +#define LUT_PAD1 0 +#define LUT_PAD2 1 +#define LUT_PAD4 2 + +/* Oprands for the LUT register. */ +#define ADDR24BIT 0x18 +#define ADDR32BIT 0x20 + +/* Macros for constructing the LUT register. */ +#define LUT0(ins, pad, opr) \ + (((opr) << OPRND0_SHIFT) | ((LUT_##pad) << PAD0_SHIFT) | \ + ((LUT_##ins) << INSTR0_SHIFT)) + +#define LUT1(ins, pad, opr) (LUT0(ins, pad, opr) << OPRND1_SHIFT) + +/* other macros for LUT register. */ +#define QUADSPI_LUT(x) (QUADSPI_LUT_BASE + (x) * 4) +#define QUADSPI_LUT_NUM 64 + +/* SEQID -- we can have 16 seqids at most. */ +#define SEQID_QUAD_READ 0 +#define SEQID_WREN 1 +#define SEQID_WRDI 2 +#define SEQID_RDSR 3 +#define SEQID_SE 4 +#define SEQID_CHIP_ERASE 5 +#define SEQID_PP 6 +#define SEQID_RDID 7 +#define SEQID_WRSR 8 +#define SEQID_RDCR 9 +#define SEQID_EN4B 10 +#define SEQID_BRWR 11 + + +enum fsl_qspi_devtype { + FSL_QUADSPI_VYBRID, + FSL_QUADSPI_IMX6SX, + FSL_QUADSPI_LS1, +}; + +struct fsl_qspi_devtype_data { + enum fsl_qspi_devtype devtype; + int rxfifo; + int txfifo; +}; + +static struct fsl_qspi_devtype_data vybrid_data = { + .devtype = FSL_QUADSPI_VYBRID, + .rxfifo = 128, + .txfifo = 64 +}; + +static struct fsl_qspi_devtype_data imx6sx_data = { + .devtype = FSL_QUADSPI_IMX6SX, + .rxfifo = 128, + .txfifo = 512 +}; + +static struct fsl_qspi_devtype_data ls1_data = { + .devtype = FSL_QUADSPI_LS1, + .rxfifo = 128, + .txfifo = 64 +}; + +#define FSL_QSPI_MAX_CHIP 4 +struct fsl_qspi { + struct mtd_info mtd[FSL_QSPI_MAX_CHIP]; + struct spi_nor nor[FSL_QSPI_MAX_CHIP]; + void __iomem *iobase; + void __iomem *ahb_base; /* Used when read from AHB bus */ + u32 memmap_phy; + struct clk *clk, *clk_en; + struct device *dev; + struct completion c; + struct fsl_qspi_devtype_data *devtype_data; + u32 nor_size; + u32 nor_num; + u32 clk_rate; + unsigned int chip_base_addr; /* We may support two chips. */ +}; + +static inline int is_vybrid_qspi(struct fsl_qspi *q) +{ + return q->devtype_data->devtype == FSL_QUADSPI_VYBRID; +} + +static inline int is_imx6sx_qspi(struct fsl_qspi *q) +{ + return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX; +} + +static inline int is_ls1_qspi(struct fsl_qspi *q) +{ + return q->devtype_data->devtype == FSL_QUADSPI_LS1; +} + +static inline void qspi_writel(struct fsl_qspi *q, u32 val, void __iomem *addr) +{ + is_ls1_qspi(q) ? __raw_writel(cpu_to_be32(val), addr) : + __raw_writel(cpu_to_le32(val), addr); +} + +static inline u32 qspi_readl(struct fsl_qspi *q, void __iomem *addr) +{ + return is_ls1_qspi(q) ? cpu_to_be32((__force u32) __raw_readl(addr)) : + cpu_to_le32((__force u32) __raw_readl(addr)); +} + +/* + * An IC bug makes us to re-arrange the 32-bit data. + * The following chips, such as IMX6SLX, have fixed this bug. + */ +static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a) +{ + return is_vybrid_qspi(q) ? __swab32(a) : a; +} + +static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q) +{ + qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); + qspi_writel(q, QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR); +} + +static inline void fsl_qspi_lock_lut(struct fsl_qspi *q) +{ + qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); + qspi_writel(q, QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR); +} + +static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) +{ + struct fsl_qspi *q = dev_id; + u32 reg; + + /* clear interrupt */ + reg = qspi_readl(q, q->iobase + QUADSPI_FR); + qspi_writel(q, reg, q->iobase + QUADSPI_FR); + + if (reg & QUADSPI_FR_TFF_MASK) + complete(&q->c); + + dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x:0x%.8x\n", q->chip_base_addr, reg); + return IRQ_HANDLED; +} + +static void fsl_qspi_init_lut(struct fsl_qspi *q) +{ + void __iomem *base = q->iobase; + int rxfifo = q->devtype_data->rxfifo; + struct spi_nor *nor = &q->nor[0]; + u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT; + u32 lut_base; + u8 op, dm; + int i; + + fsl_qspi_unlock_lut(q); + + /* Clear all the LUT table */ + for (i = 0; i < QUADSPI_LUT_NUM; i++) + qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4); + + /* Quad Read and DDR Quad Read*/ + lut_base = SEQID_QUAD_READ * 4; + op = nor->read_opcode; + dm = nor->read_dummy; + if (nor->flash_read == SPI_NOR_QUAD) { + if (op == SPINOR_OP_READ_1_1_4 || op == SPINOR_OP_READ4_1_1_4) { + /* read mode : 1-1-4 */ + qspi_writel(q, + LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + + qspi_writel(q, + LUT0(DUMMY, PAD1, dm) + | LUT1(READ, PAD4, rxfifo), + base + QUADSPI_LUT(lut_base + 1)); + } else { + dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op); + } + } else if (nor->flash_read == SPI_NOR_DDR_QUAD) { + if (op == SPINOR_OP_READ_1_4_4_D || + op == SPINOR_OP_READ4_1_4_4_D) { + /* read mode : 1-4-4, such as Spansion s25fl128s. */ + qspi_writel(q, LUT0(CMD, PAD1, op) + | LUT1(ADDR_DDR, PAD4, addrlen), + base + QUADSPI_LUT(lut_base)); + + qspi_writel(q, LUT0(MODE_DDR, PAD4, 0xff) + | LUT1(DUMMY, PAD1, dm), + base + QUADSPI_LUT(lut_base + 1)); + + qspi_writel(q, LUT0(READ_DDR, PAD4, rxfifo) + | LUT1(JMP_ON_CS, PAD1, 0), + base + QUADSPI_LUT(lut_base + 2)); + } else if (op == SPINOR_OP_READ_1_1_4_D) { + /* read mode : 1-1-4, such as Micron N25Q256A. */ + qspi_writel(q, LUT0(CMD, PAD1, op) + | LUT1(ADDR_DDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + + qspi_writel(q, LUT0(DUMMY, PAD1, dm) + | LUT1(READ_DDR, PAD4, rxfifo), + base + QUADSPI_LUT(lut_base + 1)); + + qspi_writel(q, LUT0(JMP_ON_CS, PAD1, 0), + base + QUADSPI_LUT(lut_base + 2)); + } else { + dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op); + } + } + + /* Write enable */ + lut_base = SEQID_WREN * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WREN), + base + QUADSPI_LUT(lut_base)); + + /* Page Program */ + lut_base = SEQID_PP * 4; + qspi_writel(q, LUT0(CMD, PAD1, + nor->program_opcode) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + qspi_writel(q, LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); + + /* Read Status */ + lut_base = SEQID_RDSR * 4; + qspi_writel(q, LUT0(CMD, PAD1, + SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1), + base + QUADSPI_LUT(lut_base)); + + /* Erase a sector */ + lut_base = SEQID_SE * 4; + qspi_writel(q, LUT0(CMD, PAD1, + nor->erase_opcode) | LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + + /* Erase the whole chip */ + lut_base = SEQID_CHIP_ERASE * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE), + base + QUADSPI_LUT(lut_base)); + + /* READ ID */ + lut_base = SEQID_RDID * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8), + base + QUADSPI_LUT(lut_base)); + + /* Write Register */ + lut_base = SEQID_WRSR * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2), + base + QUADSPI_LUT(lut_base)); + + /* Read Configuration Register */ + lut_base = SEQID_RDCR * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1), + base + QUADSPI_LUT(lut_base)); + + /* Write disable */ + lut_base = SEQID_WRDI * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRDI), + base + QUADSPI_LUT(lut_base)); + + /* Enter 4 Byte Mode (Micron) */ + lut_base = SEQID_EN4B * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_EN4B), + base + QUADSPI_LUT(lut_base)); + + /* Enter 4 Byte Mode (Spansion) */ + lut_base = SEQID_BRWR * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR), + base + QUADSPI_LUT(lut_base)); + + fsl_qspi_lock_lut(q); +} + +/* Get the SEQID for the command */ +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) +{ + switch (cmd) { + case SPINOR_OP_READ_1_1_4_D: + case SPINOR_OP_READ_1_4_4_D: + case SPINOR_OP_READ4_1_4_4_D: + case SPINOR_OP_READ4_1_1_4: + case SPINOR_OP_READ_1_1_4: + return SEQID_QUAD_READ; + case SPINOR_OP_WREN: + return SEQID_WREN; + case SPINOR_OP_WRDI: + return SEQID_WRDI; + case SPINOR_OP_RDSR: + return SEQID_RDSR; + case SPINOR_OP_BE_4K: + case SPINOR_OP_SE: + return SEQID_SE; + case SPINOR_OP_CHIP_ERASE: + return SEQID_CHIP_ERASE; + case SPINOR_OP_PP: + return SEQID_PP; + case SPINOR_OP_RDID: + return SEQID_RDID; + case SPINOR_OP_WRSR: + return SEQID_WRSR; + case SPINOR_OP_RDCR: + return SEQID_RDCR; + case SPINOR_OP_EN4B: + return SEQID_EN4B; + case SPINOR_OP_BRWR: + return SEQID_BRWR; + default: + dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); + break; + } + return -EINVAL; +} + +static int +fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) +{ + void __iomem *base = q->iobase; + int seqid; + u32 reg, reg2; + int err; + + init_completion(&q->c); + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len:%d, cmd:%.2x\n", + q->chip_base_addr, addr, len, cmd); + + /* save the reg */ + reg = qspi_readl(q, base + QUADSPI_MCR); + + qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr, + base + QUADSPI_SFAR); + qspi_writel(q, QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, + base + QUADSPI_RBCT); + qspi_writel(q, reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR); + + do { + reg2 = qspi_readl(q, base + QUADSPI_SR); + if (reg2 & (QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK)) { + udelay(1); + dev_dbg(q->dev, "The controller is busy, 0x%x\n", reg2); + continue; + } + break; + } while (1); + + /* trigger the LUT now */ + seqid = fsl_qspi_get_seqid(q, cmd); + qspi_writel(q, (seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, + base + QUADSPI_IPCR); + + /* Wait for the interrupt. */ + err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000)); + if (!err) { + dev_err(q->dev, + "cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n", + cmd, addr, qspi_readl(q, base + QUADSPI_FR), + qspi_readl(q, base + QUADSPI_SR)); + err = -ETIMEDOUT; + } else { + err = 0; + } + + /* restore the MCR */ + qspi_writel(q, reg, base + QUADSPI_MCR); + + return err; +} + +/* Read out the data from the QUADSPI_RBDR buffer registers. */ +static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u8 *rxbuf) +{ + u32 tmp; + int i = 0; + + while (len > 0) { + tmp = qspi_readl(q, q->iobase + QUADSPI_RBDR + i * 4); + tmp = fsl_qspi_endian_xchg(q, tmp); + dev_dbg(q->dev, "chip addr:0x%.8x, rcv:0x%.8x\n", + q->chip_base_addr, tmp); + + if (len >= 4) { + *((u32 *)rxbuf) = tmp; + rxbuf += 4; + } else { + memcpy(rxbuf, &tmp, len); + break; + } + + len -= 4; + i++; + } +} + +/* + * If we have changed the content of the flash by writing or erasing, + * we need to invalidate the AHB buffer. If we do not do so, we may read out + * the wrong data. The spec tells us reset the AHB domain and Serial Flash + * domain at the same time. + */ +static inline void fsl_qspi_invalid(struct fsl_qspi *q) +{ + u32 reg; + + reg = qspi_readl(q, q->iobase + QUADSPI_MCR); + reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK; + qspi_writel(q, reg, q->iobase + QUADSPI_MCR); + + /* + * The minimum delay : 1 AHB + 2 SFCK clocks. + * Delay 1 us is enough. + */ + udelay(1); + + reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK); + qspi_writel(q, reg, q->iobase + QUADSPI_MCR); +} + +static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, + u8 opcode, unsigned int to, u32 *txbuf, + unsigned count, size_t *retlen) +{ + int ret, i, j; + u32 tmp; + + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len : %d\n", + q->chip_base_addr, to, count); + + /* clear the TX FIFO. */ + tmp = qspi_readl(q, q->iobase + QUADSPI_MCR); + qspi_writel(q, tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR); + + /* fill the TX data to the FIFO */ + for (j = 0, i = ((count + 3) / 4); j < i; j++) { + tmp = fsl_qspi_endian_xchg(q, *txbuf); + qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR); + txbuf++; + } + + /* Trigger it */ + ret = fsl_qspi_runcmd(q, opcode, to, count); + + if (ret == 0 && retlen) + *retlen += count; + + return ret; +} + +static void fsl_qspi_set_map_addr(struct fsl_qspi *q) +{ + int nor_size = q->nor_size; + void __iomem *base = q->iobase; + + qspi_writel(q, nor_size + q->memmap_phy, base + QUADSPI_SFA1AD); + qspi_writel(q, nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD); + qspi_writel(q, nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD); + qspi_writel(q, nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD); +} + +/* + * There are two different ways to read out the data from the flash: + * the "IP Command Read" and the "AHB Command Read". + * + * The IC guy suggests we use the "AHB Command Read" which is faster + * then the "IP Command Read". (What's more is that there is a bug in + * the "IP Command Read" in the Vybrid.) + * + * After we set up the registers for the "AHB Command Read", we can use + * the memcpy to read the data directly. A "missed" access to the buffer + * causes the controller to clear the buffer, and use the sequence pointed + * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash. + */ +static void fsl_qspi_init_abh_read(struct fsl_qspi *q) +{ + void __iomem *base = q->iobase; + struct spi_nor *nor = &q->nor[0]; + u32 reg, reg2; + int seqid; + + /* AHB configuration for access buffer 0/1/2 .*/ + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); + qspi_writel(q, QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); + + /* We only use the buffer3 */ + qspi_writel(q, 0, base + QUADSPI_BUF0IND); + qspi_writel(q, 0, base + QUADSPI_BUF1IND); + qspi_writel(q, 0, base + QUADSPI_BUF2IND); + + /* Set the default lut sequence for AHB Read. */ + seqid = fsl_qspi_get_seqid(q, nor->read_opcode); + qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT, + q->iobase + QUADSPI_BFGENCR); + + /* enable the DDR quad read */ + if (nor->flash_read == SPI_NOR_DDR_QUAD) { + reg = qspi_readl(q, q->iobase + QUADSPI_MCR); + + /* Firstly, disable the module */ + qspi_writel(q, reg | QUADSPI_MCR_MDIS_MASK, q->iobase + + QUADSPI_MCR); + + /* Set the Sampling Register for DDR */ + reg2 = qspi_readl(q, q->iobase + QUADSPI_SMPR); + reg2 &= ~QUADSPI_SMPR_DDRSMP_MASK; + reg2 |= (2 << QUADSPI_SMPR_DDRSMP_SHIFT); + qspi_writel(q, reg2, q->iobase + QUADSPI_SMPR); + + /* Enable the module again (enable the DDR too) */ + reg |= QUADSPI_MCR_DDR_EN_MASK; + if (is_imx6sx_qspi(q)) + reg |= MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK; + + qspi_writel(q, reg, q->iobase + QUADSPI_MCR); + } +} + +/* We use this function to do some basic init for spi_nor_scan(). */ +static int fsl_qspi_nor_setup(struct fsl_qspi *q) +{ + void __iomem *base = q->iobase; + u32 reg; + int ret; + + /* the default frequency, we will change it in the future.*/ + ret = clk_set_rate(q->clk, 66000000); + if (ret) + return ret; + + /* Init the LUT table. */ + fsl_qspi_init_lut(q); + + /* Disable the module */ + qspi_writel(q, QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK, + base + QUADSPI_MCR); + + reg = qspi_readl(q, base + QUADSPI_SMPR); + qspi_writel(q, reg & ~(QUADSPI_SMPR_FSDLY_MASK + | QUADSPI_SMPR_FSPHS_MASK + | QUADSPI_SMPR_HSENA_MASK + | QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR); + + /* Enable the module */ + qspi_writel(q, QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK, + base + QUADSPI_MCR); + + /* enable the interrupt */ + qspi_writel(q, QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); + + return 0; +} + +static int fsl_qspi_nor_setup_last(struct fsl_qspi *q) +{ + unsigned long rate = q->clk_rate; + int ret; + + if (is_imx6sx_qspi(q)) + rate *= 4; + + ret = clk_set_rate(q->clk, rate); + if (ret) + return ret; + + /* Init the LUT table again. */ + fsl_qspi_init_lut(q); + + /* Init for AHB read */ + fsl_qspi_init_abh_read(q); + + return 0; +} + +static struct of_device_id fsl_qspi_dt_ids[] = { + { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, }, + { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, }, + { .compatible = "fsl,ls1-qspi", .data = (void *)&ls1_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); + +static void fsl_qspi_set_base_addr(struct fsl_qspi *q, struct spi_nor *nor) +{ + q->chip_base_addr = q->nor_size * (nor - q->nor); +} + +static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + int ret; + struct fsl_qspi *q = nor->priv; + + ret = fsl_qspi_runcmd(q, opcode, 0, len); + if (ret) + return ret; + + fsl_qspi_read_data(q, len, buf); + return 0; +} + +static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int write_enable) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + if (!buf) { + ret = fsl_qspi_runcmd(q, opcode, 0, 1); + if (ret) + return ret; + + if (opcode == SPINOR_OP_CHIP_ERASE) + fsl_qspi_invalid(q); + + } else if (len > 0) { + ret = fsl_qspi_nor_write(q, nor, opcode, 0, + (u32 *)buf, len, NULL); + } else { + dev_err(q->dev, "invalid cmd %d\n", opcode); + ret = -EINVAL; + } + + return ret; +} + +static void fsl_qspi_write(struct spi_nor *nor, loff_t to, + size_t len, size_t *retlen, const u_char *buf) +{ + struct fsl_qspi *q = nor->priv; + + fsl_qspi_nor_write(q, nor, nor->program_opcode, to, + (u32 *)buf, len, retlen); + + /* invalid the data in the AHB buffer. */ + fsl_qspi_invalid(q); +} + +static int fsl_qspi_read(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + struct fsl_qspi *q = nor->priv; + u8 cmd = nor->read_opcode; + int ret; + + dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n", + cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len); + + /* Wait until the previous command is finished. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; + + /* Read out the data directly from the AHB buffer.*/ + memcpy(buf, q->ahb_base + q->chip_base_addr + from, len); + + *retlen += len; + return 0; +} + +static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n", + nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs); + + /* Wait until finished previous write command. */ + ret = nor->wait_till_ready(nor); + if (ret) + return ret; + + /* Send write enable, then erase commands. */ + ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); + if (ret) + return ret; + + ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0); + if (ret) + return ret; + + fsl_qspi_invalid(q); + return 0; +} + +static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct fsl_qspi *q = nor->priv; + int ret; + + ret = clk_enable(q->clk_en); + if (ret) + return ret; + + ret = clk_enable(q->clk); + if (ret) { + clk_disable(q->clk_en); + return ret; + } + + fsl_qspi_set_base_addr(q, nor); + return 0; +} + +static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct fsl_qspi *q = nor->priv; + + clk_disable(q->clk); + clk_disable(q->clk_en); +} + +static int fsl_qspi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mtd_part_parser_data ppdata; + struct device *dev = &pdev->dev; + struct fsl_qspi *q; + struct resource *res; + struct spi_nor *nor; + struct mtd_info *mtd; + int ret, i = 0; + bool has_second_chip = false; + const struct of_device_id *of_id = + of_match_device(fsl_qspi_dt_ids, &pdev->dev); + + q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL); + if (!q) + return -ENOMEM; + + q->nor_num = of_get_child_count(dev->of_node); + if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP) + return -ENODEV; + + /* find the resources */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); + q->iobase = devm_ioremap_resource(dev, res); + if (IS_ERR(q->iobase)) { + ret = PTR_ERR(q->iobase); + goto map_failed; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "QuadSPI-memory"); + q->ahb_base = devm_ioremap_resource(dev, res); + if (IS_ERR(q->ahb_base)) { + ret = PTR_ERR(q->ahb_base); + goto map_failed; + } + q->memmap_phy = res->start; + + /* find the clocks */ + q->clk_en = devm_clk_get(dev, "qspi_en"); + if (IS_ERR(q->clk_en)) { + ret = PTR_ERR(q->clk_en); + goto map_failed; + } + + q->clk = devm_clk_get(dev, "qspi"); + if (IS_ERR(q->clk)) { + ret = PTR_ERR(q->clk); + goto map_failed; + } + + ret = clk_prepare_enable(q->clk_en); + if (ret) { + dev_err(dev, "can not enable the qspi_en clock\n"); + goto map_failed; + } + + ret = clk_prepare_enable(q->clk); + if (ret) { + clk_disable_unprepare(q->clk_en); + dev_err(dev, "can not enable the qspi clock\n"); + goto map_failed; + } + + /* find the irq */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "failed to get the irq\n"); + goto irq_failed; + } + + ret = devm_request_irq(dev, ret, + fsl_qspi_irq_handler, 0, pdev->name, q); + if (ret) { + dev_err(dev, "failed to request irq.\n"); + goto irq_failed; + } + + q->dev = dev; + q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data; + platform_set_drvdata(pdev, q); + + ret = fsl_qspi_nor_setup(q); + if (ret) + goto irq_failed; + + if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) + has_second_chip = true; + + /* iterate the subnodes. */ + for_each_available_child_of_node(dev->of_node, np) { + const struct spi_device_id *id; + enum read_mode mode = SPI_NOR_QUAD; + char modalias[40]; + u32 dummy = 0; + + /* skip the holes */ + if (!has_second_chip) + i *= 2; + + nor = &q->nor[i]; + mtd = &q->mtd[i]; + + nor->mtd = mtd; + nor->dev = dev; + nor->np = np; + nor->priv = q; + mtd->priv = nor; + + /* fill the hooks */ + nor->read_reg = fsl_qspi_read_reg; + nor->write_reg = fsl_qspi_write_reg; + nor->read = fsl_qspi_read; + nor->write = fsl_qspi_write; + nor->erase = fsl_qspi_erase; + + nor->prepare = fsl_qspi_prep; + nor->unprepare = fsl_qspi_unprep; + + if (of_modalias_node(np, modalias, sizeof(modalias)) < 0) + goto map_failed; + + id = spi_nor_match_id(modalias); + if (!id) + goto map_failed; + + ret = of_property_read_u32(np, "spi-max-frequency", + &q->clk_rate); + if (ret < 0) + goto map_failed; + + /* Can we enable the DDR Quad Read? */ + ret = of_property_read_u32(np, "spi-nor,ddr-quad-read-dummy", + &dummy); + if (!ret && dummy > 0) + mode = SPI_NOR_DDR_QUAD; + + /* set the chip address for READID */ + fsl_qspi_set_base_addr(q, nor); + + ret = spi_nor_scan(nor, id, mode); + if (ret) + goto map_failed; + + ppdata.of_node = np; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); + if (ret) + goto map_failed; + + /* Set the correct NOR size now. */ + if (q->nor_size == 0) { + q->nor_size = mtd->size; + + /* Map the SPI NOR to accessiable address */ + fsl_qspi_set_map_addr(q); + } + + /* + * The TX FIFO is 64 bytes in the Vybrid, but the Page Program + * may writes 265 bytes per time. The write is working in the + * unit of the TX FIFO, not in the unit of the SPI NOR's page + * size. + * + * So shrink the spi_nor->page_size if it is larger then the + * TX FIFO. + */ + if (nor->page_size > q->devtype_data->txfifo) + nor->page_size = q->devtype_data->txfifo; + + i++; + } + + /* finish the rest init. */ + ret = fsl_qspi_nor_setup_last(q); + if (ret) + goto last_init_failed; + + clk_disable(q->clk); + clk_disable(q->clk_en); + dev_info(dev, "QuadSPI SPI NOR flash driver\n"); + return 0; + +last_init_failed: + for (i = 0; i < q->nor_num; i++) + mtd_device_unregister(&q->mtd[i]); + +irq_failed: + clk_disable_unprepare(q->clk); + clk_disable_unprepare(q->clk_en); +map_failed: + dev_err(dev, "Freescale QuadSPI probe failed\n"); + return ret; +} + +static int fsl_qspi_remove(struct platform_device *pdev) +{ + struct fsl_qspi *q = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < q->nor_num; i++) + mtd_device_unregister(&q->mtd[i]); + + /* disable the hardware */ + qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); + qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER); + + clk_unprepare(q->clk); + clk_unprepare(q->clk_en); + return 0; +} + +static struct platform_driver fsl_qspi_driver = { + .driver = { + .name = "fsl-quadspi", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + .of_match_table = fsl_qspi_dt_ids, + }, + .probe = fsl_qspi_probe, + .remove = fsl_qspi_remove, +}; +module_platform_driver(fsl_qspi_driver); + +MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver"); +MODULE_AUTHOR("Freescale Semiconductor Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c new file mode 100644 index 0000000..5df9d0d --- /dev/null +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -0,0 +1,1183 @@ +/* + * Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with + * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c + * + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/math64.h> + +#include <linux/mtd/cfi.h> +#include <linux/mtd/mtd.h> +#include <linux/of_platform.h> +#include <linux/spi/flash.h> +#include <linux/mtd/spi-nor.h> + +/* Define max times to check status register before we give up. */ +#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ + +#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) + +/* + * Read the status register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_sr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1); + if (ret < 0) { + pr_err("error %d reading SR\n", (int) ret); + return ret; + } + + return val; +} + +/* + * Read configuration register, returning its value in the + * location. Return the configuration register value. + * Returns negative if error occured. + */ +static int read_cr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading CR\n", ret); + return ret; + } + + return val; +} + +/* + * Dummy Cycle calculation for different type of read. + * It can be used to support more commands with + * different dummy cycle requirements. + */ +static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) +{ + u32 dummy; + + switch (nor->flash_read) { + case SPI_NOR_DDR_QUAD: + /* + * The m25p80.c can not support the DDR quad read. + * We set the dummy cycles to 8 by default. The SPI NOR + * controller driver can set it in its child DT node. + * We parse it out here. + */ + if (nor->np && !of_property_read_u32(nor->np, + "spi-nor,ddr-quad-read-dummy", &dummy)) { + return dummy; + } + case SPI_NOR_FAST: + case SPI_NOR_DUAL: + case SPI_NOR_QUAD: + return 8; + case SPI_NOR_NORMAL: + return 0; + } + return 0; +} + +/* + * Write status register 1 byte + * Returns negative if error occurred. + */ +static inline int write_sr(struct spi_nor *nor, u8 val) +{ + nor->cmd_buf[0] = val; + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); +} + +/* + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static inline int write_enable(struct spi_nor *nor) +{ + return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0); +} + +/* + * Send write disble instruction to the chip. + */ +static inline int write_disable(struct spi_nor *nor) +{ + return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0); +} + +static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) +{ + return mtd->priv; +} + +/* Enable/disable 4-byte addressing mode. */ +static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable) +{ + int status; + bool need_wren = false; + u8 cmd; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_ST: /* Micron, actually */ + /* Some Micron need WREN command; all will accept it */ + need_wren = true; + case CFI_MFR_MACRONIX: + case 0xEF /* winbond */: + if (need_wren) + write_enable(nor); + + cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B; + status = nor->write_reg(nor, cmd, NULL, 0, 0); + if (need_wren) + write_disable(nor); + + return status; + default: + /* Spansion style */ + nor->cmd_buf[0] = enable << 7; + return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0); + } +} + +static int spi_nor_wait_till_ready(struct spi_nor *nor) +{ + unsigned long deadline; + int sr; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + cond_resched(); + + sr = read_sr(nor); + if (sr < 0) + break; + else if (!(sr & SR_WIP)) + return 0; + } while (!time_after_eq(jiffies, deadline)); + + return -ETIMEDOUT; +} + +/* + * Service routine to read status register until ready, or timeout occurs. + * Returns non-zero if error. + */ +static int wait_till_ready(struct spi_nor *nor) +{ + return nor->wait_till_ready(nor); +} + +/* + * Erase the whole flash memory + * + * Returns 0 if successful, non-zero otherwise. + */ +static int erase_chip(struct spi_nor *nor) +{ + int ret; + + dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10)); + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + return ret; + + /* Send write enable, then erase commands. */ + write_enable(nor); + + return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0); +} + +static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + int ret = 0; + + mutex_lock(&nor->lock); + + if (nor->prepare) { + ret = nor->prepare(nor, ops); + if (ret) { + dev_err(nor->dev, "failed in the preparation.\n"); + mutex_unlock(&nor->lock); + return ret; + } + } + return ret; +} + +static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + if (nor->unprepare) + nor->unprepare(nor, ops); + mutex_unlock(&nor->lock); +} + +/* + * Erase an address range on the nor chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 addr, len; + uint32_t rem; + int ret; + + dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, + (long long)instr->len); + + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) + return -EINVAL; + + addr = instr->addr; + len = instr->len; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE); + if (ret) + return ret; + + /* whole-chip erase? */ + if (len == mtd->size) { + if (erase_chip(nor)) { + ret = -EIO; + goto erase_err; + } + + /* REVISIT in some cases we could speed up erasing large regions + * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up + * to use "small sector erase", but that's not always optimal. + */ + + /* "sector"-at-a-time erase */ + } else { + while (len) { + if (nor->erase(nor, addr)) { + ret = -EIO; + goto erase_err; + } + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + } + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; + +erase_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + instr->state = MTD_ERASE_FAILED; + return ret; +} + +static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + uint32_t offset = ofs; + uint8_t status_old, status_new; + int ret = 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK); + if (ret) + return ret; + + /* Wait until finished previous command */ + ret = wait_till_ready(nor); + if (ret) + goto err; + + status_old = read_sr(nor); + + if (offset < mtd->size - (mtd->size / 2)) + status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 4)) + status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + else if (offset < mtd->size - (mtd->size / 8)) + status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 16)) + status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; + else if (offset < mtd->size - (mtd->size / 32)) + status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; + else if (offset < mtd->size - (mtd->size / 64)) + status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; + else + status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + + /* Only modify protection if it will not unlock other areas */ + if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) > + (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { + write_enable(nor); + ret = write_sr(nor, status_new); + if (ret) + goto err; + } + +err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); + return ret; +} + +static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + uint32_t offset = ofs; + uint8_t status_old, status_new; + int ret = 0; + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK); + if (ret) + return ret; + + /* Wait until finished previous command */ + ret = wait_till_ready(nor); + if (ret) + goto err; + + status_old = read_sr(nor); + + if (offset+len > mtd->size - (mtd->size / 64)) + status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0); + else if (offset+len > mtd->size - (mtd->size / 32)) + status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0; + else if (offset+len > mtd->size - (mtd->size / 16)) + status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1; + else if (offset+len > mtd->size - (mtd->size / 8)) + status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; + else if (offset+len > mtd->size - (mtd->size / 4)) + status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2; + else if (offset+len > mtd->size - (mtd->size / 2)) + status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; + else + status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; + + /* Only modify protection if it will not lock other areas */ + if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) < + (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) { + write_enable(nor); + ret = write_sr(nor, status_new); + if (ret) + goto err; + } + +err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); + return ret; +} + +struct flash_info { + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + u32 ext_id; + + /* The size listed here is what works with SPINOR_OP_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + + u16 page_size; + u16 addr_width; + + u16 flags; +#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */ +#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */ +#define SST_WRITE 0x04 /* use SST byte programming */ +#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */ +#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ +#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ +#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ +#define SPI_NOR_DDR_QUAD_READ 0x80 /* Flash supports DDR Quad Read */ +}; + +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .jedec_id = (_jedec_id), \ + .ext_id = (_ext_id), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), \ + }) + +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = (_page_size), \ + .addr_width = (_addr_width), \ + .flags = (_flags), \ + }) + +/* NOTE: double check command sets and memory organization when you add + * more nor chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +const struct spi_device_id spi_nor_ids[] = { + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, + + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, + { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, + + { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, + { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, + + { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, + + /* EON -- en25xxx */ + { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, + { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, + { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + + /* ESMT */ + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, + + /* Everspin */ + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + + /* GigaDevice */ + { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, + { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, + + /* Intel/Numonyx -- xxxs33b */ + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, + + /* Macronix */ + { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, + { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, + { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, + { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, + { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) }, + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, + + /* Micron */ + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, + { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, + { "s25fl128s", INFO(0x012018, 0x4d0180, 64 * 1024, 256, + SPI_NOR_QUAD_READ) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, + { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, + { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, + { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, + { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, + { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, + { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, + { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, + { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, + { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, + { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, + { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, + { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, + { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, + { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, + { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, + { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, + { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, + { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, + { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, + { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, + + { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, + { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, + { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, + { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, + { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, + { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, + { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, + { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, + { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, + + { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, + { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, + { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, + + { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, + { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, + { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, + + { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, + { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, + { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, + { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, + + /* Catalyst / On Semiconductor -- non-JEDEC */ + { "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { }, +}; +EXPORT_SYMBOL_GPL(spi_nor_ids); + +static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor) +{ + int tmp; + u8 id[6]; + u32 jedec; + u32 ext_jedec; + struct flash_info *info; + int matched = -1; + + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 6); + if (tmp < 0) { + dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + ext_jedec = id[3] << 8 | id[4]; + + for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { + info = (void *)spi_nor_ids[tmp].driver_data; + if (info->jedec_id == jedec) { + if (info->ext_id == 0) + return &spi_nor_ids[tmp]; + + /* the legacy two bytes ext_id */ + if ((info->ext_id >> 16) == 0) { + if (info->ext_id == ext_jedec) + matched = tmp; + } else { + /* check the sixth byte now */ + ext_jedec = ext_jedec << 8 | id[5]; + if (info->ext_id == ext_jedec) + return &spi_nor_ids[tmp]; + + /* reset back the ext_jedec */ + ext_jedec >>= 8; + } + } else { + /* shortcut */ + if (matched != -1) + return &spi_nor_ids[matched]; + } + } + dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec); + return ERR_PTR(-ENODEV); +} + +static const struct spi_device_id *jedec_probe(struct spi_nor *nor) +{ + return nor->read_id(nor); +} + +static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ); + if (ret) + return ret; + + ret = nor->read(nor, from, len, retlen, buf); + + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ); + return ret; +} + +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + size_t actual; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + goto time_out; + + write_enable(nor); + + nor->sst_write_second = false; + + actual = to % 2; + /* Start write from odd address. */ + if (actual) { + nor->program_opcode = SPINOR_OP_BP; + + /* write one byte. */ + nor->write(nor, to, 1, retlen, buf); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + } + to += actual; + + /* Write out most of the data here. */ + for (; actual < len - 1; actual += 2) { + nor->program_opcode = SPINOR_OP_AAI_WP; + + /* write two bytes. */ + nor->write(nor, to, 2, retlen, buf + actual); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + to += 2; + nor->sst_write_second = true; + } + nor->sst_write_second = false; + + write_disable(nor); + ret = wait_till_ready(nor); + if (ret) + goto time_out; + + /* Write out trailing byte if it exists. */ + if (actual != len) { + write_enable(nor); + + nor->program_opcode = SPINOR_OP_BP; + nor->write(nor, to, 1, retlen, buf + actual); + + ret = wait_till_ready(nor); + if (ret) + goto time_out; + write_disable(nor); + } +time_out: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return ret; +} + +/* + * Write an address range to the nor chip. Data must be written in + * FLASH_PAGESIZE chunks. The address range may be any size provided + * it is within the physical boundaries. + */ +static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 page_offset, page_size, i; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE); + if (ret) + return ret; + + /* Wait until finished previous write command. */ + ret = wait_till_ready(nor); + if (ret) + goto write_err; + + write_enable(nor); + + page_offset = to & (nor->page_size - 1); + + /* do all the bytes fit onto one page? */ + if (page_offset + len <= nor->page_size) { + nor->write(nor, to, len, retlen, buf); + } else { + /* the size of data remaining on the first page */ + page_size = nor->page_size - page_offset; + nor->write(nor, to, page_size, retlen, buf); + + /* write everything in nor->page_size chunks */ + for (i = page_size; i < len; i += page_size) { + page_size = len - i; + if (page_size > nor->page_size) + page_size = nor->page_size; + + wait_till_ready(nor); + write_enable(nor); + + nor->write(nor, to + i, page_size, retlen, buf + i); + } + } + +write_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE); + return 0; +} + +static int macronix_quad_enable(struct spi_nor *nor) +{ + int ret, val; + + val = read_sr(nor); + write_enable(nor); + + nor->cmd_buf[0] = val | SR_QUAD_EN_MX; + nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0); + + if (wait_till_ready(nor)) + return 1; + + ret = read_sr(nor); + if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { + dev_err(nor->dev, "Macronix Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Write status Register and configuration register with 2 bytes + * The first byte will be written to the status register, while the + * second byte will be written to the configuration register. + * Return negative if error occured. + */ +static int write_sr_cr(struct spi_nor *nor, u16 val) +{ + nor->cmd_buf[0] = val & 0xff; + nor->cmd_buf[1] = (val >> 8); + + return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0); +} + +static int spansion_quad_enable(struct spi_nor *nor) +{ + int ret; + int quad_en = CR_QUAD_EN_SPAN << 8; + + write_enable(nor); + + ret = write_sr_cr(nor, quad_en); + if (ret < 0) { + dev_err(nor->dev, + "error while writing configuration register\n"); + return -EINVAL; + } + + /* read back and check it */ + ret = read_cr(nor); + if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) { + dev_err(nor->dev, "Spansion Quad bit not set\n"); + return -EINVAL; + } + + return 0; +} + +static int set_ddr_quad_mode(struct spi_nor *nor, u32 jedec_id) +{ + int status; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_AMD: /* Spansion, actually */ + status = spansion_quad_enable(nor); + if (status) { + dev_err(nor->dev, + "Spansion DDR quad-read not enabled\n"); + return status; + } + return status; + case CFI_MFR_ST: /* Micron, actually */ + /* DTR quad read works with the Extended SPI protocol. */ + return 0; + default: + return -EINVAL; + } +} + +static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) +{ + int status; + + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_MACRONIX: + status = macronix_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Macronix quad-read not enabled\n"); + return -EINVAL; + } + return status; + default: + status = spansion_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Spansion quad-read not enabled\n"); + return -EINVAL; + } + return status; + } +} + +static int spi_nor_check(struct spi_nor *nor) +{ + if (!nor->dev || !nor->read || !nor->write || + !nor->read_reg || !nor->write_reg || !nor->erase) { + pr_err("spi-nor: please fill all the necessary fields!\n"); + return -EINVAL; + } + + if (!nor->read_id) + nor->read_id = spi_nor_read_id; + if (!nor->wait_till_ready) + nor->wait_till_ready = spi_nor_wait_till_ready; + + return 0; +} + +int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, + enum read_mode mode) +{ + struct flash_info *info; + struct flash_platform_data *data; + struct device *dev = nor->dev; + struct mtd_info *mtd = nor->mtd; + struct device_node *np = dev->of_node; + int ret; + int i; + + ret = spi_nor_check(nor); + if (ret) + return ret; + + /* Platform data helps sort out which chip type we have, as + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. + */ + data = dev_get_platdata(dev); + if (data && data->type) { + const struct spi_device_id *plat_id; + + for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { + plat_id = &spi_nor_ids[i]; + if (strcmp(data->type, plat_id->name)) + continue; + break; + } + + if (i < ARRAY_SIZE(spi_nor_ids) - 1) + id = plat_id; + else + dev_warn(dev, "unrecognized id %s\n", data->type); + } + + info = (void *)id->driver_data; + + if (info->jedec_id) { + const struct spi_device_id *jid; + + jid = jedec_probe(nor); + if (IS_ERR(jid)) { + return PTR_ERR(jid); + } else if (jid != id) { + /* + * JEDEC knows better, so overwrite platform ID. We + * can't trust partitions any longer, but we'll let + * mtd apply them anyway, since some partitions may be + * marked read-only, and we don't want to lose that + * information, even if it's not 100% accurate. + */ + dev_warn(dev, "found %s, expected %s\n", + jid->name, id->name); + id = jid; + info = (void *)jid->driver_data; + } + } + + mutex_init(&nor->lock); + + /* + * Atmel, SST and Intel/Numonyx serial nor tend to power + * up with the software protection bits set + */ + + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL || + JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) { + write_enable(nor); + write_sr(nor, 0); + } + + if (data && data->name) + mtd->name = data->name; + else + mtd->name = dev_name(dev); + + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; + mtd->flags = MTD_CAP_NORFLASH; + mtd->size = info->sector_size * info->n_sectors; + mtd->_erase = spi_nor_erase; + mtd->_read = spi_nor_read; + + /* nor protection support for STmicro chips */ + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; + } + + /* sst nor chips use AAI word program */ + if (info->flags & SST_WRITE) + mtd->_write = sst_write; + else + mtd->_write = spi_nor_write; + + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + nor->erase_opcode = SPINOR_OP_BE_4K; + mtd->erasesize = 4096; + } else if (info->flags & SECT_4K_PMC) { + nor->erase_opcode = SPINOR_OP_BE_4K_PMC; + mtd->erasesize = 4096; + } else { + nor->erase_opcode = SPINOR_OP_SE; + mtd->erasesize = info->sector_size; + } + + if (info->flags & SPI_NOR_NO_ERASE) + mtd->flags |= MTD_NO_ERASE; + + mtd->dev.parent = dev; + nor->page_size = info->page_size; + mtd->writebufsize = nor->page_size; + + if (np) { + /* If we were instantiated by DT, use it */ + if (of_property_read_bool(np, "m25p,fast-read")) + nor->flash_read = SPI_NOR_FAST; + else + nor->flash_read = SPI_NOR_NORMAL; + } else { + /* If we weren't instantiated by DT, default to fast-read */ + nor->flash_read = SPI_NOR_FAST; + } + + /* Some devices cannot do fast-read, no matter what DT tells us */ + if (info->flags & SPI_NOR_NO_FR) + nor->flash_read = SPI_NOR_NORMAL; + + /* DDR Quad/Quad/Dual-read mode takes precedence over fast/normal */ + if (mode == SPI_NOR_DDR_QUAD && info->flags & SPI_NOR_DDR_QUAD_READ) { + ret = set_ddr_quad_mode(nor, info->jedec_id); + if (ret) { + dev_err(dev, "DDR quad mode not supported\n"); + return ret; + } + nor->flash_read = SPI_NOR_DDR_QUAD; + } else if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { + ret = set_quad_mode(nor, info->jedec_id); + if (ret) { + dev_err(dev, "quad mode not supported\n"); + return ret; + } + nor->flash_read = SPI_NOR_QUAD; + } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { + nor->flash_read = SPI_NOR_DUAL; + } + + /* Default commands */ + switch (nor->flash_read) { + case SPI_NOR_DDR_QUAD: + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { /* Spansion */ + nor->read_opcode = SPINOR_OP_READ_1_4_4_D; + } else if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { + nor->read_opcode = SPINOR_OP_READ_1_1_4_D; + } else { + dev_err(dev, "DDR Quad Read is not supported.\n"); + return -EINVAL; + } + break; + case SPI_NOR_QUAD: + nor->read_opcode = SPINOR_OP_READ_1_1_4; + break; + case SPI_NOR_DUAL: + nor->read_opcode = SPINOR_OP_READ_1_1_2; + break; + case SPI_NOR_FAST: + nor->read_opcode = SPINOR_OP_READ_FAST; + break; + case SPI_NOR_NORMAL: + nor->read_opcode = SPINOR_OP_READ; + break; + default: + dev_err(dev, "No Read opcode defined\n"); + return -EINVAL; + } + + nor->program_opcode = SPINOR_OP_PP; + + if (info->addr_width) + nor->addr_width = info->addr_width; + else if (mtd->size > 0x1000000) { + /* enable 4-byte addressing if the device exceeds 16MiB */ + nor->addr_width = 4; + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + switch (nor->flash_read) { + case SPI_NOR_DDR_QUAD: + nor->read_opcode = SPINOR_OP_READ4_1_4_4_D; + break; + case SPI_NOR_QUAD: + nor->read_opcode = SPINOR_OP_READ4_1_1_4; + break; + case SPI_NOR_DUAL: + nor->read_opcode = SPINOR_OP_READ4_1_1_2; + break; + case SPI_NOR_FAST: + nor->read_opcode = SPINOR_OP_READ4_FAST; + break; + case SPI_NOR_NORMAL: + nor->read_opcode = SPINOR_OP_READ4; + break; + } + nor->program_opcode = SPINOR_OP_PP_4B; + /* No small sector erase for 4-byte command set */ + nor->erase_opcode = SPINOR_OP_SE_4B; + mtd->erasesize = info->sector_size; + } else + set_4byte(nor, info->jedec_id, 1); + } else { + nor->addr_width = 3; + } + + nor->read_dummy = spi_nor_read_dummy_cycles(nor); + + dev_info(dev, "%s (%lld Kbytes)\n", id->name, + (long long)mtd->size >> 10); + + dev_dbg(dev, + "mtd .name = %s, .size = 0x%llx (%lldMiB), " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", + mtd->name, (long long)mtd->size, (long long)(mtd->size >> 20), + mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions); + + if (mtd->numeraseregions) + for (i = 0; i < mtd->numeraseregions; i++) + dev_dbg(dev, + "mtd.eraseregions[%d] = { .offset = 0x%llx, " + ".erasesize = 0x%.8x (%uKiB), " + ".numblocks = %d }\n", + i, (long long)mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].erasesize / 1024, + mtd->eraseregions[i].numblocks); + return 0; +} +EXPORT_SYMBOL_GPL(spi_nor_scan); + +const struct spi_device_id *spi_nor_match_id(char *name) +{ + const struct spi_device_id *id = spi_nor_ids; + + while (id->name[0]) { + if (!strcmp(name, id->name)) + return id; + id++; + } + return NULL; +} +EXPORT_SYMBOL_GPL(spi_nor_match_id); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>"); +MODULE_AUTHOR("Mike Lavender"); +MODULE_DESCRIPTION("framework for SPI NOR"); diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 3c06947..5115a36 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -108,6 +108,18 @@ config CAN_FLEXCAN ---help--- Say Y here if you want to support for Freescale FlexCAN. +config CAN_FLEXCAN_RX_FIFO_EN + tristate "Support for RX FIFO mode in Freescale FLEXCAN based chips" + depends on CAN_FLEXCAN + ---help--- + Say Y here if your Freescale SoC's FlexCAN module has support for RX FIFO MODE. + +config CAN_FLEXCAN_ERRATA_ERR005829 + tristate "ERRATA_ERR005829 applicable to certain variants of Freescale FLEXCAN based chips" + depends on CAN_FLEXCAN + ---help--- + Say Y here if your Freescale SoC's FlexCAN module needs s/w workaround for ERRATA ERR005829. + config PCH_CAN tristate "Intel EG20T PCH CAN controller" depends on PCI diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index e381142..805a923 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -59,10 +59,11 @@ #define FLEXCAN_MCR_WAK_SRC BIT(19) #define FLEXCAN_MCR_DOZE BIT(18) #define FLEXCAN_MCR_SRX_DIS BIT(17) +#define FLEXCAN_MCR_SRX_EN ~(BIT(17)) #define FLEXCAN_MCR_BCC BIT(16) #define FLEXCAN_MCR_LPRIO_EN BIT(13) #define FLEXCAN_MCR_AEN BIT(12) -#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x1f) +#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f) #define FLEXCAN_MCR_IDAM_A (0 << 8) #define FLEXCAN_MCR_IDAM_B (1 << 8) #define FLEXCAN_MCR_IDAM_C (2 << 8) @@ -92,6 +93,27 @@ #define FLEXCAN_CTRL_ERR_ALL \ (FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE) +/* FLEXCAN control register 2 (CTRL2) bits */ +#define FLEXCAN_CRL2_ECRWRE BIT(29) +#define FLEXCAN_CRL2_WRMFRZ BIT(28) +#define FLEXCAN_CRL2_RFFN(x) (((x) & 0x0f) << 24) +#define FLEXCAN_CRL2_TASD(x) (((x) & 0x1f) << 19) +#define FLEXCAN_CRL2_MRP BIT(18) +#define FLEXCAN_CRL2_RRS BIT(17) +#define FLEXCAN_CRL2_EACEN BIT(16) + +/* FLEXCAN memory error control register (MECR) bits */ +#define FLEXCAN_MECR_ECRWRDIS BIT(31) +#define FLEXCAN_MECR_HANCEI_MSK BIT(19) +#define FLEXCAN_MECR_FANCEI_MSK BIT(18) +#define FLEXCAN_MECR_CEI_MSK BIT(16) +#define FLEXCAN_MECR_HAERRIE BIT(15) +#define FLEXCAN_MECR_FAERRIE BIT(14) +#define FLEXCAN_MECR_EXTERRIE BIT(13) +#define FLEXCAN_MECR_RERRDIS BIT(9) +#define FLEXCAN_MECR_ECCDIS BIT(8) +#define FLEXCAN_MECR_NCEFAFRZ BIT(7) + /* FLEXCAN error and status register (ESR) bits */ #define FLEXCAN_ESR_TWRN_INT BIT(17) #define FLEXCAN_ESR_RWRN_INT BIT(16) @@ -125,17 +147,39 @@ FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT) /* FLEXCAN interrupt flag register (IFLAG) bits */ +#ifdef CONFIG_CAN_FLEXCAN_ERRATA_ERR005829 +/* Errata ERR005829 step7: Reserve first valid MB */ +#define FLEXCAN_TX_BUF_RESERVED 8 +#define FLEXCAN_TX_BUF_ID 9 +#else #define FLEXCAN_TX_BUF_ID 8 +#endif #define FLEXCAN_IFLAG_BUF(x) BIT(x) #define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7) #define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6) #define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5) +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN #define FLEXCAN_IFLAG_DEFAULT \ (FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | FLEXCAN_IFLAG_RX_FIFO_AVAILABLE | \ FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID)) +#else +#define FLEXCAN_IFLAG_DEFAULT \ + (FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID)) +#endif /* FLEXCAN message buffers */ #define FLEXCAN_MB_CNT_CODE(x) (((x) & 0xf) << 24) +#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24) +#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24) +#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24) +#define FLEXCAN_MB_CODE_RX_OVERRRUN (0x6 << 24) +#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24) + +#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24) +#define FLEXCAN_MB_CODE_TX_ABORT (0x9 << 24) +#define FLEXCAN_MB_CODE_TX_DATA (0xc << 24) +#define FLEXCAN_MB_CODE_TX_TANSWER (0xe << 24) + #define FLEXCAN_MB_CNT_SRR BIT(22) #define FLEXCAN_MB_CNT_IDE BIT(21) #define FLEXCAN_MB_CNT_RTR BIT(20) @@ -150,18 +194,20 @@ * FLEXCAN hardware feature flags * * Below is some version info we got: - * SOC Version IP-Version Glitch- [TR]WRN_INT - * Filter? connected? - * MX25 FlexCAN2 03.00.00.00 no no - * MX28 FlexCAN2 03.00.04.00 yes yes - * MX35 FlexCAN2 03.00.00.00 no no - * MX53 FlexCAN2 03.00.00.00 yes no - * MX6s FlexCAN3 10.00.12.00 yes yes + * SOC Version IP-Version Glitch- [TR]WRN_INT Memory err + * Filter? connected? detection + * MX25 FlexCAN2 03.00.00.00 no no no + * MX28 FlexCAN2 03.00.04.00 yes yes no + * MX35 FlexCAN2 03.00.00.00 no no no + * MX53 FlexCAN2 03.00.00.00 yes no no + * MX6s FlexCAN3 10.00.12.00 yes yes no + * VF610 FlexCAN3 ? no yes yes * * Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected. */ #define FLEXCAN_HAS_V10_FEATURES BIT(1) /* For core version >= 10 */ #define FLEXCAN_HAS_BROKEN_ERR_STATE BIT(2) /* [TR]WRN_INT not connected */ +#define FLEXCAN_HAS_MECR_FEATURES BIT(3) /* Memory error detection */ /* Structure of the message buffer */ struct flexcan_mb { @@ -192,8 +238,17 @@ struct flexcan_regs { u32 crcr; /* 0x44 */ u32 rxfgmask; /* 0x48 */ u32 rxfir; /* 0x4c */ - u32 _reserved3[12]; - struct flexcan_mb cantxfg[64]; + u32 _reserved3[12]; /* 0x50 */ + struct flexcan_mb cantxfg[64]; /* 0x80 */ + u32 _reserved4[408]; + u32 mecr; /* 0xae0 */ + u32 erriar; /* 0xae4 */ + u32 erridpr; /* 0xae8 */ + u32 errippr; /* 0xaec */ + u32 rerrar; /* 0xaf0 */ + u32 rerrdr; /* 0xaf4 */ + u32 rerrsynr; /* 0xaf8 */ + u32 errsr; /* 0xafc */ }; struct flexcan_devtype_data { @@ -214,6 +269,10 @@ struct flexcan_priv { struct flexcan_platform_data *pdata; const struct flexcan_devtype_data *devtype_data; struct regulator *reg_xceiver; + + /* Read and Write APIs */ + u32 (*read) (void __iomem *addr); + void (*write) (u32 val, void __iomem *addr); }; static struct flexcan_devtype_data fsl_p1010_devtype_data = { @@ -224,6 +283,14 @@ static struct flexcan_devtype_data fsl_imx6q_devtype_data = { .features = FLEXCAN_HAS_V10_FEATURES, }; +static struct flexcan_devtype_data fsl_vf610_devtype_data = { + .features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES, +}; + +static struct flexcan_devtype_data fsl_ls1021a_devtype_data = { + .features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES, +}; + static const struct can_bittiming_const flexcan_bittiming_const = { .name = DRV_NAME, .tseg1_min = 4, @@ -237,29 +304,38 @@ static const struct can_bittiming_const flexcan_bittiming_const = { }; /* - * Abstract off the read/write for arm versus ppc. + * FlexCAN module is essentially modelled as a little-endian IP in most + * SoCs, i.e the registers as well as the message buffer areas are + * implemented in a little-endian fashion. + * + * However there are some SoCs (e.g. LS1021A) which implement the FlexCAN + * module in a big-endian fashion (i.e the registers as well as the + * message buffer areas are implemented in a big-endian way). + * + * In addition, the FlexCAN module can be found on SoCs having ARM or + * PPC cores. So, we need to abstract off the register read/write + * functions, ensuring that these cater to all the combinations of module + * endianess and underlying CPU endianess. */ -#if defined(__BIG_ENDIAN) -static inline u32 flexcan_read(void __iomem *addr) +static inline u32 flexcan_read_le(void __iomem *addr) { - return in_be32(addr); + return ioread32(addr); } -static inline void flexcan_write(u32 val, void __iomem *addr) +static inline void flexcan_write_le(u32 val, void __iomem *addr) { - out_be32(addr, val); + iowrite32(val, addr); } -#else -static inline u32 flexcan_read(void __iomem *addr) + +static inline u32 flexcan_read_be(void __iomem *addr) { - return readl(addr); + return ioread32be(addr); } -static inline void flexcan_write(u32 val, void __iomem *addr) +static inline void flexcan_write_be(u32 val, void __iomem *addr) { - writel(val, addr); + iowrite32be(val, addr); } -#endif static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv) { @@ -290,14 +366,14 @@ static int flexcan_chip_enable(struct flexcan_priv *priv) unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; - reg = flexcan_read(®s->mcr); + reg = priv->read(®s->mcr); reg &= ~FLEXCAN_MCR_MDIS; - flexcan_write(reg, ®s->mcr); + priv->write(reg, ®s->mcr); - while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) - usleep_range(10, 20); + while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + udelay(10); - if (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK) + if (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK) return -ETIMEDOUT; return 0; @@ -309,25 +385,80 @@ static int flexcan_chip_disable(struct flexcan_priv *priv) unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; - reg = flexcan_read(®s->mcr); + reg = priv->read(®s->mcr); reg |= FLEXCAN_MCR_MDIS; - flexcan_write(reg, ®s->mcr); + priv->write(reg, ®s->mcr); - while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) - usleep_range(10, 20); + while (timeout-- && !(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + udelay(10); - if (!(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + if (!(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) return -ETIMEDOUT; return 0; } -static int flexcan_get_berr_counter(const struct net_device *dev, - struct can_berr_counter *bec) +static int flexcan_chip_freeze(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate; + u32 reg; + + reg = priv->read(®s->mcr); + reg |= FLEXCAN_MCR_HALT; + priv->write(reg, ®s->mcr); + + while (timeout-- && !(priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + udelay(100); + + if (!(priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_unfreeze(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + u32 reg; + + reg = priv->read(®s->mcr); + reg &= ~FLEXCAN_MCR_HALT; + reg &= ~FLEXCAN_MCR_FRZ; + priv->write(reg, ®s->mcr); + + while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + udelay(10); + + if (priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_softreset(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + + priv->write(FLEXCAN_MCR_SOFTRST, ®s->mcr); + while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_SOFTRST)) + udelay(10); + + if (priv->read(®s->mcr) & FLEXCAN_MCR_SOFTRST) + return -ETIMEDOUT; + + return 0; +} + + +static int __flexcan_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) { const struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; - u32 reg = flexcan_read(®s->ecr); + u32 reg = priv->read(®s->ecr); bec->txerr = (reg >> 0) & 0xff; bec->rxerr = (reg >> 8) & 0xff; @@ -335,6 +466,29 @@ static int flexcan_get_berr_counter(const struct net_device *dev, return 0; } +static int flexcan_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + const struct flexcan_priv *priv = netdev_priv(dev); + int err; + + err = clk_prepare_enable(priv->clk_ipg); + if (err) + return err; + + err = clk_prepare_enable(priv->clk_per); + if (err) + goto out_disable_ipg; + + err = __flexcan_get_berr_counter(dev, bec); + + clk_disable_unprepare(priv->clk_per); + out_disable_ipg: + clk_disable_unprepare(priv->clk_ipg); + + return err; +} + static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) { const struct flexcan_priv *priv = netdev_priv(dev); @@ -360,17 +514,30 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) if (cf->can_dlc > 0) { u32 data = be32_to_cpup((__be32 *)&cf->data[0]); - flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]); + priv->write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[0]); } if (cf->can_dlc > 3) { u32 data = be32_to_cpup((__be32 *)&cf->data[4]); - flexcan_write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]); + priv->write(data, ®s->cantxfg[FLEXCAN_TX_BUF_ID].data[1]); } can_put_echo_skb(skb, dev, 0); - flexcan_write(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id); - flexcan_write(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); + priv->write(can_id, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_id); + priv->write(ctrl, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); +#ifdef CONFIG_CAN_FLEXCAN_ERRATA_ERR005829 + /* Errata ERR005829 step8: + * Write twice INACTIVE(0x8) code to first MB. + */ + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); +#endif + +#ifndef CONFIG_CAN_FLEXCAN_RX_FIFO_EN + netdev_info(dev, "XMIT done\n"); +#endif return NETDEV_TX_OK; } @@ -447,7 +614,7 @@ static void do_state(struct net_device *dev, struct flexcan_priv *priv = netdev_priv(dev); struct can_berr_counter bec; - flexcan_get_berr_counter(dev, &bec); + __flexcan_get_berr_counter(dev, &bec); switch (priv->can.state) { case CAN_STATE_ERROR_ACTIVE: @@ -493,6 +660,13 @@ static void do_state(struct net_device *dev, /* process state changes depending on the new state */ switch (new_state) { + case CAN_STATE_ERROR_WARNING: + netdev_dbg(dev, "Error Warning\n"); + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (bec.txerr > bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + break; case CAN_STATE_ERROR_ACTIVE: netdev_dbg(dev, "Error Active\n"); cf->can_id |= CAN_ERR_PROT; @@ -545,6 +719,7 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr) return 1; } +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN static void flexcan_read_fifo(const struct net_device *dev, struct can_frame *cf) { @@ -553,8 +728,35 @@ static void flexcan_read_fifo(const struct net_device *dev, struct flexcan_mb __iomem *mb = ®s->cantxfg[0]; u32 reg_ctrl, reg_id; - reg_ctrl = flexcan_read(&mb->can_ctrl); - reg_id = flexcan_read(&mb->can_id); + reg_ctrl = priv->read(&mb->can_ctrl); + reg_id = priv->read(&mb->can_id); + if (reg_ctrl & FLEXCAN_MB_CNT_IDE) + cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (reg_id >> 18) & CAN_SFF_MASK; + + if (reg_ctrl & FLEXCAN_MB_CNT_RTR) + cf->can_id |= CAN_RTR_FLAG; + cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf); + + *(__be32 *)(cf->data + 0) = cpu_to_be32(priv->read(&mb->data[0])); + *(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1])); + + /* mark as read */ + priv->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); + priv->read(®s->timer); +} +#else +static void flexcan_read_mailbox(const struct net_device *dev, + struct can_frame *cf, u32 mailbox) +{ + const struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->base; + struct flexcan_mb __iomem *mb = ®s->cantxfg[mailbox]; + u32 reg_ctrl, reg_id; + + reg_ctrl = priv->read(&mb->can_ctrl); + reg_id = priv->read(&mb->can_id); if (reg_ctrl & FLEXCAN_MB_CNT_IDE) cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; else @@ -564,15 +766,17 @@ static void flexcan_read_fifo(const struct net_device *dev, cf->can_id |= CAN_RTR_FLAG; cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf); - *(__be32 *)(cf->data + 0) = cpu_to_be32(flexcan_read(&mb->data[0])); - *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1])); + *(__be32 *)(cf->data + 0) = cpu_to_be32(priv->read(&mb->data[0])); + *(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1])); /* mark as read */ - flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); - flexcan_read(®s->timer); + priv->write(BIT(mailbox), ®s->iflag1); + priv->read(®s->timer); } +#endif -static int flexcan_read_frame(struct net_device *dev) +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN +static int flexcan_read_frame_fifo_mode(struct net_device *dev) { struct net_device_stats *stats = &dev->stats; struct can_frame *cf; @@ -585,6 +789,31 @@ static int flexcan_read_frame(struct net_device *dev) } flexcan_read_fifo(dev, cf); + + netif_receive_skb(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + can_led_event(dev, CAN_LED_EVENT_RX); + + return 1; +} +#else +static int flexcan_read_frame_legacy_mode(struct net_device *dev, u32 mailbox) +{ + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + skb = alloc_can_skb(dev, &cf); + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + flexcan_read_mailbox(dev, cf, mailbox); + netif_receive_skb(skb); stats->rx_packets++; @@ -594,6 +823,7 @@ static int flexcan_read_frame(struct net_device *dev) return 1; } +#endif static int flexcan_poll(struct napi_struct *napi, int quota) { @@ -607,18 +837,33 @@ static int flexcan_poll(struct napi_struct *napi, int quota) * The error bits are cleared on read, * use saved value from irq handler. */ - reg_esr = flexcan_read(®s->esr) | priv->reg_esr; + reg_esr = priv->read(®s->esr) | priv->reg_esr; /* handle state changes */ work_done += flexcan_poll_state(dev, reg_esr); +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN /* handle RX-FIFO */ - reg_iflag1 = flexcan_read(®s->iflag1); + reg_iflag1 = priv->read(®s->iflag1); while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE && work_done < quota) { - work_done += flexcan_read_frame(dev); - reg_iflag1 = flexcan_read(®s->iflag1); + work_done += flexcan_read_frame_fifo_mode(dev); + reg_iflag1 = priv->read(®s->iflag1); } +#else + unsigned long iflag1; + u32 mailbox; + + /* handle RX-Buffers */ + reg_iflag1 = priv->read(®s->iflag1); + iflag1 = reg_iflag1 & (FLEXCAN_TX_BUF_ID - 1); + while ((reg_iflag1 & (FLEXCAN_TX_BUF_ID - 1)) && + work_done < quota) { + mailbox = find_first_bit(&iflag1, (FLEXCAN_TX_BUF_ID - 1)); + work_done += flexcan_read_frame_legacy_mode(dev, mailbox); + reg_iflag1 = priv->read(®s->iflag1); + } +#endif /* report bus errors */ if (flexcan_has_and_handle_berr(priv, reg_esr) && work_done < quota) @@ -627,8 +872,8 @@ static int flexcan_poll(struct napi_struct *napi, int quota) if (work_done < quota) { napi_complete(napi); /* enable IRQs */ - flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); - flexcan_write(priv->reg_ctrl_default, ®s->ctrl); + priv->write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); + priv->write(priv->reg_ctrl_default, ®s->ctrl); } return work_done; @@ -642,11 +887,11 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) struct flexcan_regs __iomem *regs = priv->base; u32 reg_iflag1, reg_esr; - reg_iflag1 = flexcan_read(®s->iflag1); - reg_esr = flexcan_read(®s->esr); + reg_iflag1 = priv->read(®s->iflag1); + reg_esr = priv->read(®s->esr); /* ACK all bus error and state change IRQ sources */ if (reg_esr & FLEXCAN_ESR_ALL_INT) - flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); + priv->write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr); /* * schedule NAPI in case of: @@ -654,6 +899,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) * - state change IRQ * - bus error IRQ and bus error reporting is activated */ +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) || (reg_esr & FLEXCAN_ESR_ERR_STATE) || flexcan_has_and_handle_berr(priv, reg_esr)) { @@ -662,26 +908,45 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) * save them for later use. */ priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS; - flexcan_write(FLEXCAN_IFLAG_DEFAULT & + priv->write(FLEXCAN_IFLAG_DEFAULT & ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); - flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, + priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, ®s->ctrl); napi_schedule(&priv->napi); } /* FIFO overflow */ if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) { - flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); + priv->write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); dev->stats.rx_over_errors++; dev->stats.rx_errors++; } +#else + if ((reg_iflag1 & (FLEXCAN_TX_BUF_ID - 1)) || + (reg_esr & FLEXCAN_ESR_ERR_STATE) || + flexcan_has_and_handle_berr(priv, reg_esr)) { + /* + * The error bits are cleared on read, + * save them for later use. + */ + priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS; + priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, + ®s->ctrl); + napi_schedule(&priv->napi); + } +#endif /* transmission complete interrupt */ if (reg_iflag1 & (1 << FLEXCAN_TX_BUF_ID)) { stats->tx_bytes += can_get_echo_skb(dev, 0); stats->tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); - flexcan_write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1); +#ifndef CONFIG_CAN_FLEXCAN_RX_FIFO_EN + /* after sending a RTR frame mailbox is in RX mode */ + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); +#endif + priv->write((1 << FLEXCAN_TX_BUF_ID), ®s->iflag1); netif_wake_queue(dev); } @@ -695,7 +960,7 @@ static void flexcan_set_bittiming(struct net_device *dev) struct flexcan_regs __iomem *regs = priv->base; u32 reg; - reg = flexcan_read(®s->ctrl); + reg = priv->read(®s->ctrl); reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) | FLEXCAN_CTRL_RJW(0x3) | FLEXCAN_CTRL_PSEG1(0x7) | @@ -719,11 +984,11 @@ static void flexcan_set_bittiming(struct net_device *dev) reg |= FLEXCAN_CTRL_SMP; netdev_info(dev, "writing ctrl=0x%08x\n", reg); - flexcan_write(reg, ®s->ctrl); + priv->write(reg, ®s->ctrl); /* print chip status */ netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__, - flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); + priv->read(®s->mcr), priv->read(®s->ctrl)); } /* @@ -737,7 +1002,8 @@ static int flexcan_chip_start(struct net_device *dev) struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; int err; - u32 reg_mcr, reg_ctrl; + u32 reg_mcr, reg_ctrl, reg_crl2, reg_mecr; + int i; /* enable module */ err = flexcan_chip_enable(priv); @@ -745,16 +1011,9 @@ static int flexcan_chip_start(struct net_device *dev) return err; /* soft reset */ - flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr); - udelay(10); - - reg_mcr = flexcan_read(®s->mcr); - if (reg_mcr & FLEXCAN_MCR_SOFTRST) { - netdev_err(dev, "Failed to softreset can module (mcr=0x%08x)\n", - reg_mcr); - err = -ENODEV; - goto out; - } + err = flexcan_chip_softreset(priv); + if (err) + goto out_chip_disable; flexcan_set_bittiming(dev); @@ -770,14 +1029,22 @@ static int flexcan_chip_start(struct net_device *dev) * disable local echo * */ - reg_mcr = flexcan_read(®s->mcr); + reg_mcr = priv->read(®s->mcr); reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); +#else + reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | + FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | + FLEXCAN_MCR_IDAM_A | + FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID); + reg_mcr &= FLEXCAN_MCR_SRX_EN; +#endif netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); - flexcan_write(reg_mcr, ®s->mcr); + priv->write(reg_mcr, ®s->mcr); /* * CTRL @@ -791,7 +1058,7 @@ static int flexcan_chip_start(struct net_device *dev) * enable bus off interrupt * (== FLEXCAN_CTRL_ERR_STATE) */ - reg_ctrl = flexcan_read(®s->ctrl); + reg_ctrl = priv->read(®s->ctrl); reg_ctrl &= ~FLEXCAN_CTRL_TSYN; reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF | FLEXCAN_CTRL_ERR_STATE; @@ -803,45 +1070,105 @@ static int flexcan_chip_start(struct net_device *dev) if (priv->devtype_data->features & FLEXCAN_HAS_BROKEN_ERR_STATE || priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) reg_ctrl |= FLEXCAN_CTRL_ERR_MSK; + else + reg_ctrl &= ~FLEXCAN_CTRL_ERR_MSK; /* save for later use */ priv->reg_ctrl_default = reg_ctrl; netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl); - flexcan_write(reg_ctrl, ®s->ctrl); + priv->write(reg_ctrl, ®s->ctrl); + + /* clear and invalidate all mailboxes first */ +#ifndef CONFIG_CAN_FLEXCAN_RX_FIFO_EN + for (i = 0; i < ARRAY_SIZE(regs->cantxfg); i++) { + priv->write(0, ®s->cantxfg[i].can_ctrl); + priv->write(0, ®s->cantxfg[i].can_id); + priv->write(0, ®s->cantxfg[i].data[0]); + priv->write(0, ®s->cantxfg[i].data[1]); + + /* put MB into rx queue */ + priv->write(FLEXCAN_MB_CNT_CODE(0x4), + ®s->cantxfg[i].can_ctrl); + } +#else + for (i = FLEXCAN_TX_BUF_ID; i < ARRAY_SIZE(regs->cantxfg); i++) { + priv->write(FLEXCAN_MB_CODE_RX_INACTIVE, + ®s->cantxfg[i].can_ctrl); + } +#endif - /* Abort any pending TX, mark Mailbox as INACTIVE */ - flexcan_write(FLEXCAN_MB_CNT_CODE(0x4), +#ifdef CONFIG_CAN_FLEXCAN_ERRATA_ERR005829 + /* Errata ERR005829: mark first TX mailbox as INACTIVE */ + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, + ®s->cantxfg[FLEXCAN_TX_BUF_RESERVED].can_ctrl); +#endif + +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN + /* mark TX mailbox as INACTIVE */ + priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, ®s->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl); +#endif /* acceptance mask/acceptance code (accept everything) */ - flexcan_write(0x0, ®s->rxgmask); - flexcan_write(0x0, ®s->rx14mask); - flexcan_write(0x0, ®s->rx15mask); + priv->write(0x0, ®s->rxgmask); + priv->write(0x0, ®s->rx14mask); + priv->write(0x0, ®s->rx15mask); +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) - flexcan_write(0x0, ®s->rxfgmask); + priv->write(0x0, ®s->rxfgmask); +#else + priv->write(0x0, ®s->rxfgmask); +#endif + + /* + * On Vybrid, disable memory error detection interrupts + * and freeze mode. + * This also works around errata e5295 which generates + * false positive memory errors and put the device in + * freeze mode. + */ + if (priv->devtype_data->features & FLEXCAN_HAS_MECR_FEATURES) { + /* + * Follow the protocol as described in "Detection + * and Correction of Memory Errors" to write to + * MECR register + */ + reg_crl2 = priv->read(®s->crl2); + reg_crl2 |= FLEXCAN_CRL2_ECRWRE; + priv->write(reg_crl2, ®s->crl2); + + reg_mecr = priv->read(®s->mecr); + reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS; + priv->write(reg_mecr, ®s->mecr); + reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK | + FLEXCAN_MECR_FANCEI_MSK); + priv->write(reg_mecr, ®s->mecr); + } err = flexcan_transceiver_enable(priv); if (err) - goto out; + goto out_chip_disable; /* synchronize with the can bus */ - reg_mcr = flexcan_read(®s->mcr); - reg_mcr &= ~FLEXCAN_MCR_HALT; - flexcan_write(reg_mcr, ®s->mcr); + err = flexcan_chip_unfreeze(priv); + if (err) + goto out_transceiver_disable; priv->can.state = CAN_STATE_ERROR_ACTIVE; /* enable FIFO interrupts */ - flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); + priv->write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); /* print chip status */ netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__, - flexcan_read(®s->mcr), flexcan_read(®s->ctrl)); + priv->read(®s->mcr), priv->read(®s->ctrl)); return 0; - out: + out_transceiver_disable: + flexcan_transceiver_disable(priv); + out_chip_disable: flexcan_chip_disable(priv); return err; } @@ -856,16 +1183,14 @@ static void flexcan_chip_stop(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; - u32 reg; - /* Disable + halt module */ - reg = flexcan_read(®s->mcr); - reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT; - flexcan_write(reg, ®s->mcr); + /* freeze + disable module */ + flexcan_chip_freeze(priv); + flexcan_chip_disable(priv); /* Disable all interrupts */ - flexcan_write(0, ®s->imask1); - flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, + priv->write(0, ®s->imask1); + priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, ®s->ctrl); flexcan_transceiver_disable(priv); @@ -982,31 +1307,40 @@ static int register_flexcandev(struct net_device *dev) err = flexcan_chip_disable(priv); if (err) goto out_disable_per; - reg = flexcan_read(®s->ctrl); + reg = priv->read(®s->ctrl); reg |= FLEXCAN_CTRL_CLK_SRC; - flexcan_write(reg, ®s->ctrl); + priv->write(reg, ®s->ctrl); err = flexcan_chip_enable(priv); if (err) goto out_chip_disable; /* set freeze, halt and activate FIFO, restrict register access */ - reg = flexcan_read(®s->mcr); + reg = priv->read(®s->mcr); +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV; - flexcan_write(reg, ®s->mcr); +#else + reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV; +#endif + priv->write(reg, ®s->mcr); /* * Currently we only support newer versions of this core * featuring a RX FIFO. Older cores found on some Coldfire * derivates are not yet supported. */ - reg = flexcan_read(®s->mcr); + reg = priv->read(®s->mcr); +#ifdef CONFIG_CAN_FLEXCAN_RX_FIFO_EN if (!(reg & FLEXCAN_MCR_FEN)) { netdev_err(dev, "Could not enable RX FIFO, unsupported core\n"); err = -ENODEV; goto out_chip_disable; } +#else + if (!(reg & FLEXCAN_MCR_FEN)) + netdev_info(dev, "Legacy mode (non-RX FIFO) supported\n"); +#endif err = register_candev(dev); @@ -1029,7 +1363,10 @@ static void unregister_flexcandev(struct net_device *dev) static const struct of_device_id flexcan_of_match[] = { { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, }, { .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, }, + { .compatible = "fsl,ls1021a-flexcan", + .data = &fsl_ls1021a_devtype_data, }, { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, }, + { .compatible = "fsl,vf610-flexcan", .data = &fsl_vf610_devtype_data, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, flexcan_of_match); @@ -1051,6 +1388,8 @@ static int flexcan_probe(struct platform_device *pdev) void __iomem *base; int err, irq; u32 clock_freq = 0; + /* Default case for most ARM based FSL SoC having BE FlexCAN IP */ + bool core_is_little = true, module_is_little = false; if (pdev->dev.of_node) of_property_read_u32(pdev->dev.of_node, @@ -1083,9 +1422,9 @@ static int flexcan_probe(struct platform_device *pdev) of_id = of_match_device(flexcan_of_match, &pdev->dev); if (of_id) { devtype_data = of_id->data; - } else if (pdev->id_entry->driver_data) { + } else if (platform_get_device_id(pdev)->driver_data) { devtype_data = (struct flexcan_devtype_data *) - pdev->id_entry->driver_data; + platform_get_device_id(pdev)->driver_data; } else { return -ENODEV; } @@ -1099,6 +1438,25 @@ static int flexcan_probe(struct platform_device *pdev) dev->flags |= IFF_ECHO; priv = netdev_priv(dev); + + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + core_is_little = false; + + if (of_property_read_bool(dev->dev.of_node, "little-endian")) + module_is_little = true; + + if ((core_is_little && module_is_little) || + (!core_is_little && !module_is_little)) { + priv->read = flexcan_read_le; + priv->write = flexcan_write_le; + } + + if ((!core_is_little && module_is_little) || + (core_is_little && !module_is_little)) { + priv->read = flexcan_read_be; + priv->write = flexcan_write_be; + } + priv->can.clock.freq = clock_freq; priv->can.bittiming_const = &flexcan_bittiming_const; priv->can.do_set_mode = flexcan_set_mode; @@ -1152,8 +1510,7 @@ static int flexcan_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int flexcan_suspend(struct device *device) +static int __maybe_unused flexcan_suspend(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); @@ -1172,7 +1529,7 @@ static int flexcan_suspend(struct device *device) return 0; } -static int flexcan_resume(struct device *device) +static int __maybe_unused flexcan_resume(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); @@ -1184,7 +1541,6 @@ static int flexcan_resume(struct device *device) } return flexcan_chip_enable(priv); } -#endif /* CONFIG_PM_SLEEP */ static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume); diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index bc51248..3e26476 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -88,7 +88,7 @@ config UGETH_TX_ON_DEMAND config GIANFAR tristate "Gianfar Ethernet" depends on FSL_SOC - select FSL_85XX_CACHE_SRAM + select FSL_85XX_CACHE_SRAM if PPC select FSL_PQ_MDIO select PHYLIB select CRC32 diff --git a/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c b/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c index 427de85..74b0de8 100755 --- a/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c +++ b/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c @@ -58,7 +58,7 @@ #include <linux/of_irq.h> #include <asm/uaccess.h> #include <asm/errno.h> -#include <asm/qe.h> /* For struct qe_firmware */ +#include <linux/fsl/qe.h> /* For struct qe_firmware */ #include <sysdev/fsl_soc.h> #include <asm/fsl_pm.h> #include <linux/stat.h> /* For file access mask */ diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c index c4f6506..d31302a 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -29,7 +29,7 @@ #include <linux/of_device.h> #include <asm/io.h> -#include <asm/ucc.h> /* for ucc_set_qe_mux_mii_mng() */ +#include <linux/fsl/ucc.h> /* for ucc_set_qe_mux_mii_mng() */ #include "gianfar.h" @@ -103,19 +103,22 @@ static int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, { struct fsl_pq_mdio_priv *priv = bus->priv; struct fsl_pq_mii __iomem *regs = priv->regs; - u32 status; + unsigned int timeout; /* Set the PHY address and the register address we want to write */ - out_be32(®s->miimadd, (mii_id << 8) | regnum); + iowrite32be((mii_id << 8) | regnum, ®s->miimadd); /* Write out the value we want */ - out_be32(®s->miimcon, value); + iowrite32be(value, ®s->miimcon); /* Wait for the transaction to finish */ - status = spin_event_timeout(!(in_be32(®s->miimind) & MIIMIND_BUSY), - MII_TIMEOUT, 0); + timeout = MII_TIMEOUT; + while ((ioread32be(®s->miimind) & MIIMIND_BUSY) && timeout) { + cpu_relax(); + timeout--; + } - return status ? 0 : -ETIMEDOUT; + return timeout ? 0 : -ETIMEDOUT; } /* @@ -132,25 +135,29 @@ static int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { struct fsl_pq_mdio_priv *priv = bus->priv; struct fsl_pq_mii __iomem *regs = priv->regs; - u32 status; + unsigned int timeout; u16 value; /* Set the PHY address and the register address we want to read */ - out_be32(®s->miimadd, (mii_id << 8) | regnum); + iowrite32be((mii_id << 8) | regnum, ®s->miimadd); /* Clear miimcom, and then initiate a read */ - out_be32(®s->miimcom, 0); - out_be32(®s->miimcom, MII_READ_COMMAND); + iowrite32be(0, ®s->miimcom); + iowrite32be(MII_READ_COMMAND, ®s->miimcom); /* Wait for the transaction to finish, normally less than 100us */ - status = spin_event_timeout(!(in_be32(®s->miimind) & - (MIIMIND_NOTVALID | MIIMIND_BUSY)), - MII_TIMEOUT, 0); - if (!status) + timeout = MII_TIMEOUT; + while ((ioread32be(®s->miimind) & + (MIIMIND_NOTVALID | MIIMIND_BUSY)) && timeout) { + cpu_relax(); + timeout--; + } + + if (!timeout) return -ETIMEDOUT; /* Grab the value of the register from miimstat */ - value = in_be32(®s->miimstat); + value = ioread32be(®s->miimstat); dev_dbg(&bus->dev, "read %04x from address %x/%x\n", value, mii_id, regnum); return value; @@ -161,23 +168,26 @@ static int fsl_pq_mdio_reset(struct mii_bus *bus) { struct fsl_pq_mdio_priv *priv = bus->priv; struct fsl_pq_mii __iomem *regs = priv->regs; - u32 status; + unsigned int timeout; mutex_lock(&bus->mdio_lock); /* Reset the management interface */ - out_be32(®s->miimcfg, MIIMCFG_RESET); + iowrite32be(MIIMCFG_RESET, ®s->miimcfg); /* Setup the MII Mgmt clock speed */ - out_be32(®s->miimcfg, MIIMCFG_INIT_VALUE); + iowrite32be(MIIMCFG_INIT_VALUE, ®s->miimcfg); /* Wait until the bus is free */ - status = spin_event_timeout(!(in_be32(®s->miimind) & MIIMIND_BUSY), - MII_TIMEOUT, 0); + timeout = MII_TIMEOUT; + while ((ioread32be(®s->miimind) & MIIMIND_BUSY) && timeout) { + cpu_relax(); + timeout--; + } mutex_unlock(&bus->mdio_lock); - if (!status) { + if (!timeout) { dev_err(&bus->dev, "timeout waiting for MII bus\n"); return -EBUSY; } @@ -434,7 +444,7 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) tbipa = data->get_tbipa(priv->map); - out_be32(tbipa, be32_to_cpup(prop)); + iowrite32be(be32_to_cpup(prop), tbipa); } } diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 5584c39..2f67fe8 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -90,8 +90,10 @@ #endif #include <asm/io.h> +#ifdef CONFIG_PPC #include <asm/reg.h> #include <asm/mpc85xx.h> +#endif #include <asm/irq.h> #include <asm/uaccess.h> #include <linux/module.h> @@ -102,6 +104,8 @@ #include <linux/phy_fixed.h> #include <linux/of.h> #include <linux/of_net.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include "gianfar.h" @@ -161,10 +165,12 @@ static void gfar_set_mac_for_addr(struct net_device *dev, int num, const u8 *addr); static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#ifdef CONFIG_FSL_85XX_CACHE_SRAM bool gfar_l2sram_en = true; module_param(gfar_l2sram_en, bool, 0444); MODULE_PARM_DESC(gfar_l2sram_en, "Enable allocation to L2 SRAM."); +#endif MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_DESCRIPTION("Gianfar Ethernet Driver"); @@ -202,7 +208,8 @@ static int gfar_init_bds(struct net_device *ndev) /* Set the last descriptor in the ring to indicate wrap */ txbdp--; - txbdp->status |= TXBD_WRAP; + txbdp->status = cpu_to_be16(be16_to_cpu(txbdp->status) | + TXBD_WRAP); } for (i = 0; i < priv->num_rx_queues; i++) { @@ -216,7 +223,7 @@ static int gfar_init_bds(struct net_device *ndev) if (skb) { gfar_init_rxbdp(rx_queue, rxbdp, - rxbdp->bufPtr); + be32_to_cpu(rxbdp->bufPtr)); } else { skb = gfar_alloc_skb(ndev); if (!skb) { @@ -240,7 +247,6 @@ static int gfar_alloc_skb_resources(struct net_device *ndev) { void *vaddr = NULL; dma_addr_t addr; - phys_addr_t paddr; int i, j, k; struct gfar_private *priv = netdev_priv(ndev); struct device *dev = priv->dev; @@ -256,7 +262,9 @@ static int gfar_alloc_skb_resources(struct net_device *ndev) priv->total_rx_ring_size += priv->rx_queue[i]->rx_ring_size; /* Allocate memory for the buffer descriptors */ +#ifdef CONFIG_FSL_85XX_CACHE_SRAM if (priv->bd_l2sram_en) { + phys_addr_t paddr; vaddr = mpc85xx_cache_sram_alloc(BD_RING_REG_SZ(priv), &paddr, L1_CACHE_BYTES); if (vaddr) @@ -268,6 +276,7 @@ static int gfar_alloc_skb_resources(struct net_device *ndev) priv->bd_l2sram_en = 0; } } +#endif if (!priv->bd_l2sram_en) vaddr = dma_alloc_coherent(dev, BD_RING_REG_SZ(priv), @@ -697,26 +706,27 @@ static int gfar_parse_group(struct device_node *np, grp->priv = priv; spin_lock_init(&grp->grplock); if (priv->mode == MQ_MG_MODE) { - u32 *rxq_mask, *txq_mask; - rxq_mask = (u32 *)of_get_property(np, "fsl,rx-bit-map", NULL); - txq_mask = (u32 *)of_get_property(np, "fsl,tx-bit-map", NULL); + u32 rxq_mask, txq_mask; + int ret; + grp->rx_bit_map = (DEFAULT_MAPPING >> priv->num_grps); + grp->tx_bit_map = (DEFAULT_MAPPING >> priv->num_grps); + + ret = of_property_read_u32(np, "fsl,rx-bit-map", &rxq_mask); + if (!ret) + grp->rx_bit_map = rxq_mask; + + ret = of_property_read_u32(np, "fsl,tx-bit-map", &txq_mask); + if (!ret) + grp->tx_bit_map = txq_mask; + +#ifndef CONFIG_AS_FASTPATH if (priv->poll_mode == GFAR_SQ_POLLING) { /* One Q per interrupt group: Q0 to G0, Q1 to G1 */ grp->rx_bit_map = (DEFAULT_MAPPING >> priv->num_grps); grp->tx_bit_map = (DEFAULT_MAPPING >> priv->num_grps); - #ifdef CONFIG_AS_FASTPATH - grp->rx_bit_map = rxq_mask ? - *rxq_mask : (DEFAULT_MAPPING >> priv->num_grps); - grp->tx_bit_map = txq_mask ? - *txq_mask : (DEFAULT_MAPPING >> priv->num_grps); - #endif - } else { /* GFAR_MQ_POLLING */ - grp->rx_bit_map = rxq_mask ? - *rxq_mask : (DEFAULT_MAPPING >> priv->num_grps); - grp->tx_bit_map = txq_mask ? - *txq_mask : (DEFAULT_MAPPING >> priv->num_grps); } +#endif } else { grp->rx_bit_map = 0xFF; grp->tx_bit_map = 0xFF; @@ -764,11 +774,10 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) struct gfar_private *priv = NULL; struct device_node *np = ofdev->dev.of_node; struct device_node *child = NULL; - const u32 *stash; - const u32 *stash_len; - const u32 *stash_idx; + struct property *stash; + u32 stash_len = 0; + u32 stash_idx = 0; unsigned int num_tx_qs, num_rx_qs; - u32 *tx_queues, *rx_queues; unsigned short mode, poll_mode; if (!np || !of_device_is_available(np)) @@ -782,10 +791,6 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) poll_mode = GFAR_SQ_POLLING; } - /* parse the num of HW tx and rx queues */ - tx_queues = (u32 *)of_get_property(np, "fsl,num_tx_queues", NULL); - rx_queues = (u32 *)of_get_property(np, "fsl,num_rx_queues", NULL); - if (mode == SQ_SG_MODE) { num_tx_qs = 1; num_rx_qs = 1; @@ -804,8 +809,17 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) num_tx_qs = num_grps; /* one txq per int group */ num_rx_qs = num_grps; /* one rxq per int group */ } else { /* GFAR_MQ_POLLING */ - num_tx_qs = tx_queues ? *tx_queues : 1; - num_rx_qs = rx_queues ? *rx_queues : 1; + u32 tx_queues, rx_queues; + int ret; + + /* parse the num of HW tx and rx queues */ + ret = of_property_read_u32(np, "fsl,num_tx_queues", + &tx_queues); + num_tx_qs = ret ? 1 : tx_queues; + + ret = of_property_read_u32(np, "fsl,num_rx_queues", + &rx_queues); + num_rx_qs = ret ? 1 : rx_queues; } } @@ -856,7 +870,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) priv->rx_list.count = 0; mutex_init(&priv->rx_queue_access); - model = of_get_property(np, "model", NULL); + err = of_property_read_string(np, "model", &model); for (i = 0; i < MAXGROUPS; i++) priv->gfargrp[i].regs = NULL; @@ -874,27 +888,29 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) goto err_grp_init; } +#ifdef CONFIG_FSL_85XX_CACHE_SRAM if (gfar_l2sram_en) { /* try to alloc the BD rings to L2 SRAM */ priv->bd_l2sram_en = 1; } +#endif - stash = of_get_property(np, "bd-stash", NULL); + stash = of_find_property(np, "bd-stash", NULL); if (stash) { priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING; priv->bd_stash_en = 1; } - stash_len = of_get_property(np, "rx-stash-len", NULL); + err = of_property_read_u32(np, "rx-stash-len", &stash_len); - if (stash_len) - priv->rx_stash_size = *stash_len; + if (err == 0) + priv->rx_stash_size = stash_len; - stash_idx = of_get_property(np, "rx-stash-idx", NULL); + err = of_property_read_u32(np, "rx-stash-idx", &stash_idx); - if (stash_idx) - priv->rx_stash_index = *stash_idx; + if (err == 0) + priv->rx_stash_index = stash_idx; if (stash_len || stash_idx) priv->device_flags |= FSL_GIANFAR_DEV_HAS_BUF_STASHING; @@ -921,24 +937,27 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) FSL_GIANFAR_DEV_HAS_EXTENDED_HASH | FSL_GIANFAR_DEV_HAS_TIMER; - ctype = of_get_property(np, "phy-connection-type", NULL); + err = of_property_read_string(np, "phy-connection-type", &ctype); /* We only care about rgmii-id. The rest are autodetected */ - if (ctype && !strcmp(ctype, "rgmii-id")) + if (err == 0 && !strcmp(ctype, "rgmii-id")) priv->interface = PHY_INTERFACE_MODE_RGMII_ID; else priv->interface = PHY_INTERFACE_MODE_MII; - if (of_get_property(np, "fsl,magic-packet", NULL)) + if (of_find_property(np, "fsl,magic-packet", NULL)) priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; - if (of_get_property(np, "fsl,wake-on-filer", NULL)) + if (of_find_property(np, "fsl,wake-on-filer", NULL)) priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER; priv->phy_node = of_parse_phandle(np, "phy-handle", 0); /* Find the TBI PHY. If it's not there, we don't support SGMII */ priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0); + + priv->dma_endian_le = of_property_read_bool(np, "fsl,dma-endian-le"); + #if defined CONFIG_FSL_GIANFAR_1588 /* Handle IEEE1588 node */ if (!gfar_ptp_init(np, priv)) @@ -1095,6 +1114,7 @@ static void gfar_init_filer_table(struct gfar_private *priv) } } +#ifdef CONFIG_PPC static void __gfar_detect_errata_83xx(struct gfar_private *priv) { unsigned int pvr = mfspr(SPRN_PVR); @@ -1127,6 +1147,7 @@ static void __gfar_detect_errata_85xx(struct gfar_private *priv) ((SVR_SOC_VER(svr) == SVR_P2010) && (SVR_REV(svr) < 0x20))) priv->errata |= GFAR_ERRATA_76; /* aka eTSEC 20 */ } +#endif static void gfar_detect_errata(struct gfar_private *priv) { @@ -1135,10 +1156,12 @@ static void gfar_detect_errata(struct gfar_private *priv) /* no plans to fix */ priv->errata |= GFAR_ERRATA_A002; +#ifdef CONFIG_PPC if (pvr_version_is(PVR_VER_E500V1) || pvr_version_is(PVR_VER_E500V2)) __gfar_detect_errata_85xx(priv); else /* non-mpc85xx parts, i.e. e300 core based */ __gfar_detect_errata_83xx(priv); +#endif if (priv->errata) dev_info(dev, "enabled errata workarounds, flags: 0x%x\n", @@ -1276,7 +1299,7 @@ static void gfar_hw_init(struct gfar_private *priv) gfar_write_isrg(priv); } -static void __init gfar_init_addr_hash_table(struct gfar_private *priv) +static void gfar_init_addr_hash_table(struct gfar_private *priv) { struct gfar __iomem *regs = priv->gfargrp[0].regs; @@ -1988,26 +2011,32 @@ static void gfar_halt_nodisable(struct gfar_private *priv) { struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; + unsigned int timeout; + int stopped; gfar_ints_disable(priv); + if (gfar_is_dma_stopped(priv)) + return; + /* Stop the DMA, and wait for it to stop */ tempval = gfar_read(®s->dmactrl); - if ((tempval & (DMACTRL_GRS | DMACTRL_GTS)) != - (DMACTRL_GRS | DMACTRL_GTS)) { - int ret; - - tempval |= (DMACTRL_GRS | DMACTRL_GTS); - gfar_write(®s->dmactrl, tempval); + tempval |= (DMACTRL_GRS | DMACTRL_GTS); + gfar_write(®s->dmactrl, tempval); - do { - ret = spin_event_timeout(((gfar_read(®s->ievent) & - (IEVENT_GRSC | IEVENT_GTSC)) == - (IEVENT_GRSC | IEVENT_GTSC)), 1000000, 0); - if (!ret && !(gfar_read(®s->ievent) & IEVENT_GRSC)) - ret = __gfar_is_rx_idle(priv); - } while (!ret); +retry: + timeout = 1000; + while (!(stopped = gfar_is_dma_stopped(priv)) && timeout) { + cpu_relax(); + timeout--; } + + if (!timeout) + stopped = gfar_is_dma_stopped(priv); + + if (!stopped && !gfar_is_rx_dma_stopped(priv) && + !__gfar_is_rx_idle(priv)) + goto retry; } /* Halt the receive and transmit queues */ @@ -2065,14 +2094,15 @@ static void free_skb_tx_queue(struct gfar_priv_tx_q *tx_queue) if (!tx_queue->tx_skbuff[i]) continue; - dma_unmap_single(priv->dev, txbdp->bufPtr, - txbdp->length, DMA_TO_DEVICE); + dma_unmap_single(priv->dev, be32_to_cpu(txbdp->bufPtr), + be16_to_cpu(txbdp->length), DMA_TO_DEVICE); txbdp->lstatus = 0; for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags; j++) { txbdp++; - dma_unmap_page(priv->dev, txbdp->bufPtr, - txbdp->length, DMA_TO_DEVICE); + dma_unmap_page(priv->dev, be32_to_cpu(txbdp->bufPtr), + be16_to_cpu(txbdp->length), + DMA_TO_DEVICE); } txbdp++; dev_kfree_skb_any(tx_queue->tx_skbuff[i]); @@ -2092,7 +2122,7 @@ static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue) for (i = 0; i < rx_queue->rx_ring_size; i++) { if (rx_queue->rx_skbuff[i]) { - dma_unmap_single(priv->dev, rxbdp->bufPtr, + dma_unmap_single(priv->dev, be32_to_cpu(rxbdp->bufPtr), priv->rx_buffer_size, DMA_FROM_DEVICE); dev_kfree_skb_any(rx_queue->rx_skbuff[i]); @@ -2131,9 +2161,11 @@ static void free_skb_resources(struct gfar_private *priv) free_skb_rx_queue(rx_queue); } +#ifdef CONFIG_FSL_85XX_CACHE_SRAM if (priv->bd_l2sram_en) mpc85xx_cache_sram_free(priv->tx_queue[0]->tx_bd_base); else +#endif dma_free_coherent(priv->dev, BD_RING_REG_SZ(priv), priv->tx_queue[0]->tx_bd_base, priv->tx_queue[0]->tx_bd_dma_base); @@ -2152,6 +2184,8 @@ void gfar_start(struct gfar_private *priv) /* Initialize DMACTRL to have WWR and WOP */ tempval = gfar_read(®s->dmactrl); tempval |= DMACTRL_INIT_SETTINGS; + if (priv->dma_endian_le) + tempval |= DMACTRL_LE; gfar_write(®s->dmactrl, tempval); /* Make sure we aren't stopped */ @@ -2328,10 +2362,11 @@ static int gfar_enet_open(struct net_device *dev) return err; } + void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb) { fcb->flags |= TXFCB_VLN; - fcb->vlctl = vlan_tx_tag_get(skb); + fcb->vlctl = cpu_to_be16(vlan_tx_tag_get(skb)); } /* This is called by the kernel when a frame is ready for transmission. @@ -2416,7 +2451,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_queue->stats.tx_packets++; txbdp = txbdp_start = tx_queue->cur_tx; - lstatus = txbdp->lstatus; + lstatus = be32_to_cpu(txbdp->lstatus); /* Time stamp insertion requires one additional TxBD */ if (unlikely(do_tstamp)) @@ -2424,11 +2459,13 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_queue->tx_ring_size); if (nr_frags == 0) { - if (unlikely(do_tstamp)) - txbdp_tstamp->lstatus |= BD_LFLAG(TXBD_LAST | - TXBD_INTERRUPT); - else + if (unlikely(do_tstamp)) { + u32 lstatus_ts = be32_to_cpu(txbdp_tstamp->lstatus); + lstatus_ts |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT); + txbdp_tstamp->lstatus = cpu_to_be32(lstatus_ts); + } else { lstatus |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT); + } } else { /* Place the fragment addresses and lengths into the TxBDs */ for (i = 0; i < nr_frags; i++) { @@ -2438,7 +2475,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) frag_len = skb_shinfo(skb)->frags[i].size; - lstatus = txbdp->lstatus | frag_len | + lstatus = be32_to_cpu(txbdp->lstatus) | frag_len | BD_LFLAG(TXBD_READY); /* Handle the last BD specially */ @@ -2452,11 +2489,11 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) DMA_TO_DEVICE); /* set the TxBD length and buffer pointer */ - txbdp->bufPtr = bufaddr; - txbdp->lstatus = lstatus; + txbdp->bufPtr = cpu_to_be32(bufaddr); + txbdp->lstatus = cpu_to_be32(lstatus); } - lstatus = txbdp_start->lstatus; + lstatus = be32_to_cpu(txbdp_start->lstatus); } /* Add TxPAL between FCB and frame if required */ @@ -2507,13 +2544,14 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) */ vlan_ctrl = gfar_read(®s->dfvlan); vlan_ctrl &= ~0xFFFF; - vlan_ctrl |= (fcb->vlctl & 0xFFFF); + vlan_ctrl |= (be16_to_cpu(fcb->vlctl) & 0xFFFF); gfar_write(®s->dfvlan, vlan_ctrl); #endif } - txbdp_start->bufPtr = dma_map_single(priv->dev, skb->data, - skb_headlen(skb), DMA_TO_DEVICE); + bufaddr = dma_map_single(priv->dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + txbdp_start->bufPtr = cpu_to_be32(bufaddr); /* If time stamping is requested one additional TxBD must be set up. The * first TxBD points to the FCB and must have a data length of @@ -2521,26 +2559,25 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) * the full frame length. */ if (unlikely(do_tstamp)) { - txbdp_tstamp->bufPtr = txbdp_start->bufPtr + fcb_len; - txbdp_tstamp->lstatus |= BD_LFLAG(TXBD_READY) | - (skb_headlen(skb) - fcb_len); + u32 lstatus_ts = be32_to_cpu(txbdp_tstamp->lstatus); + bufaddr = be32_to_cpu(txbdp_start->bufPtr); + + bufaddr += fcb_len; + lstatus_ts |= BD_LFLAG(TXBD_READY) | + (skb_headlen(skb) - fcb_len); + + txbdp_tstamp->bufPtr = cpu_to_be32(bufaddr); + txbdp_tstamp->lstatus = cpu_to_be32(lstatus_ts); lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | GMAC_FCB_LEN; } else { lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb); } - /* The powerpc-specific eieio() is used, as wmb() has too strong - * semantics (it requires synchronization between cacheable and - * uncacheable mappings, which eieio doesn't provide and which we - * don't need), thus requiring a more expensive sync instruction. At - * some point, the set of architecture-independent barrier functions - * should be expanded to include weaker barriers. - */ - eieio(); + gfar_wmb(); - txbdp_start->lstatus = lstatus; + txbdp_start->lstatus = cpu_to_be32(lstatus); - eieio(); /* force lstatus write before tx_skbuff */ + gfar_wmb(); /* force lstatus write before tx_skbuff */ tx_queue->tx_skbuff[tx_queue->skb_curtx] = skb; @@ -2721,7 +2758,7 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) lbdp = skip_txbd(bdp, nr_txbds - 1, base, tx_ring_size); - lstatus = lbdp->lstatus; + lstatus = be32_to_cpu(lbdp->lstatus); /* Only clean completed frames */ if ((lstatus & BD_LFLAG(TXBD_READY)) && @@ -2730,11 +2767,12 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { next = next_txbd(bdp, base, tx_ring_size); - buflen = next->length + GMAC_FCB_LEN + GMAC_TXPAL_LEN; + buflen = be16_to_cpu(next->length) + + GMAC_FCB_LEN + GMAC_TXPAL_LEN; } else - buflen = bdp->length; + buflen = be16_to_cpu(bdp->length); - dma_unmap_single(priv->dev, bdp->bufPtr, + dma_unmap_single(priv->dev, be32_to_cpu(bdp->bufPtr), buflen, DMA_TO_DEVICE); if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { @@ -2780,17 +2818,18 @@ static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) skb_pull(skb, GMAC_FCB_LEN + GMAC_TXPAL_LEN); skb_tstamp_tx(skb, &shhwtstamps); #endif - bdp->lstatus &= BD_LFLAG(TXBD_WRAP); + gfar_clear_txbd_status(bdp); bdp = next; } - bdp->lstatus &= BD_LFLAG(TXBD_WRAP); + gfar_clear_txbd_status(bdp); bdp = next_txbd(bdp, base, tx_ring_size); for (i = 0; i < frags; i++) { - dma_unmap_page(priv->dev, bdp->bufPtr, - bdp->length, DMA_TO_DEVICE); - bdp->lstatus &= BD_LFLAG(TXBD_WRAP); + dma_unmap_page(priv->dev, be32_to_cpu(bdp->bufPtr), + be16_to_cpu(bdp->length), + DMA_TO_DEVICE); + gfar_clear_txbd_status(bdp); bdp = next_txbd(bdp, base, tx_ring_size); } @@ -2979,8 +3018,9 @@ static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb, * RXFCB_VLN is pseudo randomly set. */ if (dev->features & NETIF_F_HW_VLAN_CTAG_RX && - fcb->flags & RXFCB_VLN) - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), fcb->vlctl); + be16_to_cpu(fcb->flags) & RXFCB_VLN) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + be16_to_cpu(fcb->vlctl)); /* Send the packet up the stack */ napi_gro_receive(napi, skb); @@ -3011,7 +3051,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) amount_pull = priv->uses_rxfcb ? GMAC_FCB_LEN : 0; - while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) { + while (!(be16_to_cpu(bdp->status) & RXBD_EMPTY) && rx_work_limit--) { struct sk_buff *newskb; rmb(); @@ -3021,17 +3061,18 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) skb = rx_queue->rx_skbuff[rx_queue->skb_currx]; - dma_unmap_single(priv->dev, bdp->bufPtr, + dma_unmap_single(priv->dev, be32_to_cpu(bdp->bufPtr), priv->rx_buffer_size, DMA_FROM_DEVICE); - if (unlikely(!(bdp->status & RXBD_ERR) && - bdp->length > priv->rx_buffer_size)) - bdp->status = RXBD_LARGE; + if (unlikely(!(be16_to_cpu(bdp->status) & RXBD_ERR) && + be16_to_cpu(bdp->length) > priv->rx_buffer_size)) + bdp->status = cpu_to_be16(RXBD_LARGE); /* We drop the frame if we failed to allocate a new buffer */ - if (unlikely(!newskb || !(bdp->status & RXBD_LAST) || - bdp->status & RXBD_ERR)) { - count_errors(bdp->status, dev); + if (unlikely(!newskb || + !(be16_to_cpu(bdp->status) & RXBD_LAST) || + be16_to_cpu(bdp->status) & RXBD_ERR)) { + count_errors(be16_to_cpu(bdp->status), dev); if (unlikely(!newskb)) newskb = skb; @@ -3043,7 +3084,8 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) howmany++; if (likely(skb)) { - pkt_len = bdp->length - ETH_FCS_LEN; + pkt_len = be16_to_cpu(bdp->length) - + ETH_FCS_LEN; /* Remove the FCS from the packet length */ skb_put(skb, pkt_len); rx_queue->stats.rx_bytes += pkt_len; @@ -3456,22 +3498,21 @@ static void gfar_set_mac_for_addr(struct net_device *dev, int num, { struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = priv->gfargrp[0].regs; - int idx; - char tmpbuf[ETH_ALEN]; u32 tempval; u32 __iomem *macptr = ®s->macstnaddr1; macptr += num*2; - /* Now copy it into the mac registers backwards, cuz - * little endian is silly + /* For a station address of 0x12345678ABCD in transmission + * order (BE), MACnADDR1 is set to 0xCDAB7856 and + * MACnADDR2 is set to 0x34120000. */ - for (idx = 0; idx < ETH_ALEN; idx++) - tmpbuf[ETH_ALEN - 1 - idx] = addr[idx]; + tempval = (addr[5] << 24) | (addr[4] << 16) | + (addr[3] << 8) | addr[2]; - gfar_write(macptr, *((u32 *) (tmpbuf))); + gfar_write(macptr, tempval); - tempval = *((u32 *) (tmpbuf + 4)); + tempval = (addr[1] << 24) | (addr[0] << 16); gfar_write(macptr+1, tempval); } diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index 5f7a98d..f7c14a5 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -302,9 +302,14 @@ extern const char gfar_driver_version[]; #define RQUEUE_EN_ALL 0x000000FF /* Init to do tx snooping for buffers and descriptors */ +#ifdef CONFIG_SOC_LS1021A +#define DMACTRL_INIT_SETTINGS 0x00000003 +#else #define DMACTRL_INIT_SETTINGS 0x000000c3 +#endif #define DMACTRL_GRS 0x00000010 #define DMACTRL_GTS 0x00000008 +#define DMACTRL_LE 0x00008000 #define TSTAT_CLEAR_THALT_ALL 0xFF000000 #define TSTAT_CLEAR_THALT 0x80000000 @@ -616,12 +621,12 @@ struct txbd8 { union { struct { - u16 status; /* Status Fields */ - u16 length; /* Buffer length */ + __be16 status; /* Status Fields */ + __be16 length; /* Buffer length */ }; - u32 lstatus; + __be32 lstatus; }; - u32 bufPtr; /* Buffer Pointer */ + __be32 bufPtr; /* Buffer Pointer */ }; struct txfcb { @@ -629,28 +634,28 @@ struct txfcb { u8 ptp; /* Flag to enable tx timestamping */ u8 l4os; /* Level 4 Header Offset */ u8 l3os; /* Level 3 Header Offset */ - u16 phcs; /* Pseudo-header Checksum */ - u16 vlctl; /* VLAN control word */ + __be16 phcs; /* Pseudo-header Checksum */ + __be16 vlctl; /* VLAN control word */ }; struct rxbd8 { union { struct { - u16 status; /* Status Fields */ - u16 length; /* Buffer Length */ + __be16 status; /* Status Fields */ + __be16 length; /* Buffer Length */ }; - u32 lstatus; + __be32 lstatus; }; - u32 bufPtr; /* Buffer Pointer */ + __be32 bufPtr; /* Buffer Pointer */ }; struct rxfcb { - u16 flags; + __be16 flags; u8 rq; /* Receive Queue index */ u8 pro; /* Layer 4 Protocol */ u16 reserved; - u16 vlctl; /* VLAN control word */ + __be16 vlctl; /* VLAN control word */ }; struct gianfar_skb_cb { @@ -1327,6 +1332,9 @@ struct gfar_private { /* L2 SRAM alloc of BDs */ bd_l2sram_en:1; + /* little endian dma buffer and descriptor host interface */ + unsigned int dma_endian_le; + /* The total tx and rx ring size for the enabled queues */ unsigned int total_tx_ring_size; unsigned int total_rx_ring_size; @@ -1424,6 +1432,44 @@ static inline void gfar_write_isrg(struct gfar_private *priv) } } +static inline int gfar_is_dma_stopped(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + + return ((gfar_read(®s->ievent) & (IEVENT_GRSC | IEVENT_GTSC)) == + (IEVENT_GRSC | IEVENT_GTSC)); +} + +static inline int gfar_is_rx_dma_stopped(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + + return gfar_read(®s->ievent) & IEVENT_GRSC; +} + +static inline void gfar_wmb(void) +{ +#if defined(CONFIG_PPC) + /* The powerpc-specific eieio() is used, as wmb() has too strong + * semantics (it requires synchronization between cacheable and + * uncacheable mappings, which eieio() doesn't provide and which we + * don't need), thus requiring a more expensive sync instruction. At + * some point, the set of architecture-independent barrier functions + * should be expanded to include weaker barriers. + */ + eieio(); +#else + wmb(); /* order write acesses for BD (or FCB) fields */ +#endif +} + +static inline void gfar_clear_txbd_status(struct txbd8 *bdp) +{ + u32 lstatus = be32_to_cpu(bdp->lstatus); + lstatus &= BD_LFLAG(TXBD_WRAP); + bdp->lstatus = cpu_to_be32(lstatus); +} + irqreturn_t gfar_receive(int irq, void *dev_id); int startup_gfar(struct net_device *dev); void stop_gfar(struct net_device *dev); @@ -1503,30 +1549,31 @@ static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb) } static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb, - int fcb_length) + int fcb_length) { /* If we're here, it's a IP packet with a TCP or UDP - * payload. We set it to checksum, using a pseudo-header - * we provide - */ + * payload. We set it to checksum, using a pseudo-header + * we provide + */ u8 flags = TXFCB_DEFAULT; /* Tell the controller what the protocol is - * And provide the already calculated phcs - */ + * And provide the already calculated phcs + */ if (ip_hdr(skb)->protocol == IPPROTO_UDP) { flags |= TXFCB_UDP; - fcb->phcs = udp_hdr(skb)->check; + fcb->phcs = (__force __be16)(udp_hdr(skb)->check); } else - fcb->phcs = tcp_hdr(skb)->check; + fcb->phcs = (__force __be16)(tcp_hdr(skb)->check); /* l3os is the distance between the start of the - * frame (skb->data) and the start of the IP hdr. - * l4os is the distance between the start of the - * l3 hdr and the l4 hdr - */ - fcb->l3os = (u16)(skb_network_offset(skb) - fcb_length); + * frame (skb->data) and the start of the IP hdr. + * l4os is the distance between the start of the + * l3 hdr and the l4 hdr + */ + fcb->l3os = (u8)(skb_network_offset(skb) - fcb_length); fcb->l4os = skb_network_header_len(skb); + fcb->flags = flags; } @@ -1549,15 +1596,15 @@ static inline void gfar_init_rxbdp(struct gfar_priv_rx_q *rx_queue, { u32 lstatus; - bdp->bufPtr = buf; + bdp->bufPtr = cpu_to_be32(buf); lstatus = BD_LFLAG(RXBD_EMPTY | RXBD_INTERRUPT); if (bdp == rx_queue->rx_bd_base + rx_queue->rx_ring_size - 1) lstatus |= BD_LFLAG(RXBD_WRAP); - eieio(); + gfar_wmb(); - bdp->lstatus = lstatus; + bdp->lstatus = cpu_to_be32(lstatus); } static inline void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, @@ -1615,7 +1662,8 @@ static inline void gfar_rx_checksum(struct sk_buff *skb, struct rxfcb *fcb) * were verified, then we tell the kernel that no * checksumming is necessary. Otherwise, it is [FIXME] */ - if ((fcb->flags & RXFCB_CSUM_MASK) == (RXFCB_CIP | RXFCB_CTU)) + if ((be16_to_cpu(fcb->flags) & RXFCB_CSUM_MASK) == + (RXFCB_CIP | RXFCB_CTU)) skb->ip_summed = CHECKSUM_UNNECESSARY; else skb_checksum_none_assert(skb); diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 5930c39..fbaedd3 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -38,10 +38,10 @@ #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> -#include <asm/ucc.h> -#include <asm/ucc_fast.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/ucc.h> +#include <linux/fsl/ucc_fast.h> #include <asm/machdep.h> #include "ucc_geth.h" diff --git a/drivers/net/ethernet/freescale/ucc_geth.h b/drivers/net/ethernet/freescale/ucc_geth.h index 75f3371..a803635 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.h +++ b/drivers/net/ethernet/freescale/ucc_geth.h @@ -22,11 +22,11 @@ #include <linux/list.h> #include <linux/if_ether.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> -#include <asm/ucc.h> -#include <asm/ucc_fast.h> +#include <linux/fsl/ucc.h> +#include <linux/fsl/ucc_fast.h> #define DRV_DESC "QE UCC Gigabit Ethernet Controller" #define DRV_NAME "ucc_geth" diff --git a/drivers/net/wan/fsl_ucc_hdlc.h b/drivers/net/wan/fsl_ucc_hdlc.h index e0c8a4a..93cc20cc 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.h +++ b/drivers/net/wan/fsl_ucc_hdlc.h @@ -14,11 +14,11 @@ #include <linux/kernel.h> #include <linux/list.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> -#include <asm/ucc.h> -#include <asm/ucc_fast.h> +#include <linux/fsl/ucc.h> +#include <linux/fsl/ucc_fast.h> /* SI RAM entries */ #define SIR_LAST 0x0001 diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 1752988..526cde6 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -79,6 +79,211 @@ struct device_node *of_irq_find_parent(struct device_node *child) } /** + * of_irq_parse_raw - Low level interrupt tree parsing + * @parent: the device interrupt parent + * @addr: address specifier (start of "reg" property of the device) + * in be32 format + * @out_irq: structure of_irq updated by this function + * + * Returns 0 on success and a negative number on error + * + * This function is a low-level interrupt tree walking function. It + * can be used to do a partial walk with synthetized reg and interrupts + * properties, for example when resolving PCI interrupts when no device + * node exist for the parent. It takes an interrupt specifier structure as + * input, walks the tree looking for any interrupt-map properties, translates + * the specifier for each map, and then returns the translated map. + */ +int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) +{ + struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; + __be32 initial_match_array[MAX_PHANDLE_ARGS]; + const __be32 *match_array = initial_match_array; + const __be32 *tmp, *imap, *imask; + const __be32 dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 }; + u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; + int imaplen, match, i; + +#ifdef DEBUG + of_print_phandle_args("of_irq_parse_raw: ", out_irq); +#endif + + ipar = of_node_get(out_irq->np); + + /* First get the #interrupt-cells property of the current cursor + * that tells us how to interpret the passed-in intspec. If there + * is none, we are nice and just walk up the tree + */ + do { + tmp = of_get_property(ipar, "#interrupt-cells", NULL); + if (tmp != NULL) { + intsize = be32_to_cpu(*tmp); + break; + } + tnode = ipar; + ipar = of_irq_find_parent(ipar); + of_node_put(tnode); + } while (ipar); + if (ipar == NULL) { + pr_debug(" -> no parent found !\n"); + goto fail; + } + + pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", + of_node_full_name(ipar), intsize); + + if (out_irq->args_count != intsize) + return -EINVAL; + + /* Look for this #address-cells. We have to implement the old linux + * trick of looking for the parent here as some device-trees rely on it + */ + old = of_node_get(ipar); + do { + tmp = of_get_property(old, "#address-cells", NULL); + tnode = of_get_parent(old); + of_node_put(old); + old = tnode; + } while (old && tmp == NULL); + of_node_put(old); + old = NULL; + addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp); + + pr_debug(" -> addrsize=%d\n", addrsize); + + /* Range check so that the temporary buffer doesn't overflow */ + if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS)) + goto fail; + + /* Precalculate the match array - this simplifies match loop */ + for (i = 0; i < addrsize; i++) + initial_match_array[i] = addr ? addr[i] : 0; + for (i = 0; i < intsize; i++) + initial_match_array[addrsize + i] = + cpu_to_be32(out_irq->args[i]); + + /* Now start the actual "proper" walk of the interrupt tree */ + while (ipar != NULL) { + /* Now check if cursor is an interrupt-controller and if it is + * then we are done + */ + if (of_get_property(ipar, "interrupt-controller", NULL) != + NULL) { + pr_debug(" -> got it !\n"); + return 0; + } + + /* + * interrupt-map parsing does not work without a reg + * property when #address-cells != 0 + */ + if (addrsize && !addr) { + pr_debug(" -> no reg passed in when needed !\n"); + goto fail; + } + + /* Now look for an interrupt-map */ + imap = of_get_property(ipar, "interrupt-map", &imaplen); + /* No interrupt map, check for an interrupt parent */ + if (imap == NULL) { + pr_debug(" -> no map, getting parent\n"); + newpar = of_irq_find_parent(ipar); + goto skiplevel; + } + imaplen /= sizeof(u32); + + /* Look for a mask */ + imask = of_get_property(ipar, "interrupt-map-mask", NULL); + if (!imask) + imask = dummy_imask; + + /* Parse interrupt-map */ + match = 0; + while (imaplen > (addrsize + intsize + 1) && !match) { + /* Compare specifiers */ + match = 1; + for (i = 0; i < (addrsize + intsize); i++, imaplen--) + match &= !((match_array[i] ^ *imap++) & + imask[i]); + + pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); + + /* Get the interrupt parent */ + if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) + newpar = of_node_get(of_irq_dflt_pic); + else + newpar = of_find_node_by_phandle( + be32_to_cpup(imap)); + imap++; + --imaplen; + + /* Check if not found */ + if (newpar == NULL) { + pr_debug(" -> imap parent not found !\n"); + goto fail; + } + + if (!of_device_is_available(newpar)) + match = 0; + + /* Get #interrupt-cells and #address-cells of new + * parent + */ + tmp = of_get_property(newpar, "#interrupt-cells", NULL); + if (tmp == NULL) { + pr_debug(" -> parent lacks #interrupt-cells!\n"); + goto fail; + } + newintsize = be32_to_cpu(*tmp); + tmp = of_get_property(newpar, "#address-cells", NULL); + newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp); + + pr_debug(" -> newintsize=%d, newaddrsize=%d\n", + newintsize, newaddrsize); + + /* Check for malformed properties */ + if (WARN_ON(newaddrsize + newintsize > + MAX_PHANDLE_ARGS)) + goto fail; + if (imaplen < (newaddrsize + newintsize)) + goto fail; + + imap += newaddrsize + newintsize; + imaplen -= newaddrsize + newintsize; + + pr_debug(" -> imaplen=%d\n", imaplen); + } + if (!match) + goto fail; + + /* + * Successfully parsed an interrrupt-map translation; copy new + * interrupt specifier into the out_irq structure + */ + out_irq->np = newpar; + + match_array = imap - newaddrsize - newintsize; + for (i = 0; i < newintsize; i++) + out_irq->args[i] = be32_to_cpup(imap - newintsize + i); + out_irq->args_count = intsize = newintsize; + addrsize = newaddrsize; + +skiplevel: + /* Iterate again with new parent */ + pr_debug(" -> new parent: %s\n", of_node_full_name(newpar)); + of_node_put(ipar); + ipar = newpar; + newpar = NULL; + } + fail: + of_node_put(ipar); + of_node_put(newpar); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(of_irq_parse_raw); + +/** * of_irq_map_raw - Low level interrupt tree parsing * @parent: the device interrupt parent * @intspec: interrupt specifier ("interrupts" property of the device) @@ -272,6 +477,77 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, EXPORT_SYMBOL_GPL(of_irq_map_raw); /** + * of_irq_parse_one - Resolve an interrupt for a device + * @device: the device whose interrupt is to be resolved + * @index: index of the interrupt to resolve + * @out_irq: structure of_irq filled by this function + * + * This function resolves an interrupt for a node by walking the interrupt tree, + * finding which interrupt controller node it is attached to, and returning the + * interrupt specifier that can be used to retrieve a Linux IRQ number. + */ +int of_irq_parse_one(struct device_node *device, int index, + struct of_phandle_args *out_irq) +{ + struct device_node *p; + const __be32 *intspec, *tmp, *addr; + u32 intsize, intlen; + int i, res = -EINVAL; + + pr_debug("of_irq_parse_one: dev=%s, index=%d\n", + of_node_full_name(device), index); + + /* Get the reg property (if any) */ + addr = of_get_property(device, "reg", NULL); + + /* Try the new-style interrupts-extended first */ + res = of_parse_phandle_with_args(device, "interrupts-extended", + "#interrupt-cells", index, out_irq); + if (!res) + return of_irq_parse_raw(addr, out_irq); + + /* Get the interrupts property */ + intspec = of_get_property(device, "interrupts", &intlen); + if (intspec == NULL) + return -EINVAL; + + intlen /= sizeof(*intspec); + + pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); + + /* Look for the interrupt parent. */ + p = of_irq_find_parent(device); + if (p == NULL) + return -EINVAL; + + /* Get size of interrupt specifier */ + tmp = of_get_property(p, "#interrupt-cells", NULL); + if (tmp == NULL) + goto out; + intsize = be32_to_cpu(*tmp); + + pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); + + /* Check index */ + if ((index + 1) * intsize > intlen) + goto out; + + /* Copy intspec into irq structure */ + intspec += index * intsize; + out_irq->np = p; + out_irq->args_count = intsize; + for (i = 0; i < intsize; i++) + out_irq->args[i] = be32_to_cpup(intspec++); + + /* Check if there are any interrupt-map translations to process */ + res = of_irq_parse_raw(addr, out_irq); + out: + of_node_put(p); + return res; +} +EXPORT_SYMBOL_GPL(of_irq_parse_one); + +/** * of_irq_map_one - Resolve an interrupt for a device * @device: the device whose interrupt is to be resolved * @index: index of the interrupt to resolve diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c index 6770538..3843b46 100644 --- a/drivers/of/of_pci_irq.c +++ b/drivers/of/of_pci_irq.c @@ -91,3 +91,116 @@ int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq) return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq); } EXPORT_SYMBOL_GPL(of_irq_map_pci); + +/** + * of_irq_parse_pci - Resolve the interrupt for a PCI device + * @pdev: the device whose interrupt is to be resolved + * @out_irq: structure of_irq filled by this function + * + * This function resolves the PCI interrupt for a given PCI device. If a + * device-node exists for a given pci_dev, it will use normal OF tree + * walking. If not, it will implement standard swizzling and walk up the + * PCI tree until an device-node is found, at which point it will finish + * resolving using the OF tree walking. + */ +int of_irq_parse_pci(const struct pci_dev *pdev, + struct of_phandle_args *out_irq) +{ + struct device_node *dn, *ppnode; + struct pci_dev *ppdev; + __be32 laddr[3]; + u8 pin; + int rc; + + /* Check if we have a device node, if yes, fallback to standard + * device tree parsing + */ + dn = pci_device_to_OF_node(pdev); + if (dn) { + rc = of_irq_parse_one(dn, 0, out_irq); + if (!rc) + return rc; + } + + /* Ok, we don't, time to have fun. Let's start by building up an + * interrupt spec. we assume #interrupt-cells is 1, which is standard + * for PCI. If you do different, then don't use that routine. + */ + rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin); + if (rc != 0) + return rc; + /* No pin, exit */ + if (pin == 0) + return -ENODEV; + + /* Now we walk up the PCI tree */ + for (;;) { + /* Get the pci_dev of our parent */ + ppdev = pdev->bus->self; + + /* Ouch, it's a host bridge... */ + if (ppdev == NULL) { + ppnode = pci_bus_to_OF_node(pdev->bus); + + /* No node for host bridge ? give up */ + if (ppnode == NULL) + return -EINVAL; + } else { + /* We found a P2P bridge, check if it has a node */ + ppnode = pci_device_to_OF_node(ppdev); + } + + /* Ok, we have found a parent with a device-node, hand over to + * the OF parsing code. + * We build a unit address from the linux device to be used for + * resolution. Note that we use the linux bus number which may + * not match your firmware bus numbering. + * Fortunately, in most cases, interrupt-map-mask doesn't + * include the bus number as part of the matching. + * You should still be careful about that though if you intend + * to rely on this function (you ship a firmware that doesn't + * create device nodes for all PCI devices). + */ + if (ppnode) + break; + + /* We can only get here if we hit a P2P bridge with no node, + * let's do standard swizzling and try again + */ + pin = pci_swizzle_interrupt_pin(pdev, pin); + pdev = ppdev; + } + + out_irq->np = ppnode; + out_irq->args_count = 1; + out_irq->args[0] = pin; + laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); + laddr[1] = laddr[2] = cpu_to_be32(0); + return of_irq_parse_raw(laddr, out_irq); +} +EXPORT_SYMBOL_GPL(of_irq_parse_pci); + +/** + * of_irq_parse_and_map_pci() - Decode a PCI irq from the device tree and map to a virq + * @dev: The pci device needing an irq + * @slot: PCI slot number; passed when used as map_irq callback. Unused + * @pin: PCI irq pin number; passed when used as map_irq callback. Unused + * + * @slot and @pin are unused, but included in the function so that this + * function can be used directly as the map_irq callback to pci_fixup_irqs(). + */ +int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct of_phandle_args oirq; + int ret; + + ret = of_irq_parse_pci(dev, &oirq); + if (ret) { + dev_err(&dev->dev, + "of_irq_parse_pci() failed with rc=%d\n", ret); + return 0; /* Proper return code 0 == NO_IRQ */ + } + + return irq_create_of_mapping_new(&oirq); +} +EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci); diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 3d95048..d6b60f6 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -19,4 +19,12 @@ config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA +config PCI_LAYERSCAPE + bool "Freescale Layerscape PCIe controller" + depends on OF + select PCIE_DW + select MFD_SYSCON + help + Say Y here if you want PCIe controller support on Layerscape SoCs. + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index c9a997b..e293b51 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c new file mode 100644 index 0000000..c5d9ec3 --- /dev/null +++ b/drivers/pci/host/pci-layerscape.c @@ -0,0 +1,273 @@ +/* + * PCIe host controller driver for Freescale Layerscape SoCs + * + * Copyright (C) 2014 Freescale Semiconductor. + * + * Author: Minghuan Lian <Minghuan.Lian@freescale.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of_pci.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/resource.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/bitrev.h> + +#include "pcie-designware.h" + +/* PEX1/2 Misc Ports Status Register */ +#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4) +#define LTSSM_STATE_SHIFT 20 +#define LTSSM_STATE_MASK 0x3f +#define LTSSM_PCIE_L0 0x11 /* L0 state */ + +/* SCFG MSI register */ +#define SCFG_SPIMSICR 0x40 +#define SCFG_SPIMSICLRCR 0x90 + +#define MSI_LS1021A_ADDR 0x1570040 +#define MSI_LS1021A_DATA(pex_idx) (0xb3 + pex_idx) + +/* Symbol Timer Register and Filter Mask Register 1 */ +#define PCIE_STRFMR1 0x71c + +struct ls_pcie { + struct list_head node; + struct device *dev; + struct pci_bus *bus; + void __iomem *dbi; + struct regmap *scfg; + struct pcie_port pp; + int index; + int msi_irq; +}; + +#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) + +static int ls_pcie_link_up(struct pcie_port *pp) +{ + u32 state; + struct ls_pcie *pcie = to_ls_pcie(pp); + + regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); + state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; + + if (state < LTSSM_PCIE_L0) + return 0; + + return 1; +} + +static u32 ls_pcie_get_msi_addr(struct pcie_port *pp) +{ + return MSI_LS1021A_ADDR; +} + +static u32 ls_pcie_get_msi_data(struct pcie_port *pp, int pos) +{ + struct ls_pcie *pcie = to_ls_pcie(pp); + + return MSI_LS1021A_DATA(pcie->index); +} + +static irqreturn_t ls_pcie_msi_irq_handler(int irq, void *data) +{ + struct pcie_port *pp = data; + struct ls_pcie *pcie = to_ls_pcie(pp); + unsigned int msi_irq; + + /* clear the interrupt */ + regmap_write(pcie->scfg, SCFG_SPIMSICLRCR, + MSI_LS1021A_DATA(pcie->index)); + + msi_irq = irq_find_mapping(pp->irq_domain, 0); + if (!msi_irq) { + /* + * that's weird who triggered this? + * just clear it + */ + dev_err(pcie->dev, "unexpected MSI\n"); + return IRQ_NONE; + } + + generic_handle_irq(msi_irq); + return IRQ_HANDLED; +} + +static void ls_pcie_msi_clear_irq(struct pcie_port *pp, int irq) +{ +} + +static void ls_pcie_msi_set_irq(struct pcie_port *pp, int irq) +{ +} + +static void ls1021a_pcie_msi_fixup(struct pcie_port *pp) +{ + int i; + + /* + * LS1021A has only one MSI interrupt + * Set all msi interrupts as used except the first one + */ + for (i = 1; i < MAX_MSI_IRQS; i++) + set_bit(i, pp->msi_irq_in_use); +} + +static void ls_pcie_host_init(struct pcie_port *pp) +{ + struct ls_pcie *pcie = to_ls_pcie(pp); + int count = 0; + u32 val; + + dw_pcie_setup_rc(pp); + + while (!ls_pcie_link_up(pp)) { + usleep_range(100, 1000); + count++; + if (count >= 200) { + dev_err(pp->dev, "phy link never came up\n"); + return; + } + } + + if (of_device_is_compatible(pcie->dev->of_node, "fsl,ls1021a-pcie")) { + /* + * LS1021A Workaround for internal TKT228622 + * to fix the INTx hang issue + */ + val = ioread32(pcie->dbi + PCIE_STRFMR1); + val &= 0xffff; + iowrite32(val, pcie->dbi + PCIE_STRFMR1); + + ls1021a_pcie_msi_fixup(pp); + } +} + +static struct pcie_host_ops ls_pcie_host_ops = { + .link_up = ls_pcie_link_up, + .host_init = ls_pcie_host_init, + .msi_set_irq = ls_pcie_msi_set_irq, + .msi_clear_irq = ls_pcie_msi_clear_irq, + .get_msi_addr = ls_pcie_get_msi_addr, + .get_msi_data = ls_pcie_get_msi_data, +}; + +static int ls_add_pcie_port(struct ls_pcie *pcie) +{ + struct pcie_port *pp; + int ret; + + if (!pcie) + return -EINVAL; + + pp = &pcie->pp; + pp->dev = pcie->dev; + pp->dbi_base = pcie->dbi; + pp->msi_irq = pcie->msi_irq; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + ret = devm_request_irq(pp->dev, pp->msi_irq, + ls_pcie_msi_irq_handler, + IRQF_SHARED, "ls-pcie-msi", pp); + if (ret) { + dev_err(pp->dev, "failed to request msi irq\n"); + return ret; + } + } + + pp->root_bus_nr = -1; + pp->ops = &ls_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(pp->dev, "failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int __init ls_pcie_probe(struct platform_device *pdev) +{ + struct ls_pcie *pcie; + struct resource *dbi_base; + u32 index[2]; + int ret; + + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->dev = &pdev->dev; + + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!dbi_base) { + dev_err(&pdev->dev, "missing *regs* space\n"); + return -ENODEV; + } + + pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); + if (IS_ERR(pcie->dbi)) + return PTR_ERR(pcie->dbi); + + pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "fsl,pcie-scfg"); + if (IS_ERR(pcie->scfg)) { + dev_err(&pdev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(pcie->scfg); + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "fsl,pcie-scfg", index, 2); + if (ret) + return ret; + pcie->index = index[1]; + + pcie->msi_irq = platform_get_irq_byname(pdev, "msi"); + if (pcie->msi_irq < 0) { + dev_err(&pdev->dev, + "failed to get MSI IRQ: %d\n", pcie->msi_irq); + return pcie->msi_irq; + } + + ret = ls_add_pcie_port(pcie); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, pcie); + + return 0; +} + +static const struct of_device_id ls_pcie_of_match[] = { + { .compatible = "fsl,ls1021a-pcie" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ls_pcie_of_match); + +static struct platform_driver ls_pcie_driver = { + .driver = { + .name = "layerscape-pcie", + .owner = THIS_MODULE, + .of_match_table = ls_pcie_of_match, + }, +}; + +module_platform_driver_probe(ls_pcie_driver, ls_pcie_probe); + +MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@freescale.com>"); +MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 510994a..f727445 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -11,11 +11,16 @@ * published by the Free Software Foundation. */ +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/msi.h> #include <linux/of_address.h> +#include <linux/of_pci.h> #include <linux/pci.h> #include <linux/pci_regs.h> +#include <linux/platform_device.h> #include <linux/types.h> #include "pcie-designware.h" @@ -64,14 +69,16 @@ static struct hw_pci dw_pci; -unsigned long global_io_offset; +static unsigned long global_io_offset; static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) { + BUG_ON(!sys->private_data); + return sys->private_data; } -int cfg_read(void __iomem *addr, int where, int size, u32 *val) +int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) { *val = readl(addr); @@ -85,7 +92,7 @@ int cfg_read(void __iomem *addr, int where, int size, u32 *val) return PCIBIOS_SUCCESSFUL; } -int cfg_write(void __iomem *addr, int where, int size, u32 val) +int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val) { if (size == 4) writel(val, addr); @@ -115,33 +122,270 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg) writel(val, pp->dbi_base + reg); } -int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) +static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) { int ret; if (pp->ops->rd_own_conf) ret = pp->ops->rd_own_conf(pp, where, size, val); else - ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val); + ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, + size, val); return ret; } -int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) +static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) { int ret; if (pp->ops->wr_own_conf) ret = pp->ops->wr_own_conf(pp, where, size, val); else - ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size, - val); + ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where, + size, val); + + return ret; +} + +static struct irq_chip dw_msi_irq_chip = { + .name = "PCI-MSI", + .irq_enable = unmask_msi_irq, + .irq_disable = mask_msi_irq, + .irq_mask = mask_msi_irq, + .irq_unmask = unmask_msi_irq, +}; + +/* MSI int handler */ +irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) +{ + unsigned long val; + int i, pos, irq; + irqreturn_t ret = IRQ_NONE; + + for (i = 0; i < MAX_MSI_CTRLS; i++) { + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, + (u32 *)&val); + if (val) { + ret = IRQ_HANDLED; + pos = 0; + while ((pos = find_next_bit(&val, 32, pos)) != 32) { + irq = irq_find_mapping(pp->irq_domain, + i * 32 + pos); + dw_pcie_wr_own_conf(pp, + PCIE_MSI_INTR0_STATUS + i * 12, + 4, 1 << pos); + generic_handle_irq(irq); + pos++; + } + } + } return ret; } +void dw_pcie_msi_init(struct pcie_port *pp) +{ + pp->msi_data = __get_free_pages(GFP_KERNEL, 0); + + /* program the msi_data */ + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, + virt_to_phys((void *)pp->msi_data)); + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); +} + +static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) +{ + int flag = 1; + + do { + pos = find_next_zero_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos); + /*if you have reached to the end then get out from here.*/ + if (pos == MAX_MSI_IRQS) + return -ENOSPC; + /* + * Check if this position is at correct offset.nvec is always a + * power of two. pos0 must be nvec bit aligned. + */ + if (pos % msgvec) + pos += msgvec - (pos % msgvec); + else + flag = 0; + } while (flag); + + *pos0 = pos; + return 0; +} + +static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) +{ + unsigned int res, bit, val; + + res = (irq / 32) * 12; + bit = irq % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + +static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, + unsigned int nvec, unsigned int pos) +{ + unsigned int i; + + for (i = 0; i < nvec; i++) { + irq_set_msi_desc_off(irq_base, i, NULL); + clear_bit(pos + i, pp->msi_irq_in_use); + /* Disable corresponding interrupt on MSI controller */ + if (pp->ops->msi_clear_irq) + pp->ops->msi_clear_irq(pp, pos + i); + else + dw_pcie_msi_clear_irq(pp, pos + i); + } +} + +static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) +{ + unsigned int res, bit, val; + + res = (irq / 32) * 12; + bit = irq % 32; + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); + val |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); +} + +static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) +{ + int irq, pos0, pos1, i; + struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); + + pos0 = find_first_zero_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS); + if (pos0 % no_irqs) { + if (find_valid_pos0(pp, no_irqs, pos0, &pos0)) + goto no_valid_irq; + } + if (no_irqs > 1) { + pos1 = find_next_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos0); + /* there must be nvec number of consecutive free bits */ + while ((pos1 - pos0) < no_irqs) { + if (find_valid_pos0(pp, no_irqs, pos1, &pos0)) + goto no_valid_irq; + pos1 = find_next_bit(pp->msi_irq_in_use, + MAX_MSI_IRQS, pos0); + } + } + + irq = irq_find_mapping(pp->irq_domain, pos0); + if (!irq) + goto no_valid_irq; + + /* + * irq_create_mapping (called from dw_pcie_host_init) pre-allocates + * descs so there is no need to allocate descs here. We can therefore + * assume that if irq_find_mapping above returns non-zero, then the + * descs are also successfully allocated. + */ + + for (i = 0; i < no_irqs; i++) { + if (irq_set_msi_desc_off(irq, i, desc) != 0) { + clear_irq_range(pp, irq, i, pos0); + goto no_valid_irq; + } + set_bit(pos0 + i, pp->msi_irq_in_use); + /*Enable corresponding interrupt in MSI interrupt controller */ + if (pp->ops->msi_set_irq) + pp->ops->msi_set_irq(pp, pos0 + i); + else + dw_pcie_msi_set_irq(pp, pos0 + i); + } + + *pos = pos0; + return irq; + +no_valid_irq: + *pos = pos0; + return -ENOSPC; +} + +static void clear_irq(unsigned int irq) +{ + unsigned int pos, nvec; + struct msi_desc *msi; + struct pcie_port *pp; + struct irq_data *data = irq_get_irq_data(irq); + + /* get the port structure */ + msi = irq_data_get_msi(data); + pp = sys_to_pcie(msi->dev->bus->sysdata); + + /* undo what was done in assign_irq */ + pos = data->hwirq; + nvec = 1 << msi->msi_attrib.multiple; + + clear_irq_range(pp, irq, nvec, pos); + + /* all irqs cleared; reset attributes */ + msi->irq = 0; + msi->msi_attrib.multiple = 0; +} + +static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, + struct msi_desc *desc) +{ + int irq, pos, msgvec; + u16 msg_ctr; + struct msi_msg msg; + struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); + + pci_read_config_word(pdev, pdev->msi_cap + PCI_MSI_FLAGS, &msg_ctr); + msgvec = (msg_ctr & PCI_MSI_FLAGS_QSIZE) >> 4; + if (msgvec == 0) + msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1; + if (msgvec > 5) + msgvec = 0; + + irq = assign_irq((1 << msgvec), desc, &pos); + if (irq < 0) + return irq; + + /* + * write_msi_msg() will update PCI_MSI_FLAGS so there is + * no need to explicitly call pci_write_config_word(). + */ + desc->msi_attrib.multiple = msgvec; + + if (pp->ops->get_msi_addr) + msg.address_lo = pp->ops->get_msi_addr(pp); + else + msg.address_lo = virt_to_phys((void *)pp->msi_data); + msg.address_hi = 0x0; + + if (pp->ops->get_msi_data) + msg.data = pp->ops->get_msi_data(pp, pos); + else + msg.data = pos; + + write_msi_msg(irq, &msg); + + return 0; +} + +static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) +{ + clear_irq(irq); +} + +static struct msi_chip dw_pcie_msi_chip = { + .setup_irq = dw_msi_setup_irq, + .teardown_irq = dw_msi_teardown_irq, +}; + int dw_pcie_link_up(struct pcie_port *pp) { if (pp->ops->link_up) @@ -150,12 +394,52 @@ int dw_pcie_link_up(struct pcie_port *pp) return 0; } +static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +static const struct irq_domain_ops msi_domain_ops = { + .map = dw_pcie_msi_map, +}; + int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; + struct platform_device *pdev = to_platform_device(pp->dev); struct of_pci_range range; struct of_pci_range_parser parser; - u32 val; + struct resource *cfg_res; + u32 val, na, ns; + const __be32 *addrp; + int i, index, ret; + + /* Find the address cell size and the number of cells in order to get + * the untranslated address. + */ + of_property_read_u32(np, "#address-cells", &na); + ns = of_n_size_cells(np); + + cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (cfg_res) { + pp->cfg0_size = resource_size(cfg_res)/2; + pp->cfg1_size = resource_size(cfg_res)/2; + pp->cfg0_base = cfg_res->start; + pp->cfg1_base = cfg_res->start + pp->cfg0_size; + + /* Find the untranslated configuration space address */ + index = of_property_match_string(np, "reg-names", "config"); + addrp = of_get_address(np, index, NULL, NULL); + pp->cfg0_mod_base = of_read_number(addrp, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; + } else { + dev_err(pp->dev, "missing *config* reg space\n"); + } if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); @@ -174,23 +458,50 @@ int __init dw_pcie_host_init(struct pcie_port *pp) pp->io.end = min_t(resource_size_t, IO_SPACE_LIMIT, range.pci_addr + range.size - + global_io_offset); - pp->config.io_size = resource_size(&pp->io); - pp->config.io_bus_addr = range.pci_addr; + + global_io_offset - 1); + pp->io_size = resource_size(&pp->io); + pp->io_bus_addr = range.pci_addr; + pp->io_base = range.cpu_addr; + + /* Find the untranslated IO space address */ + pp->io_mod_base = of_read_number(parser.range - + parser.np + na, ns); } if (restype == IORESOURCE_MEM) { of_pci_range_to_resource(&range, np, &pp->mem); pp->mem.name = "MEM"; - pp->config.mem_size = resource_size(&pp->mem); - pp->config.mem_bus_addr = range.pci_addr; + pp->mem_size = resource_size(&pp->mem); + pp->mem_bus_addr = range.pci_addr; + + /* Find the untranslated MEM space address */ + pp->mem_mod_base = of_read_number(parser.range - + parser.np + na, ns); } if (restype == 0) { of_pci_range_to_resource(&range, np, &pp->cfg); - pp->config.cfg0_size = resource_size(&pp->cfg)/2; - pp->config.cfg1_size = resource_size(&pp->cfg)/2; + pp->cfg0_size = resource_size(&pp->cfg)/2; + pp->cfg1_size = resource_size(&pp->cfg)/2; + pp->cfg0_base = pp->cfg.start; + pp->cfg1_base = pp->cfg.start + pp->cfg0_size; + + /* Find the untranslated configuration space address */ + pp->cfg0_mod_base = of_read_number(parser.range - + parser.np + na, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + + pp->cfg0_size; } } + ret = of_pci_parse_bus_range(np, &pp->busn); + if (ret < 0) { + pp->busn.name = np->name; + pp->busn.start = 0; + pp->busn.end = 0xff; + pp->busn.flags = IORESOURCE_BUS; + dev_dbg(pp->dev, "failed to parse bus-range property: %d, using default %pR\n", + ret, &pp->busn); + } + if (!pp->dbi_base) { pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start, resource_size(&pp->cfg)); @@ -200,22 +511,24 @@ int __init dw_pcie_host_init(struct pcie_port *pp) } } - pp->cfg0_base = pp->cfg.start; - pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size; - pp->io_base = pp->io.start; pp->mem_base = pp->mem.start; - pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, - pp->config.cfg0_size); if (!pp->va_cfg0_base) { - dev_err(pp->dev, "error with ioremap in function\n"); - return -ENOMEM; + pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, + pp->cfg0_size); + if (!pp->va_cfg0_base) { + dev_err(pp->dev, "error with ioremap in function\n"); + return -ENOMEM; + } } - pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base, - pp->config.cfg1_size); + if (!pp->va_cfg1_base) { - dev_err(pp->dev, "error with ioremap\n"); - return -ENOMEM; + pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base, + pp->cfg1_size); + if (!pp->va_cfg1_base) { + dev_err(pp->dev, "error with ioremap\n"); + return -ENOMEM; + } } if (of_property_read_u32(np, "num-lanes", &pp->lanes)) { @@ -223,6 +536,25 @@ int __init dw_pcie_host_init(struct pcie_port *pp) return -EINVAL; } + if (IS_ENABLED(CONFIG_PCI_MSI)) { + if (!pp->ops->msi_host_init) { + pp->irq_domain = irq_domain_add_linear(pp->dev->of_node, + MAX_MSI_IRQS, &msi_domain_ops, + &dw_pcie_msi_chip); + if (!pp->irq_domain) { + dev_err(pp->dev, "irq domain init failed\n"); + return -ENXIO; + } + + for (i = 0; i < MAX_MSI_IRQS; i++) + irq_create_mapping(pp->irq_domain, i); + } else { + ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); + if (ret < 0) + return ret; + } + } + if (pp->ops->host_init) pp->ops->host_init(pp); @@ -238,8 +570,7 @@ int __init dw_pcie_host_init(struct pcie_port *pp) dw_pci.nr_controllers = 1; dw_pci.private_data = (void **)&pp; - pci_common_init(&dw_pci); - pci_assign_unassigned_resources(); + pci_common_init_dev(pp->dev, &dw_pci); #ifdef CONFIG_PCI_DOMAINS dw_pci.domain++; #endif @@ -252,9 +583,9 @@ static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) /* Program viewport 0 : OUTBOUND : CFG0 */ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, pp->cfg0_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg0_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1, + dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -268,9 +599,9 @@ static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->cfg1_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg1_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1, + dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1, PCIE_ATU_LIMIT); dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); @@ -283,12 +614,12 @@ static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->mem_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->mem_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->mem_base + pp->config.mem_size - 1, + dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1, PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr), + dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr), PCIE_ATU_UPPER_TARGET); dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); } @@ -299,12 +630,12 @@ static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, PCIE_ATU_VIEWPORT); dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->io_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->io_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->io_base + pp->config.io_size - 1, + dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1, PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr), + dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr), PCIE_ATU_UPPER_TARGET); dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); } @@ -321,11 +652,13 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, if (bus->parent->number == pp->root_bus_nr) { dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = cfg_read(pp->va_cfg0_base + address, where, size, val); + ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, + val); dw_pcie_prog_viewport_mem_outbound(pp); } else { dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = cfg_read(pp->va_cfg1_base + address, where, size, val); + ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, + val); dw_pcie_prog_viewport_io_outbound(pp); } @@ -344,18 +677,19 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, if (bus->parent->number == pp->root_bus_nr) { dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = cfg_write(pp->va_cfg0_base + address, where, size, val); + ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, + val); dw_pcie_prog_viewport_mem_outbound(pp); } else { dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = cfg_write(pp->va_cfg1_base + address, where, size, val); + ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, + val); dw_pcie_prog_viewport_io_outbound(pp); } return ret; } - static int dw_pcie_valid_config(struct pcie_port *pp, struct pci_bus *bus, int dev) { @@ -383,26 +717,22 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 *val) { struct pcie_port *pp = sys_to_pcie(bus->sysdata); - unsigned long flags; int ret; - if (!pp) { - BUG(); - return -EINVAL; - } - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) { *val = 0xffffffff; return PCIBIOS_DEVICE_NOT_FOUND; } - spin_lock_irqsave(&pp->conf_lock, flags); if (bus->number != pp->root_bus_nr) - ret = dw_pcie_rd_other_conf(pp, bus, devfn, + if (pp->ops->rd_other_conf) + ret = pp->ops->rd_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_rd_own_conf(pp, where, size, val); - spin_unlock_irqrestore(&pp->conf_lock, flags); return ret; } @@ -411,24 +741,20 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 val) { struct pcie_port *pp = sys_to_pcie(bus->sysdata); - unsigned long flags; int ret; - if (!pp) { - BUG(); - return -EINVAL; - } - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) return PCIBIOS_DEVICE_NOT_FOUND; - spin_lock_irqsave(&pp->conf_lock, flags); if (bus->number != pp->root_bus_nr) - ret = dw_pcie_wr_other_conf(pp, bus, devfn, + if (pp->ops->wr_other_conf) + ret = pp->ops->wr_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); else ret = dw_pcie_wr_own_conf(pp, where, size, val); - spin_unlock_irqrestore(&pp->conf_lock, flags); return ret; } @@ -438,67 +764,82 @@ static struct pci_ops dw_pcie_ops = { .write = dw_pcie_wr_conf, }; -int dw_pcie_setup(int nr, struct pci_sys_data *sys) +static int dw_pcie_setup(int nr, struct pci_sys_data *sys) { struct pcie_port *pp; pp = sys_to_pcie(sys); - if (!pp) - return 0; - - if (global_io_offset < SZ_1M && pp->config.io_size > 0) { - sys->io_offset = global_io_offset - pp->config.io_bus_addr; - pci_ioremap_io(sys->io_offset, pp->io.start); + if (global_io_offset < SZ_1M && pp->io_size > 0) { + sys->io_offset = global_io_offset - pp->io_bus_addr; + pci_ioremap_io(global_io_offset, pp->io_base); global_io_offset += SZ_64K; pci_add_resource_offset(&sys->resources, &pp->io, sys->io_offset); } - sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr; + sys->mem_offset = pp->mem.start - pp->mem_bus_addr; pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset); + pci_add_resource(&sys->resources, &pp->busn); return 1; } -struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) +static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) { struct pci_bus *bus; struct pcie_port *pp = sys_to_pcie(sys); - if (pp) { - pp->root_bus_nr = sys->busnr; - bus = pci_scan_root_bus(NULL, sys->busnr, &dw_pcie_ops, - sys, &sys->resources); - } else { - bus = NULL; - BUG(); - } + pp->root_bus_nr = sys->busnr; + bus = pci_create_root_bus(pp->dev, sys->busnr, + &dw_pcie_ops, sys, &sys->resources); + if (!bus) + return NULL; + + pci_scan_child_bus(bus); + + if (bus && pp->ops->scan_bus) + pp->ops->scan_bus(pp); return bus; } -int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata); + int irq; + + irq = of_irq_parse_and_map_pci(dev, slot, pin); + if (!irq) + irq = pp->irq; + + return irq; +} - return pp->irq; +static void dw_pcie_add_bus(struct pci_bus *bus) +{ + if (IS_ENABLED(CONFIG_PCI_MSI)) { + struct pcie_port *pp = sys_to_pcie(bus->sysdata); + + dw_pcie_msi_chip.dev = pp->dev; + bus->msi = &dw_pcie_msi_chip; + } } static struct hw_pci dw_pci = { .setup = dw_pcie_setup, .scan = dw_pcie_scan_bus, .map_irq = dw_pcie_map_irq, + .add_bus = dw_pcie_add_bus, }; void dw_pcie_setup_rc(struct pcie_port *pp) { - struct pcie_port_info *config = &pp->config; u32 val; u32 membase; u32 memlimit; - /* set the number of lines as 4 */ + /* set the number of lanes */ dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val); val &= ~PORT_LINK_MODE_MASK; switch (pp->lanes) { @@ -548,7 +889,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* setup memory base, memory limit */ membase = ((u32)pp->mem_base & 0xfff00000) >> 16; - memlimit = (config->mem_size + (u32)pp->mem_base) & 0xfff00000; + memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000; val = memlimit | membase; dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index 133820f..c625675 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -11,33 +11,48 @@ * published by the Free Software Foundation. */ -struct pcie_port_info { - u32 cfg0_size; - u32 cfg1_size; - u32 io_size; - u32 mem_size; - phys_addr_t io_bus_addr; - phys_addr_t mem_bus_addr; -}; +#ifndef _PCIE_DESIGNWARE_H +#define _PCIE_DESIGNWARE_H + +/* + * Maximum number of MSI IRQs can be 256 per controller. But keep + * it 32 as of now. Probably we will never need more than 32. If needed, + * then increment it in multiple of 32. + */ +#define MAX_MSI_IRQS 32 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) struct pcie_port { struct device *dev; u8 root_bus_nr; void __iomem *dbi_base; u64 cfg0_base; + u64 cfg0_mod_base; void __iomem *va_cfg0_base; + u32 cfg0_size; u64 cfg1_base; + u64 cfg1_mod_base; void __iomem *va_cfg1_base; + u32 cfg1_size; u64 io_base; + u64 io_mod_base; + phys_addr_t io_bus_addr; + u32 io_size; u64 mem_base; - spinlock_t conf_lock; + u64 mem_mod_base; + phys_addr_t mem_bus_addr; + u32 mem_size; struct resource cfg; struct resource io; struct resource mem; - struct pcie_port_info config; + struct resource busn; int irq; u32 lanes; struct pcie_host_ops *ops; + int msi_irq; + struct irq_domain *irq_domain; + unsigned long msi_data; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; struct pcie_host_ops { @@ -47,19 +62,26 @@ struct pcie_host_ops { u32 val, void __iomem *dbi_base); int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); + int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); int (*link_up)(struct pcie_port *pp); void (*host_init)(struct pcie_port *pp); + void (*msi_set_irq)(struct pcie_port *pp, int irq); + void (*msi_clear_irq)(struct pcie_port *pp, int irq); + u32 (*get_msi_addr)(struct pcie_port *pp); + u32 (*get_msi_data)(struct pcie_port *pp, int pos); + void (*scan_bus)(struct pcie_port *pp); + int (*msi_host_init)(struct pcie_port *pp, struct msi_chip *chip); }; -extern unsigned long global_io_offset; - -int cfg_read(void __iomem *addr, int where, int size, u32 *val); -int cfg_write(void __iomem *addr, int where, int size, u32 val); -int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val); -int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val); +int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); +int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val); +irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); +void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); -int dw_pcie_setup(int nr, struct pci_sys_data *sys); -struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys); -int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); + +#endif /* _PCIE_DESIGNWARE_H */ diff --git a/drivers/platform/fsl/sleep_fsm.c b/drivers/platform/fsl/sleep_fsm.c index 35b033e..987a93a8 100644 --- a/drivers/platform/fsl/sleep_fsm.c +++ b/drivers/platform/fsl/sleep_fsm.c @@ -12,342 +12,261 @@ * option) any later version. */ +#include <linux/kernel.h> #include <linux/io.h> #include <linux/types.h> -#define FSL_STRIDE_4B 4 -#define FSL_STRIDE_8B 8 - -/* End flag */ -#define FSM_END_FLAG 0xFFFFFFFFUL - -/* EPGCR (Event Processor Global Control Register) */ -#define EPGCR 0x000 - -/* EPEVTCR0-9 (Event Processor EVT Pin Control Registers) */ -#define EPEVTCR0 0x050 -#define EPEVTCR9 0x074 -#define EPEVTCR_STRIDE FSL_STRIDE_4B - -/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */ -#define EPXTRIGCR 0x090 - -/* EPIMCR0-31 (Event Processor Input Mux Control Registers) */ -#define EPIMCR0 0x100 -#define EPIMCR31 0x17C -#define EPIMCR_STRIDE FSL_STRIDE_4B - -/* EPSMCR0-15 (Event Processor SCU Mux Control Registers) */ -#define EPSMCR0 0x200 -#define EPSMCR15 0x278 -#define EPSMCR_STRIDE FSL_STRIDE_8B - -/* EPECR0-15 (Event Processor Event Control Registers) */ -#define EPECR0 0x300 -#define EPECR15 0x33C -#define EPECR_STRIDE FSL_STRIDE_4B - -/* EPACR0-15 (Event Processor Action Control Registers) */ -#define EPACR0 0x400 -#define EPACR15 0x43C -#define EPACR_STRIDE FSL_STRIDE_4B - -/* EPCCRi0-15 (Event Processor Counter Control Registers) */ -#define EPCCR0 0x800 -#define EPCCR15 0x83C -#define EPCCR_STRIDE FSL_STRIDE_4B - -/* EPCMPR0-15 (Event Processor Counter Compare Registers) */ -#define EPCMPR0 0x900 -#define EPCMPR15 0x93C -#define EPCMPR_STRIDE FSL_STRIDE_4B - -/* EPCTR0-31 (Event Processor Counter Register) */ -#define EPCTR0 0xA00 -#define EPCTR31 0xA7C -#define EPCTR_STRIDE FSL_STRIDE_4B - -/* NPC triggered Memory-Mapped Access Registers */ -#define NCR 0x000 -#define MCCR1 0x0CC -#define MCSR1 0x0D0 -#define MMAR1LO 0x0D4 -#define MMAR1HI 0x0D8 -#define MMDR1 0x0DC -#define MCSR2 0x0E0 -#define MMAR2LO 0x0E4 -#define MMAR2HI 0x0E8 -#define MMDR2 0x0EC -#define MCSR3 0x0F0 -#define MMAR3LO 0x0F4 -#define MMAR3HI 0x0F8 -#define MMDR3 0x0FC - -/* RCPM Core State Action Control Register 0 */ -#define CSTTACR0 0xB00 - -/* RCPM Core Group 1 Configuration Register 0 */ -#define CG1CR0 0x31C - -/* Block offsets */ -#define RCPM_BLOCK_OFFSET 0x00022000 -#define EPU_BLOCK_OFFSET 0x00000000 -#define NPC_BLOCK_OFFSET 0x00001000 - -struct fsm_reg_vals { - u32 offset; - u32 value; -}; - +#include "sleep_fsm.h" /* * These values are from chip's reference manual. For example, * the values for T1040 can be found in "8.4.3.8 Programming * supporting deep sleep mode" of Chapter 8 "Run Control and * Power Management (RCPM)". - * The default value can be applied to T104x. + * The default value can be applied to T104x, LS1021. */ -struct fsm_reg_vals fsm_default_val[] = { +struct fsm_reg_vals epu_default_val[] = { /* EPGCR (Event Processor Global Control Register) */ - {EPU_BLOCK_OFFSET + EPGCR, 0}, + {EPGCR, 0}, /* EPECR (Event Processor Event Control Registers) */ - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 0, 0}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 1, 0}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 2, 0xF0004004}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 3, 0x80000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 4, 0x20000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 5, 0x08000004}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 6, 0x80000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 7, 0x80000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 8, 0x60000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 9, 0x08000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 10, 0x42000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 11, 0x90000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 12, 0x80000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 13, 0x08000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 14, 0x02000084}, - {EPU_BLOCK_OFFSET + EPECR0 + EPECR_STRIDE * 15, 0x00000004}, + {EPECR0 + EPECR_STRIDE * 0, 0}, + {EPECR0 + EPECR_STRIDE * 1, 0}, + {EPECR0 + EPECR_STRIDE * 2, 0xF0004004}, + {EPECR0 + EPECR_STRIDE * 3, 0x80000084}, + {EPECR0 + EPECR_STRIDE * 4, 0x20000084}, + {EPECR0 + EPECR_STRIDE * 5, 0x08000004}, + {EPECR0 + EPECR_STRIDE * 6, 0x80000084}, + {EPECR0 + EPECR_STRIDE * 7, 0x80000084}, + {EPECR0 + EPECR_STRIDE * 8, 0x60000084}, + {EPECR0 + EPECR_STRIDE * 9, 0x08000084}, + {EPECR0 + EPECR_STRIDE * 10, 0x42000084}, + {EPECR0 + EPECR_STRIDE * 11, 0x90000084}, + {EPECR0 + EPECR_STRIDE * 12, 0x80000084}, + {EPECR0 + EPECR_STRIDE * 13, 0x08000084}, + {EPECR0 + EPECR_STRIDE * 14, 0x02000084}, + {EPECR0 + EPECR_STRIDE * 15, 0x00000004}, /* * EPEVTCR (Event Processor EVT Pin Control Registers) * SCU8 triger EVT2, and SCU11 triger EVT9 */ - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 0, 0}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 1, 0}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 2, 0x80000001}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 3, 0}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 4, 0}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 5, 0}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 6, 0}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 7, 0}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 8, 0}, - {EPU_BLOCK_OFFSET + EPEVTCR0 + EPEVTCR_STRIDE * 9, 0xB0000001}, + {EPEVTCR0 + EPEVTCR_STRIDE * 0, 0}, + {EPEVTCR0 + EPEVTCR_STRIDE * 1, 0}, + {EPEVTCR0 + EPEVTCR_STRIDE * 2, 0x80000001}, + {EPEVTCR0 + EPEVTCR_STRIDE * 3, 0}, + {EPEVTCR0 + EPEVTCR_STRIDE * 4, 0}, + {EPEVTCR0 + EPEVTCR_STRIDE * 5, 0}, + {EPEVTCR0 + EPEVTCR_STRIDE * 6, 0}, + {EPEVTCR0 + EPEVTCR_STRIDE * 7, 0}, + {EPEVTCR0 + EPEVTCR_STRIDE * 8, 0}, + {EPEVTCR0 + EPEVTCR_STRIDE * 9, 0xB0000001}, /* EPCMPR (Event Processor Counter Compare Registers) */ - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 0, 0}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 1, 0}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 2, 0x000000FF}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 3, 0}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 4, 0x000000FF}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 5, 0x00000020}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 6, 0}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 7, 0}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 8, 0x000000FF}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 9, 0x000000FF}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 10, 0x000000FF}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 11, 0x000000FF}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 12, 0x000000FF}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 13, 0}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 14, 0x000000FF}, - {EPU_BLOCK_OFFSET + EPCMPR0 + EPCMPR_STRIDE * 15, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 0, 0}, + {EPCMPR0 + EPCMPR_STRIDE * 1, 0}, + {EPCMPR0 + EPCMPR_STRIDE * 2, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 3, 0}, + {EPCMPR0 + EPCMPR_STRIDE * 4, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 5, 0x00000020}, + {EPCMPR0 + EPCMPR_STRIDE * 6, 0}, + {EPCMPR0 + EPCMPR_STRIDE * 7, 0}, + {EPCMPR0 + EPCMPR_STRIDE * 8, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 9, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 10, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 11, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 12, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 13, 0}, + {EPCMPR0 + EPCMPR_STRIDE * 14, 0x000000FF}, + {EPCMPR0 + EPCMPR_STRIDE * 15, 0x000000FF}, /* EPCCR (Event Processor Counter Control Registers) */ - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 0, 0}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 1, 0}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 2, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 3, 0}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 4, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 5, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 6, 0}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 7, 0}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 8, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 9, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 10, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 11, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 12, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 13, 0}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 14, 0x92840000}, - {EPU_BLOCK_OFFSET + EPCCR0 + EPCCR_STRIDE * 15, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 0, 0}, + {EPCCR0 + EPCCR_STRIDE * 1, 0}, + {EPCCR0 + EPCCR_STRIDE * 2, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 3, 0}, + {EPCCR0 + EPCCR_STRIDE * 4, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 5, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 6, 0}, + {EPCCR0 + EPCCR_STRIDE * 7, 0}, + {EPCCR0 + EPCCR_STRIDE * 8, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 9, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 10, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 11, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 12, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 13, 0}, + {EPCCR0 + EPCCR_STRIDE * 14, 0x92840000}, + {EPCCR0 + EPCCR_STRIDE * 15, 0x92840000}, /* EPSMCR (Event Processor SCU Mux Control Registers) */ - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 0, 0}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 1, 0}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 2, 0x6C700000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 3, 0x2F000000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 4, 0x002F0000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 5, 0x00002E00}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 6, 0x7C000000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 7, 0x30000000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 8, 0x64300000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 9, 0x00003000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 10, 0x65000030}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 11, 0x31740000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 12, 0x7F000000}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 13, 0x00003100}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 14, 0x00000031}, - {EPU_BLOCK_OFFSET + EPSMCR0 + EPSMCR_STRIDE * 15, 0x76000000}, + {EPSMCR0 + EPSMCR_STRIDE * 0, 0}, + {EPSMCR0 + EPSMCR_STRIDE * 1, 0}, + {EPSMCR0 + EPSMCR_STRIDE * 2, 0x6C700000}, + {EPSMCR0 + EPSMCR_STRIDE * 3, 0x2F000000}, + {EPSMCR0 + EPSMCR_STRIDE * 4, 0x002F0000}, + {EPSMCR0 + EPSMCR_STRIDE * 5, 0x00002E00}, + {EPSMCR0 + EPSMCR_STRIDE * 6, 0x7C000000}, + {EPSMCR0 + EPSMCR_STRIDE * 7, 0x30000000}, + {EPSMCR0 + EPSMCR_STRIDE * 8, 0x64300000}, + {EPSMCR0 + EPSMCR_STRIDE * 9, 0x00003000}, + {EPSMCR0 + EPSMCR_STRIDE * 10, 0x65000030}, + {EPSMCR0 + EPSMCR_STRIDE * 11, 0x31740000}, + {EPSMCR0 + EPSMCR_STRIDE * 12, 0x7F000000}, + {EPSMCR0 + EPSMCR_STRIDE * 13, 0x00003100}, + {EPSMCR0 + EPSMCR_STRIDE * 14, 0x00000031}, + {EPSMCR0 + EPSMCR_STRIDE * 15, 0x76000000}, /* EPACR (Event Processor Action Control Registers) */ - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 0, 0}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 1, 0}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 2, 0}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 3, 0x00000080}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 4, 0}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 5, 0x00000040}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 6, 0}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 7, 0}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 8, 0}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 9, 0x0000001C}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 10, 0x00000020}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 11, 0}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 12, 0x00000003}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 13, 0x06000000}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 14, 0x04000000}, - {EPU_BLOCK_OFFSET + EPACR0 + EPACR_STRIDE * 15, 0x02000000}, + {EPACR0 + EPACR_STRIDE * 0, 0}, + {EPACR0 + EPACR_STRIDE * 1, 0}, + {EPACR0 + EPACR_STRIDE * 2, 0}, + {EPACR0 + EPACR_STRIDE * 3, 0x00000080}, + {EPACR0 + EPACR_STRIDE * 4, 0}, + {EPACR0 + EPACR_STRIDE * 5, 0x00000040}, + {EPACR0 + EPACR_STRIDE * 6, 0}, + {EPACR0 + EPACR_STRIDE * 7, 0}, + {EPACR0 + EPACR_STRIDE * 8, 0}, + {EPACR0 + EPACR_STRIDE * 9, 0x0000001C}, + {EPACR0 + EPACR_STRIDE * 10, 0x00000020}, + {EPACR0 + EPACR_STRIDE * 11, 0}, + {EPACR0 + EPACR_STRIDE * 12, 0x00000003}, + {EPACR0 + EPACR_STRIDE * 13, 0x06000000}, + {EPACR0 + EPACR_STRIDE * 14, 0x04000000}, + {EPACR0 + EPACR_STRIDE * 15, 0x02000000}, /* EPIMCR (Event Processor Input Mux Control Registers) */ - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 0, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 1, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 2, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 3, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 4, 0x44000000}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 5, 0x40000000}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 6, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 7, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 8, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 9, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 10, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 11, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 12, 0x44000000}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 13, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 14, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 15, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 16, 0x6A000000}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 17, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 18, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 19, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 20, 0x48000000}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 21, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 22, 0x6C000000}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 23, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 24, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 25, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 26, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 27, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 28, 0x76000000}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 29, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 30, 0}, - {EPU_BLOCK_OFFSET + EPIMCR0 + EPIMCR_STRIDE * 31, 0x76000000}, + {EPIMCR0 + EPIMCR_STRIDE * 0, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 1, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 2, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 3, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 4, 0x44000000}, + {EPIMCR0 + EPIMCR_STRIDE * 5, 0x40000000}, + {EPIMCR0 + EPIMCR_STRIDE * 6, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 7, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 8, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 9, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 10, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 11, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 12, 0x44000000}, + {EPIMCR0 + EPIMCR_STRIDE * 13, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 14, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 15, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 16, 0x6A000000}, + {EPIMCR0 + EPIMCR_STRIDE * 17, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 18, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 19, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 20, 0x48000000}, + {EPIMCR0 + EPIMCR_STRIDE * 21, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 22, 0x6C000000}, + {EPIMCR0 + EPIMCR_STRIDE * 23, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 24, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 25, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 26, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 27, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 28, 0x76000000}, + {EPIMCR0 + EPIMCR_STRIDE * 29, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 30, 0}, + {EPIMCR0 + EPIMCR_STRIDE * 31, 0x76000000}, /* EPXTRIGCR (Event Processor Crosstrigger Control Register) */ - {EPU_BLOCK_OFFSET + EPXTRIGCR, 0x0000FFDF}, + {EPXTRIGCR, 0x0000FFDF}, + /* end */ + {FSM_END_FLAG, 0}, +}; + +struct fsm_reg_vals npc_default_val[] = { /* NPC triggered Memory-Mapped Access Registers */ - {NPC_BLOCK_OFFSET + NCR, 0x80000000}, - {NPC_BLOCK_OFFSET + MCCR1, 0}, - {NPC_BLOCK_OFFSET + MCSR1, 0}, - {NPC_BLOCK_OFFSET + MMAR1LO, 0}, - {NPC_BLOCK_OFFSET + MMAR1HI, 0}, - {NPC_BLOCK_OFFSET + MMDR1, 0}, - {NPC_BLOCK_OFFSET + MCSR2, 0}, - {NPC_BLOCK_OFFSET + MMAR2LO, 0}, - {NPC_BLOCK_OFFSET + MMAR2HI, 0}, - {NPC_BLOCK_OFFSET + MMDR2, 0}, - {NPC_BLOCK_OFFSET + MCSR3, 0x80000000}, - {NPC_BLOCK_OFFSET + MMAR3LO, 0x000E2130}, - {NPC_BLOCK_OFFSET + MMAR3HI, 0x00030000}, - {NPC_BLOCK_OFFSET + MMDR3, 0x00020000}, - /* Configure RCPM for detecting Core0’s PH15 state */ - {RCPM_BLOCK_OFFSET + CSTTACR0, 0x00001001}, - {RCPM_BLOCK_OFFSET + CG1CR0, 0x00000001}, + {NCR, 0x80000000}, + {MCCR1, 0}, + {MCSR1, 0}, + {MMAR1LO, 0}, + {MMAR1HI, 0}, + {MMDR1, 0}, + {MCSR2, 0}, + {MMAR2LO, 0}, + {MMAR2HI, 0}, + {MMDR2, 0}, + {MCSR3, 0x80000000}, + {MMAR3LO, 0x000E2130}, + {MMAR3HI, 0x00030000}, + {MMDR3, 0x00020000}, /* end */ {FSM_END_FLAG, 0}, }; /** - * fsl_dp_fsm_clean - Clear EPU's FSM - * @dcsr_base: the base address of DCSR registers - * @val: Pointer to values for FSM registers. If NULL, - * will use the default value. + * fsl_fsm_setup - Configure EPU's FSM registers + * @base: the base address of registers + * @val: Pointer to address-value pairs for FSM registers */ -void fsl_dp_fsm_clean(void __iomem *dcsr_base, struct fsm_reg_vals *val) +void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val) { - void *epu_base = dcsr_base + EPU_BLOCK_OFFSET; - u32 offset; - struct fsm_reg_vals *data; + struct fsm_reg_vals *data = val; + + BUG_ON(!base || !data); + while (data->offset != FSM_END_FLAG) { + iowrite32be(data->value, base + data->offset); + data++; + } +} + +void fsl_epu_setup_default(void __iomem *epu_base) +{ + fsl_fsm_setup(epu_base, epu_default_val); +} + +void fsl_npc_setup_default(void __iomem *npc_base) +{ + fsl_fsm_setup(npc_base, npc_default_val); +} - if (val) { - data = val; - while (data->offset != FSM_END_FLAG) { - out_be32(dcsr_base + data->offset, 0); - data++; - } - return; +/** + * fsl_fsm_clean - Clear EPU's FSM registers + * @base: the base address of registers + * @val: Pointer to address-value pairs for FSM registers + */ +void fsl_fsm_clean(void __iomem *base, struct fsm_reg_vals *val) +{ + struct fsm_reg_vals *data = val; + + BUG_ON(!base || !data); + while (data->offset != FSM_END_FLAG) { + iowrite32be(0, base + data->offset); + data++; } +} + +void fsl_epu_clean_default(void __iomem *epu_base) +{ + u32 offset; /* follow the exact sequence to clear the registers */ /* Clear EPACRn */ for (offset = EPACR0; offset <= EPACR15; offset += EPACR_STRIDE) - out_be32(epu_base + offset, 0); + iowrite32be(0, epu_base + offset); /* Clear EPEVTCRn */ for (offset = EPEVTCR0; offset <= EPEVTCR9; offset += EPEVTCR_STRIDE) - out_be32(epu_base + offset, 0); + iowrite32be(0, epu_base + offset); /* Clear EPGCR */ - out_be32(epu_base + EPGCR, 0); + iowrite32be(0, epu_base + EPGCR); /* Clear EPSMCRn */ for (offset = EPSMCR0; offset <= EPSMCR15; offset += EPSMCR_STRIDE) - out_be32(epu_base + offset, 0); + iowrite32be(0, epu_base + offset); /* Clear EPCCRn */ - for (offset = EPCCR0; offset <= EPCCR15; offset += EPCCR_STRIDE) - out_be32(epu_base + offset, 0); + for (offset = EPCCR0; offset <= EPCCR31; offset += EPCCR_STRIDE) + iowrite32be(0, epu_base + offset); /* Clear EPCMPRn */ - for (offset = EPCMPR0; offset <= EPCMPR15; offset += EPCMPR_STRIDE) - out_be32(epu_base + offset, 0); + for (offset = EPCMPR0; offset <= EPCMPR31; offset += EPCMPR_STRIDE) + iowrite32be(0, epu_base + offset); /* Clear EPCTRn */ for (offset = EPCTR0; offset <= EPCTR31; offset += EPCTR_STRIDE) - out_be32(epu_base + offset, 0); + iowrite32be(0, epu_base + offset); /* Clear EPIMCRn */ for (offset = EPIMCR0; offset <= EPIMCR31; offset += EPIMCR_STRIDE) - out_be32(epu_base + offset, 0); + iowrite32be(0, epu_base + offset); /* Clear EPXTRIGCRn */ - out_be32(epu_base + EPXTRIGCR, 0); + iowrite32be(0, epu_base + EPXTRIGCR); /* Clear EPECRn */ for (offset = EPECR0; offset <= EPECR15; offset += EPECR_STRIDE) - out_be32(epu_base + offset, 0); -} - -/** - * fsl_dp_fsm_setup - Configure EPU's FSM - * @dcsr_base: the base address of DCSR registers - * @val: Pointer to values for FSM registers. If NULL, - * will use the default value. - */ -void fsl_dp_fsm_setup(void __iomem *dcsr_base, struct fsm_reg_vals *val) -{ - struct fsm_reg_vals *data; - - /* if NULL, use the default values */ - if (val) - data = val; - else - data = fsm_default_val; - - /* clear all registers */ - fsl_dp_fsm_clean(dcsr_base, NULL); - - while (data->offset != FSM_END_FLAG) { - if (data->value) - out_be32(dcsr_base + data->offset, data->value); - data++; - } + iowrite32be(0, epu_base + offset); } diff --git a/drivers/platform/fsl/sleep_fsm.h b/drivers/platform/fsl/sleep_fsm.h new file mode 100644 index 0000000..8fea8ec --- /dev/null +++ b/drivers/platform/fsl/sleep_fsm.h @@ -0,0 +1,106 @@ +/* + * Freescale deep sleep FSM (finite-state machine) configuration + * + * Copyright 2014 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 _FSL_SLEEP_FSM_H +#define _FSL_SLEEP_FSM_H + +#define FSL_STRIDE_4B 4 +#define FSL_STRIDE_8B 8 + +/* End flag */ +#define FSM_END_FLAG 0xFFFFFFFFUL + +/* Block offsets */ +#define RCPM_BLOCK_OFFSET 0x00022000 +#define EPU_BLOCK_OFFSET 0x00000000 +#define NPC_BLOCK_OFFSET 0x00001000 + +/* EPGCR (Event Processor Global Control Register) */ +#define EPGCR 0x000 + +/* EPEVTCR0-9 (Event Processor EVT Pin Control Registers) */ +#define EPEVTCR0 0x050 +#define EPEVTCR9 0x074 +#define EPEVTCR_STRIDE FSL_STRIDE_4B + +/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */ +#define EPXTRIGCR 0x090 + +/* EPIMCR0-31 (Event Processor Input Mux Control Registers) */ +#define EPIMCR0 0x100 +#define EPIMCR31 0x17C +#define EPIMCR_STRIDE FSL_STRIDE_4B + +/* EPSMCR0-15 (Event Processor SCU Mux Control Registers) */ +#define EPSMCR0 0x200 +#define EPSMCR15 0x278 +#define EPSMCR_STRIDE FSL_STRIDE_8B + +/* EPECR0-15 (Event Processor Event Control Registers) */ +#define EPECR0 0x300 +#define EPECR15 0x33C +#define EPECR_STRIDE FSL_STRIDE_4B + +/* EPACR0-15 (Event Processor Action Control Registers) */ +#define EPACR0 0x400 +#define EPACR15 0x43C +#define EPACR_STRIDE FSL_STRIDE_4B + +/* EPCCRi0-15 (Event Processor Counter Control Registers) */ +#define EPCCR0 0x800 +#define EPCCR15 0x83C +#define EPCCR31 0x87C +#define EPCCR_STRIDE FSL_STRIDE_4B + +/* EPCMPR0-15 (Event Processor Counter Compare Registers) */ +#define EPCMPR0 0x900 +#define EPCMPR15 0x93C +#define EPCMPR31 0x97C +#define EPCMPR_STRIDE FSL_STRIDE_4B + +/* EPCTR0-31 (Event Processor Counter Register) */ +#define EPCTR0 0xA00 +#define EPCTR31 0xA7C +#define EPCTR_STRIDE FSL_STRIDE_4B + +/* NPC triggered Memory-Mapped Access Registers */ +#define NCR 0x000 +#define MCCR1 0x0CC +#define MCSR1 0x0D0 +#define MMAR1LO 0x0D4 +#define MMAR1HI 0x0D8 +#define MMDR1 0x0DC +#define MCSR2 0x0E0 +#define MMAR2LO 0x0E4 +#define MMAR2HI 0x0E8 +#define MMDR2 0x0EC +#define MCSR3 0x0F0 +#define MMAR3LO 0x0F4 +#define MMAR3HI 0x0F8 +#define MMDR3 0x0FC + +/* RCPM Core State Action Control Register 0 */ +#define CSTTACR0 0xB00 + +/* RCPM Core Group 1 Configuration Register 0 */ +#define CG1CR0 0x31C + +struct fsm_reg_vals { + u32 offset; + u32 value; +}; + +void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val); +void fsl_epu_setup_default(void __iomem *epu_base); +void fsl_npc_setup_default(void __iomem *npc_base); +void fsl_fsm_clean(void __iomem *base, struct fsm_reg_vals *val); +void fsl_epu_clean_default(void __iomem *epu_base); + +#endif /* _FSL_SLEEP_FSM_H */ diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 75840b5..9c64341 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -62,6 +62,17 @@ config PWM_BFIN To compile this driver as a module, choose M here: the module will be called pwm-bfin. +config PWM_FSL_FTM + tristate "Freescale FlexTimer Module (FTM) PWM support" + depends on OF + select REGMAP_MMIO + help + Generic FTM PWM framework driver for Freescale VF610 and + Layerscape LS-1 SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-fsl-ftm. + config PWM_IMX tristate "i.MX PWM support" depends on ARCH_MXC diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 77a8c18..f383784 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o +obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c new file mode 100644 index 0000000..666e1be --- /dev/null +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -0,0 +1,556 @@ +/* + * Freescale FlexTimer Module (FTM) PWM Driver + * + * 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 <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pwm.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define FTM_SC 0x00 +#define FTM_SC_CLK_MASK_SHIFT 3 +#define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT) +#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) +#define FTM_SC_PS_MASK 0x7 + +#define FTM_CNT 0x04 +#define FTM_MOD 0x08 + +#define FTM_CSC_BASE 0x0C +#define FTM_CSC_MSB BIT(5) +#define FTM_CSC_MSA BIT(4) +#define FTM_CSC_ELSB BIT(3) +#define FTM_CSC_ELSA BIT(2) +#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8)) + +#define FTM_CV_BASE 0x10 +#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8)) + +#define FTM_CNTIN 0x4C +#define FTM_STATUS 0x50 + +#define FTM_MODE 0x54 +#define FTM_MODE_FTMEN BIT(0) +#define FTM_MODE_INIT BIT(2) +#define FTM_MODE_PWMSYNC BIT(3) + +#define FTM_SYNC 0x58 +#define FTM_OUTINIT 0x5C +#define FTM_OUTMASK 0x60 +#define FTM_COMBINE 0x64 +#define FTM_DEADTIME 0x68 +#define FTM_EXTTRIG 0x6C +#define FTM_POL 0x70 +#define FTM_FMS 0x74 +#define FTM_FILTER 0x78 +#define FTM_FLTCTRL 0x7C +#define FTM_QDCTRL 0x80 +#define FTM_CONF 0x84 +#define FTM_FLTPOL 0x88 +#define FTM_SYNCONF 0x8C +#define FTM_INVCTRL 0x90 +#define FTM_SWOCTRL 0x94 +#define FTM_PWMLOAD 0x98 + +enum fsl_pwm_clk { + FSL_PWM_CLK_SYS, + FSL_PWM_CLK_FIX, + FSL_PWM_CLK_EXT, + FSL_PWM_CLK_CNTEN, + FSL_PWM_CLK_MAX +}; + +struct fsl_pwm_chip { + struct pwm_chip chip; + + struct mutex lock; + + unsigned int use_count; + unsigned int cnt_select; + unsigned int clk_ps; + + struct regmap *regmap; + + int period_ns; + + struct clk *clk[FSL_PWM_CLK_MAX]; +}; + +static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct fsl_pwm_chip, chip); +} + +static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + + return clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); +} + +static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); +} + +static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc, + enum fsl_pwm_clk index) +{ + unsigned long sys_rate, cnt_rate; + unsigned long long ratio; + + sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]); + if (!sys_rate) + return -EINVAL; + + cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]); + if (!cnt_rate) + return -EINVAL; + + switch (index) { + case FSL_PWM_CLK_SYS: + fpc->clk_ps = 1; + break; + case FSL_PWM_CLK_FIX: + ratio = 2 * cnt_rate - 1; + do_div(ratio, sys_rate); + fpc->clk_ps = ratio; + break; + case FSL_PWM_CLK_EXT: + ratio = 4 * cnt_rate - 1; + do_div(ratio, sys_rate); + fpc->clk_ps = ratio; + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc, + unsigned long period_ns) +{ + unsigned long long c, c0; + + c = clk_get_rate(fpc->clk[fpc->cnt_select]); + c = c * period_ns; + do_div(c, 1000000000UL); + + do { + c0 = c; + do_div(c0, (1 << fpc->clk_ps)); + if (c0 <= 0xFFFF) + return (unsigned long)c0; + } while (++fpc->clk_ps < 8); + + return 0; +} + +static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc, + unsigned long period_ns, + enum fsl_pwm_clk index) +{ + int ret; + + ret = fsl_pwm_calculate_default_ps(fpc, index); + if (ret) { + dev_err(fpc->chip.dev, + "failed to calculate default prescaler: %d\n", + ret); + return 0; + } + + return fsl_pwm_calculate_cycles(fpc, period_ns); +} + +static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc, + unsigned long period_ns) +{ + enum fsl_pwm_clk m0, m1; + unsigned long fix_rate, ext_rate, cycles; + + cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, + FSL_PWM_CLK_SYS); + if (cycles) { + fpc->cnt_select = FSL_PWM_CLK_SYS; + return cycles; + } + + fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]); + ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]); + + if (fix_rate > ext_rate) { + m0 = FSL_PWM_CLK_FIX; + m1 = FSL_PWM_CLK_EXT; + } else { + m0 = FSL_PWM_CLK_EXT; + m1 = FSL_PWM_CLK_FIX; + } + + cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0); + if (cycles) { + fpc->cnt_select = m0; + return cycles; + } + + fpc->cnt_select = m1; + + return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1); +} + +static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, + unsigned long period_ns, + unsigned long duty_ns) +{ + unsigned long long duty; + u32 val; + + regmap_read(fpc->regmap, FTM_MOD, &val); + duty = (unsigned long long)duty_ns * (val + 1); + do_div(duty, period_ns); + + return (unsigned long)duty; +} + +static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + u32 period, duty; + + mutex_lock(&fpc->lock); + + /* + * The Freescale FTM controller supports only a single period for + * all PWM channels, therefore incompatible changes need to be + * refused. + */ + if (fpc->period_ns && fpc->period_ns != period_ns) { + dev_err(fpc->chip.dev, + "conflicting period requested for PWM %u\n", + pwm->hwpwm); + mutex_unlock(&fpc->lock); + return -EBUSY; + } + + if (!fpc->period_ns && duty_ns) { + period = fsl_pwm_calculate_period(fpc, period_ns); + if (!period) { + dev_err(fpc->chip.dev, "failed to calculate period\n"); + mutex_unlock(&fpc->lock); + return -EINVAL; + } + + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, + fpc->clk_ps); + regmap_write(fpc->regmap, FTM_MOD, period - 1); + + fpc->period_ns = period_ns; + } + + mutex_unlock(&fpc->lock); + + duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); + + regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm), + FTM_CSC_MSB | FTM_CSC_ELSB); + regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty); + + return 0; +} + +static int fsl_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + u32 val; + + regmap_read(fpc->regmap, FTM_POL, &val); + + if (polarity == PWM_POLARITY_INVERSED) + val |= BIT(pwm->hwpwm); + else + val &= ~BIT(pwm->hwpwm); + + regmap_write(fpc->regmap, FTM_POL, val); + + return 0; +} + +static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) +{ + int ret; + + if (fpc->use_count++ != 0) + return 0; + + /* select counter clock source */ + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, + FTM_SC_CLK(fpc->cnt_select)); + + ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); + if (ret) + return ret; + + ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); + if (ret) { + clk_disable_unprepare(fpc->clk[fpc->cnt_select]); + return ret; + } + + return 0; +} + +static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + int ret; + + mutex_lock(&fpc->lock); + regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0); + + ret = fsl_counter_clock_enable(fpc); + mutex_unlock(&fpc->lock); + + return ret; +} + +static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) +{ + /* + * already disabled, do nothing + */ + if (fpc->use_count == 0) + return; + + /* there are still users, so can't disable yet */ + if (--fpc->use_count > 0) + return; + + /* no users left, disable PWM counter clock */ + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0); + + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); + clk_disable_unprepare(fpc->clk[fpc->cnt_select]); +} + +static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); + u32 val; + + mutex_lock(&fpc->lock); + regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), + BIT(pwm->hwpwm)); + + fsl_counter_clock_disable(fpc); + + regmap_read(fpc->regmap, FTM_OUTMASK, &val); + if ((val & 0xFF) == 0xFF) + fpc->period_ns = 0; + + mutex_unlock(&fpc->lock); +} + +static const struct pwm_ops fsl_pwm_ops = { + .request = fsl_pwm_request, + .free = fsl_pwm_free, + .config = fsl_pwm_config, + .set_polarity = fsl_pwm_set_polarity, + .enable = fsl_pwm_enable, + .disable = fsl_pwm_disable, + .owner = THIS_MODULE, +}; + +static int fsl_pwm_init(struct fsl_pwm_chip *fpc) +{ + int ret; + + ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); + if (ret) + return ret; + + regmap_write(fpc->regmap, FTM_CNTIN, 0x00); + regmap_write(fpc->regmap, FTM_OUTINIT, 0x00); + regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF); + + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); + + return 0; +} + +static bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FTM_CNT: + return true; + } + return false; +} + +static const struct regmap_config fsl_pwm_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = FTM_PWMLOAD, + .volatile_reg = fsl_pwm_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int fsl_pwm_probe(struct platform_device *pdev) +{ + struct fsl_pwm_chip *fpc; + struct resource *res; + void __iomem *base; + int ret; + + fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); + if (!fpc) + return -ENOMEM; + + mutex_init(&fpc->lock); + + fpc->chip.dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "ftm_sys", base, + &fsl_pwm_regmap_config); + if (IS_ERR(fpc->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(fpc->regmap); + } + + fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); + if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { + dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n"); + return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]); + } + + fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix"); + if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX])) + return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]); + + fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext"); + if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT])) + return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]); + + fpc->clk[FSL_PWM_CLK_CNTEN] = + devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en"); + if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN])) + return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]); + + fpc->chip.ops = &fsl_pwm_ops; + fpc->chip.of_xlate = of_pwm_xlate_with_flags; + fpc->chip.of_pwm_n_cells = 3; + fpc->chip.base = -1; + fpc->chip.npwm = 8; + fpc->chip.can_sleep = true; + + ret = pwmchip_add(&fpc->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, fpc); + + return fsl_pwm_init(fpc); +} + +static int fsl_pwm_remove(struct platform_device *pdev) +{ + struct fsl_pwm_chip *fpc = platform_get_drvdata(pdev); + + return pwmchip_remove(&fpc->chip); +} + +#ifdef CONFIG_PM_SLEEP +static int fsl_pwm_suspend(struct device *dev) +{ + struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); + u32 val; + + regcache_cache_only(fpc->regmap, true); + regcache_mark_dirty(fpc->regmap); + + /* read from cache */ + regmap_read(fpc->regmap, FTM_OUTMASK, &val); + if ((val & 0xFF) != 0xFF) { + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); + clk_disable_unprepare(fpc->clk[fpc->cnt_select]); + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); + } + + return 0; +} + +static int fsl_pwm_resume(struct device *dev) +{ + struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); + u32 val; + + /* read from cache */ + regmap_read(fpc->regmap, FTM_OUTMASK, &val); + if ((val & 0xFF) != 0xFF) { + clk_prepare_enable(fpc->clk[FSL_PWM_CLK_SYS]); + clk_prepare_enable(fpc->clk[fpc->cnt_select]); + clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); + } + + /* restore all registers from cache */ + regcache_cache_only(fpc->regmap, false); + regcache_sync(fpc->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops fsl_pwm_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fsl_pwm_suspend, fsl_pwm_resume) +}; + +static const struct of_device_id fsl_pwm_dt_ids[] = { + { .compatible = "fsl,vf610-ftm-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids); + +static struct platform_driver fsl_pwm_driver = { + .driver = { + .name = "fsl-ftm-pwm", + .of_match_table = fsl_pwm_dt_ids, + .pm = &fsl_pwm_pm_ops, + }, + .probe = fsl_pwm_probe, + .remove = fsl_pwm_remove, +}; +module_platform_driver(fsl_pwm_driver); + +MODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver"); +MODULE_AUTHOR("Xiubo Li <Li.Xiubo@freescale.com>"); +MODULE_ALIAS("platform:fsl-ftm-pwm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index dfd810f..adaf06c 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -57,6 +57,7 @@ struct ds3232 { * in the remove function. */ struct mutex mutex; + bool suspended; int exiting; }; @@ -345,7 +346,15 @@ static irqreturn_t ds3232_irq(int irq, void *dev_id) struct ds3232 *ds3232 = i2c_get_clientdata(client); disable_irq_nosync(irq); - schedule_work(&ds3232->work); + + /* + * If rtc as a wakeup source, can't schedule the work + * at system resume flow, because at this time the i2c bus + * has not been resumed. + */ + if (!ds3232->suspended) + schedule_work(&ds3232->work); + return IRQ_HANDLED; } @@ -363,22 +372,26 @@ static void ds3232_work(struct work_struct *work) if (stat & DS3232_REG_SR_A1F) { control = i2c_smbus_read_byte_data(client, DS3232_REG_CR); - if (control < 0) - goto out; - /* disable alarm1 interrupt */ - control &= ~(DS3232_REG_CR_A1IE); - i2c_smbus_write_byte_data(client, DS3232_REG_CR, control); - - /* clear the alarm pend flag */ - stat &= ~DS3232_REG_SR_A1F; - i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); - - rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + if (control < 0) { + pr_warn("Read DS3232 Control Register error." + "Disable IRQ%d.\n", client->irq); + } else { + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + i2c_smbus_write_byte_data(client, DS3232_REG_CR, + control); + + /* clear the alarm pend flag */ + stat &= ~DS3232_REG_SR_A1F; + i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat); + + rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + + if (!ds3232->exiting) + enable_irq(client->irq); + } } -out: - if (!ds3232->exiting) - enable_irq(client->irq); unlock: mutex_unlock(&ds3232->mutex); } @@ -411,23 +424,17 @@ static int ds3232_probe(struct i2c_client *client, if (ret) return ret; - ds3232->rtc = devm_rtc_device_register(&client->dev, client->name, - &ds3232_rtc_ops, THIS_MODULE); - if (IS_ERR(ds3232->rtc)) { - dev_err(&client->dev, "unable to register the class device\n"); - return PTR_ERR(ds3232->rtc); - } - if (client->irq > 0) { ret = devm_request_irq(&client->dev, client->irq, ds3232_irq, IRQF_SHARED, "ds3232", client); if (ret) { dev_err(&client->dev, "unable to request IRQ\n"); - return ret; } + device_init_wakeup(&client->dev, 1); } - - return 0; + ds3232->rtc = devm_rtc_device_register(&client->dev, client->name, + &ds3232_rtc_ops, THIS_MODULE); + return PTR_ERR_OR_ZERO(ds3232->rtc); } static int ds3232_remove(struct i2c_client *client) @@ -446,6 +453,42 @@ static int ds3232_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int ds3232_suspend(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(dev); + + if (device_can_wakeup(dev)) { + ds3232->suspended = true; + irq_set_irq_wake(client->irq, 1); + } + + return 0; +} + +static int ds3232_resume(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(dev); + + if (ds3232->suspended) { + ds3232->suspended = false; + + /* Clear the hardware alarm pend flag */ + schedule_work(&ds3232->work); + + irq_set_irq_wake(client->irq, 0); + } + + return 0; +} +#endif + +static const struct dev_pm_ops ds3232_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume) +}; + static const struct i2c_device_id ds3232_id[] = { { "ds3232", 0 }, { } @@ -456,6 +499,7 @@ static struct i2c_driver ds3232_driver = { .driver = { .name = "rtc-ds3232", .owner = THIS_MODULE, + .pm = &ds3232_pm_ops, }, .probe = ds3232_probe, .remove = ds3232_remove, diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig new file mode 100644 index 0000000..bb02cfa --- /dev/null +++ b/drivers/soc/Kconfig @@ -0,0 +1,18 @@ +menu "SOC (System On Chip) specific Drivers" + +config FSL_SOC_DRIVERS + bool "Freescale Soc Drivers" + depends on FSL_SOC || ARCH_MXC + default n + help + Say y here to enable Freescale Soc Device Drivers support. + The Soc Drivers provides the device driver that is a specific block + or feature on Freescale platform. + +if FSL_SOC_DRIVERS + source "drivers/soc/fsl/Kconfig" +endif + +source drivers/soc/qe/Kconfig + +endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile new file mode 100644 index 0000000..1212b75 --- /dev/null +++ b/drivers/soc/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Freescale SOC specific device drivers. +# + +obj-$(CONFIG_FSL_SOC_DRIVERS) += fsl/ + +obj-$(CONFIG_QUICC_ENGINE) += qe/ diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig new file mode 100644 index 0000000..72df9b3 --- /dev/null +++ b/drivers/soc/fsl/Kconfig @@ -0,0 +1,3 @@ +if ARM +source "drivers/soc/fsl/Kconfig.arm" +endif diff --git a/drivers/soc/fsl/Kconfig.arm b/drivers/soc/fsl/Kconfig.arm new file mode 100644 index 0000000..8c90b82 --- /dev/null +++ b/drivers/soc/fsl/Kconfig.arm @@ -0,0 +1,16 @@ +# +# Freescale ARM SOC Drivers +# + +config LS1_SOC_DRIVERS + bool "LS1021A Soc Drivers" + depends on SOC_LS1021A + default n + help + Say y here to enable Freescale LS1021A Soc Device Drivers support. + The Soc Drivers provides the device driver that is a specific block + or feature on LS1021A platform. + +if LS1_SOC_DRIVERS + source "drivers/soc/fsl/ls1/Kconfig" +endif diff --git a/drivers/soc/fsl/Makefile b/drivers/soc/fsl/Makefile new file mode 100644 index 0000000..5ca0e1c --- /dev/null +++ b/drivers/soc/fsl/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for ls1 Soc specific device drivers. +# + +obj-$(CONFIG_LS1_SOC_DRIVERS) += ls1/ diff --git a/drivers/soc/fsl/ls1/Kconfig b/drivers/soc/fsl/ls1/Kconfig new file mode 100644 index 0000000..c9b04c4 --- /dev/null +++ b/drivers/soc/fsl/ls1/Kconfig @@ -0,0 +1,11 @@ +# +# LS-1 Soc drivers +# +config FTM_ALARM + bool "FTM alarm driver" + depends on SOC_LS1021A + default n + help + Say y here to enable FTM alarm support. The FTM alarm provides + alarm functions for wakeup system from deep sleep. There is only + one FTM can be used in ALARM(FTM 0). diff --git a/drivers/soc/fsl/ls1/Makefile b/drivers/soc/fsl/ls1/Makefile new file mode 100644 index 0000000..6299aa1 --- /dev/null +++ b/drivers/soc/fsl/ls1/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FTM_ALARM) += ftm_alarm.o diff --git a/drivers/soc/fsl/ls1/ftm_alarm.c b/drivers/soc/fsl/ls1/ftm_alarm.c new file mode 100644 index 0000000..5135964 --- /dev/null +++ b/drivers/soc/fsl/ls1/ftm_alarm.c @@ -0,0 +1,271 @@ +/* + * Freescale FlexTimer Module (FTM) Alarm driver. + * + * Copyright 2014 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 <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> + +#define FTM_SC 0x00 +#define FTM_SC_CLK_SHIFT 3 +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT) +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT) +#define FTM_SC_PS_MASK 0x7 +#define FTM_SC_TOIE BIT(6) +#define FTM_SC_TOF BIT(7) + +#define FTM_SC_CLKS_FIXED_FREQ 0x02 + +#define FTM_CNT 0x04 +#define FTM_MOD 0x08 +#define FTM_CNTIN 0x4C + +#define FIXED_FREQ_CLK 32000 +#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK) +#define MAX_COUNT_VAL 0xffff + +static void __iomem *ftm1_base; +static u32 alarm_freq; +static bool big_endian; + +static inline u32 ftm_readl(void __iomem *addr) +{ + if (big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static inline void ftm_writel(u32 val, void __iomem *addr) +{ + if (big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static inline void ftm_counter_enable(void __iomem *base) +{ + u32 val; + + /* select and enable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ)); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_counter_disable(void __iomem *base) +{ + u32 val; + + /* disable counter clock source */ + val = ftm_readl(base + FTM_SC); + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_acknowledge(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOF; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_enable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val |= FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_irq_disable(void __iomem *base) +{ + u32 val; + + val = ftm_readl(base + FTM_SC); + val &= ~FTM_SC_TOIE; + ftm_writel(val, base + FTM_SC); +} + +static inline void ftm_reset_counter(void __iomem *base) +{ + /* + * The CNT register contains the FTM counter value. + * Reset clears the CNT register. Writing any value to COUNT + * updates the counter with its initial value, CNTIN. + */ + ftm_writel(0x00, base + FTM_CNT); +} + +static u32 time_to_cycle(unsigned long time) +{ + u32 cycle; + + cycle = time * alarm_freq; + if (cycle > MAX_COUNT_VAL) { + pr_err("Out of alarm range.\n"); + cycle = 0; + } + + return cycle; +} + +static u32 cycle_to_time(u32 cycle) +{ + return cycle / alarm_freq + 1; +} + +static void ftm_clean_alarm(void) +{ + ftm_counter_disable(ftm1_base); + + ftm_writel(0x00, ftm1_base + FTM_CNTIN); + ftm_writel(~0UL, ftm1_base + FTM_MOD); + + ftm_reset_counter(ftm1_base); +} + +static int ftm_set_alarm(u64 cycle) +{ + ftm_irq_disable(ftm1_base); + + /* + * The counter increments until the value of MOD is reached, + * at which point the counter is reloaded with the value of CNTIN. + * The TOF (the overflow flag) bit is set when the FTM counter + * changes from MOD to CNTIN. So we should using the cycle - 1. + */ + ftm_writel(cycle - 1, ftm1_base + FTM_MOD); + + ftm_counter_enable(ftm1_base); + + ftm_irq_enable(ftm1_base); + + return 0; +} + +static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id) +{ + ftm_irq_acknowledge(ftm1_base); + ftm_irq_disable(ftm1_base); + ftm_clean_alarm(); + + return IRQ_HANDLED; +} + +static ssize_t ftm_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 count, val; + count = ftm_readl(ftm1_base + FTM_MOD); + val = ftm_readl(ftm1_base + FTM_CNT); + val = (count & MAX_COUNT_VAL) - val; + val = cycle_to_time(val); + + return sprintf(buf, "%u\n", val); +} + +static ssize_t ftm_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + u32 cycle; + unsigned long time; + + if (kstrtoul(buf, 0, &time)) + return -EINVAL; + + ftm_clean_alarm(); + + cycle = time_to_cycle(time); + if (!cycle) + return -EINVAL; + + ftm_set_alarm(cycle); + + return count; +} + +static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644, + ftm_alarm_show, ftm_alarm_store); + +static int ftm_alarm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *r; + int irq; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + ftm1_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(ftm1_base)) + return PTR_ERR(ftm1_base); + + irq = irq_of_parse_and_map(np, 0); + if (irq <= 0) { + pr_err("ftm: unable to get IRQ from DT, %d\n", irq); + return -EINVAL; + } + + big_endian = of_property_read_bool(np, "big-endian"); + + ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt, + IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq\n"); + return ret; + } + + ret = device_create_file(&pdev->dev, &ftm_alarm_attributes); + if (ret) { + dev_err(&pdev->dev, "create sysfs fail.\n"); + return ret; + } + + alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV; + + ftm_clean_alarm(); + + return ret; +} + +static const struct of_device_id ftm_alarm_match[] = { + { .compatible = "fsl,ftm-alarm", }, + { }, +}; + +static struct platform_driver ftm_alarm_driver = { + .probe = ftm_alarm_probe, + .driver = { + .name = "ftm-alarm", + .owner = THIS_MODULE, + .of_match_table = ftm_alarm_match, + }, +}; + +static int __init ftm_alarm_init(void) +{ + return platform_driver_register(&ftm_alarm_driver); +} +device_initcall(ftm_alarm_init); diff --git a/drivers/soc/qe/Kconfig b/drivers/soc/qe/Kconfig new file mode 100644 index 0000000..43b984b --- /dev/null +++ b/drivers/soc/qe/Kconfig @@ -0,0 +1,46 @@ +# +# QE Communication options +# + +config QUICC_ENGINE + bool "Freescale QUICC Engine (QE) Support" + select LIB_RHEAP + select CRC32 + ---help--- + The QUICC Engine (QE) is a new generation of communications + coprocessors on Freescale embedded CPUs (akin to CPM in older chips). + Selecting this option means that you wish to build a kernel + for a machine with a QE coprocessor. + +config QE_GPIO + bool "QE GPIO support" + depends on QUICC_ENGINE + select ARCH_REQUIRE_GPIOLIB + ---help--- + Say Y here if you're going to use hardware that connects to the + QE GPIOs. + + +config UCC_SLOW + bool + default y if SERIAL_QE + help + This option provides qe_lib support to UCC slow + protocols: UART, BISYNC, QMC + +config UCC_FAST + bool + default y if UCC_GETH || FSL_UCC_TDM || FSL_UCC_HDLC + help + This option provides qe_lib support to UCC fast + protocols: HDLC, Ethernet, ATM, transparent + +config UCC + bool + default y if UCC_FAST || UCC_SLOW + +config QE_USB + bool + default y if USB_FSL_QE + help + QE USB Controller support diff --git a/arch/powerpc/sysdev/qe_lib/Makefile b/drivers/soc/qe/Makefile index f1855c1..77f6fd9 100644 --- a/arch/powerpc/sysdev/qe_lib/Makefile +++ b/drivers/soc/qe/Makefile @@ -1,7 +1,7 @@ # # Makefile for the linux ppc-specific parts of QE # -obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o +obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_ic.o qe_io.o qe_common.o obj-$(CONFIG_UCC) += ucc.o obj-$(CONFIG_UCC_SLOW) += ucc_slow.o diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/drivers/soc/qe/gpio.c index 521e67a..fb891d4 100644 --- a/arch/powerpc/sysdev/qe_lib/gpio.c +++ b/drivers/soc/qe/gpio.c @@ -21,7 +21,7 @@ #include <linux/gpio.h> #include <linux/slab.h> #include <linux/export.h> -#include <asm/qe.h> +#include <linux/fsl/qe.h> struct qe_gpio_chip { struct of_mm_gpio_chip mm_gc; @@ -158,7 +158,8 @@ struct qe_pin *qe_pin_request(struct device_node *np, int index) if (WARN_ON(!gc)) goto err0; - if (!of_device_is_compatible(gc->of_node, "fsl,mpc8323-qe-pario-bank")) { + if (!of_device_is_compatible(gc->of_node, + "fsl,mpc8323-qe-pario-bank")) { pr_debug("%s: tried to get a non-qe pin\n", __func__); err = -EINVAL; goto err0; diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/drivers/soc/qe/qe.c index ad324ba..bfea0f8 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/drivers/soc/qe/qe.c @@ -2,8 +2,8 @@ * Copyright (C) 2006-2010, 2012 Freescale Semiconductor, Inc. * All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * Based on cpm2_common.c from Dan Malek (dmalek@jlc.net) * * Description: @@ -33,10 +33,10 @@ #include <asm/irq.h> #include <asm/page.h> #include <asm/pgtable.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> #include <asm/prom.h> -#include <asm/rheap.h> +#include <linux/fsl/rheap.h> static void qe_snums_init(void); static int qe_sdma_init(void); @@ -65,7 +65,8 @@ struct qe_snum { struct qe_immap __iomem *qe_immr; EXPORT_SYMBOL(qe_immr); -static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */ +/* Dynamically allocated SNUMs */ +static struct qe_snum snums[QE_NUM_OF_SNUM]; static unsigned int qe_num_of_snum; static phys_addr_t qebase = -1; @@ -73,8 +74,8 @@ static phys_addr_t qebase = -1; phys_addr_t get_qe_base(void) { struct device_node *qe; - int size; - const u32 *prop; + int ret; + struct resource res; if (qebase != -1) return qebase; @@ -86,14 +87,13 @@ phys_addr_t get_qe_base(void) return qebase; } - prop = of_get_property(qe, "reg", &size); - if (prop && size >= sizeof(*prop)) - qebase = of_translate_address(qe, prop); + ret = of_address_to_resource(qe, 0, &res); + if (!ret) + qebase = res.start; of_node_put(qe); return qebase; } - EXPORT_SYMBOL(get_qe_base); void qe_reset(void) @@ -121,7 +121,7 @@ int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input) spin_lock_irqsave(&qe_lock, flags); if (cmd == QE_RESET) { - out_be32(&qe_immr->cp.cecr, (u32) (cmd | QE_CR_FLG)); + iowrite32be((u32) (cmd | QE_CR_FLG), &qe_immr->cp.cecr); } else { if (cmd == QE_ASSIGN_PAGE) { /* Here device is the SNUM, not sub-block */ @@ -138,15 +138,14 @@ int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input) mcn_shift = QE_CR_MCN_NORMAL_SHIFT; } - out_be32(&qe_immr->cp.cecdr, cmd_input); - out_be32(&qe_immr->cp.cecr, - (cmd | QE_CR_FLG | ((u32) device << dev_shift) | (u32) - mcn_protocol << mcn_shift)); + iowrite32be(cmd_input, &qe_immr->cp.cecdr); + iowrite32be((cmd | QE_CR_FLG | ((u32) device << dev_shift) | + (u32)mcn_protocol << mcn_shift), &qe_immr->cp.cecr); } /* wait for the QE_CR_FLG to clear */ - ret = spin_event_timeout((in_be32(&qe_immr->cp.cecr) & QE_CR_FLG) == 0, - 100, 0); + ret = spin_event_timeout((ioread32be(&qe_immr->cp.cecr) + & QE_CR_FLG) == 0, 100, 0); /* On timeout (e.g. failure), the expression will be false (ret == 0), otherwise it will be true (ret == 1). */ spin_unlock_irqrestore(&qe_lock, flags); @@ -165,13 +164,13 @@ EXPORT_SYMBOL(qe_issue_cmd); * Baud rate clocks are zero-based in the driver code (as that maps * to port numbers). Documentation uses 1-based numbering. */ -static unsigned int brg_clk = 0; +static unsigned int brg_clk; unsigned int qe_get_brg_clk(void) { struct device_node *qe; - int size; - const u32 *prop; + u32 val; + int ret; if (brg_clk) return brg_clk; @@ -183,9 +182,9 @@ unsigned int qe_get_brg_clk(void) return brg_clk; } - prop = of_get_property(qe, "brg-frequency", &size); - if (prop && size == sizeof(*prop)) - brg_clk = *prop; + ret = of_property_read_u32_index(qe, "brg-frequency", 0, &val); + if (!ret) + brg_clk = val; of_node_put(qe); @@ -225,7 +224,7 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier) tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE | div16; - out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval); + iowrite32be(tempval, &qe_immr->brg.brgc[brg - QE_BRG1]); return 0; } @@ -238,7 +237,8 @@ EXPORT_SYMBOL(qe_setbrg); */ enum qe_clock qe_clock_source(const char *source) { - unsigned int i; + unsigned int ret; + unsigned long val; if (strcasecmp(source, "none") == 0) return QE_CLK_NONE; @@ -250,17 +250,22 @@ enum qe_clock qe_clock_source(const char *source) return QE_RSYNC_PIN; if (strncasecmp(source, "brg", 3) == 0) { - i = simple_strtoul(source + 3, NULL, 10); - if ((i >= 1) && (i <= 16)) - return (QE_BRG1 - 1) + i; + ret = kstrtoul(source + 3, 10, &val); + if (ret) + return ret; + + if ((val >= 1) && (val <= 16)) + return (QE_BRG1 - 1) + val; else return QE_CLK_DUMMY; } if (strncasecmp(source, "clk", 3) == 0) { - i = simple_strtoul(source + 3, NULL, 10); - if ((i >= 1) && (i <= 24)) - return (QE_CLK1 - 1) + i; + ret = kstrtoul(source + 3, 10, &val); + if (ret) + return ret; + if ((val >= 1) && (val <= 24)) + return (QE_CLK1 - 1) + val; else return QE_CLK_DUMMY; } @@ -359,9 +364,9 @@ static int qe_sdma_init(void) return -ENOMEM; } - out_be32(&sdma->sdebcr, (u32) sdma_buf_offset & QE_SDEBCR_BA_MASK); - out_be32(&sdma->sdmr, (QE_SDMR_GLB_1_MSK | - (0x1 << QE_SDMR_CEN_SHIFT))); + iowrite32be((u32) sdma_buf_offset & QE_SDEBCR_BA_MASK, &sdma->sdebcr); + iowrite32be((QE_SDMR_GLB_1_MSK | (0x1 << QE_SDMR_CEN_SHIFT)), + &sdma->sdmr); return 0; } @@ -391,22 +396,20 @@ static void qe_upload_microcode(const void *base, unsigned int i; if (ucode->major || ucode->minor || ucode->revision) - printk(KERN_INFO "qe-firmware: " - "uploading microcode '%s' version %u.%u.%u\n", + pr_info("qe-FM: uploading microcode '%s' version %u.%u.%u\n", ucode->id, ucode->major, ucode->minor, ucode->revision); else - printk(KERN_INFO "qe-firmware: " - "uploading microcode '%s'\n", ucode->id); + pr_info("qe-FM: uploading microcode '%s'\n", ucode->id); /* Use auto-increment */ - out_be32(&qe_immr->iram.iadd, be32_to_cpu(ucode->iram_offset) | - QE_IRAM_IADD_AIE | QE_IRAM_IADD_BADDR); + iowrite32be(be32_to_cpu(ucode->iram_offset) | QE_IRAM_IADD_AIE | + QE_IRAM_IADD_BADDR, &qe_immr->iram.iadd); for (i = 0; i < be32_to_cpu(ucode->count); i++) - out_be32(&qe_immr->iram.idata, be32_to_cpu(code[i])); - + iowrite32be(be32_to_cpu(code[i]), &qe_immr->iram.idata); + /* Set I-RAM Ready Register */ - out_be32(&qe_immr->iram.iready, be32_to_cpu(QE_IRAM_READY)); + iowrite32be(be32_to_cpu(QE_IRAM_READY), &qe_immr->iram.iready); } /* @@ -436,7 +439,7 @@ int qe_upload_firmware(const struct qe_firmware *firmware) const struct qe_header *hdr; if (!firmware) { - printk(KERN_ERR "qe-firmware: invalid pointer\n"); + pr_info("qe-firmware: invalid pointer\n"); return -EINVAL; } @@ -446,19 +449,19 @@ int qe_upload_firmware(const struct qe_firmware *firmware) /* Check the magic */ if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || (hdr->magic[2] != 'F')) { - printk(KERN_ERR "qe-firmware: not a microcode\n"); + pr_info("qe-firmware: not a microcode\n"); return -EPERM; } /* Check the version */ if (hdr->version != 1) { - printk(KERN_ERR "qe-firmware: unsupported version\n"); + pr_info("qe-firmware: unsupported version\n"); return -EPERM; } /* Validate some of the fields */ if ((firmware->count < 1) || (firmware->count > MAX_QE_RISC)) { - printk(KERN_ERR "qe-firmware: invalid data\n"); + pr_info("qe-firmware: invalid data\n"); return -EINVAL; } @@ -476,14 +479,14 @@ int qe_upload_firmware(const struct qe_firmware *firmware) /* Validate the length */ if (length != calc_size + sizeof(__be32)) { - printk(KERN_ERR "qe-firmware: invalid length\n"); + pr_info("qe-firmware: invalid length\n"); return -EPERM; } /* Validate the CRC */ crc = be32_to_cpu(*(__be32 *)((void *)firmware + calc_size)); if (crc != crc32(0, firmware, calc_size)) { - printk(KERN_ERR "qe-firmware: firmware CRC is invalid\n"); + pr_info("qe-firmware: firmware CRC is invalid\n"); return -EIO; } @@ -494,12 +497,11 @@ int qe_upload_firmware(const struct qe_firmware *firmware) setbits16(&qe_immr->cp.cercr, QE_CP_CERCR_CIR); if (firmware->soc.model) - printk(KERN_INFO - "qe-firmware: firmware '%s' for %u V%u.%u\n", + pr_info("qe-firmware: firmware '%s' for %u V%u.%u\n", firmware->id, be16_to_cpu(firmware->soc.model), firmware->soc.major, firmware->soc.minor); else - printk(KERN_INFO "qe-firmware: firmware '%s'\n", + pr_info("qe-firmware: firmware '%s'\n", firmware->id); /* @@ -525,11 +527,11 @@ int qe_upload_firmware(const struct qe_firmware *firmware) u32 trap = be32_to_cpu(ucode->traps[j]); if (trap) - out_be32(&qe_immr->rsp[i].tibcr[j], trap); + iowrite32be(trap, &qe_immr->rsp[i].tibcr[j]); } /* Enable traps */ - out_be32(&qe_immr->rsp[i].eccr, be32_to_cpu(ucode->eccr)); + iowrite32be(be32_to_cpu(ucode->eccr), &qe_immr->rsp[i].eccr); } qe_firmware_uploaded = 1; @@ -648,9 +650,9 @@ EXPORT_SYMBOL(qe_get_num_of_risc); unsigned int qe_get_num_of_snums(void) { struct device_node *qe; - int size; unsigned int num_of_snums; - const u32 *prop; + u32 val; + int ret; num_of_snums = 28; /* The default number of snum for threads is 28 */ qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); @@ -664,9 +666,9 @@ unsigned int qe_get_num_of_snums(void) return num_of_snums; } - prop = of_get_property(qe, "fsl,qe-num-snums", &size); - if (prop && size == sizeof(*prop)) { - num_of_snums = *prop; + ret = of_property_read_u32_index(qe, "fsl,qe-num-snums", 0, &val); + if (!ret) { + num_of_snums = val; if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) { /* No QE ever has fewer than 28 SNUMs */ pr_err("QE: number of snum is invalid\n"); @@ -681,6 +683,21 @@ unsigned int qe_get_num_of_snums(void) } EXPORT_SYMBOL(qe_get_num_of_snums); +static int __init qe_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!np) { + pr_err("%s: Could not find Quicc Engine node\n", __func__); + return -ENODEV; + } + qe_reset(); + of_node_put(np); + return 0; +} +subsys_initcall(qe_init); + #if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC_85xx) static int qe_resume(struct platform_device *ofdev) { diff --git a/drivers/soc/qe/qe_common.c b/drivers/soc/qe/qe_common.c new file mode 100644 index 0000000..6bc9b18 --- /dev/null +++ b/drivers/soc/qe/qe_common.c @@ -0,0 +1,185 @@ +/* + * Common QE code + * + * Author: Scott Wood <scottwood@freescale.com> + * + * Copyright 2007-2008,2010 Freescale Semiconductor, Inc. + * + * Some parts derived from commproc.c/cpm2_common.c, which is: + * Copyright (c) 1997 Dan error_act (dmalek@jlc.net) + * Copyright (c) 1999-2001 Dan Malek <dan@embeddedalley.com> + * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) + * 2006 (c) MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/of_device.h> +#include <linux/spinlock.h> +#include <linux/export.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +#include <linux/io.h> +#include <linux/fsl/rheap.h> +#include <linux/fsl/qe.h> + +static spinlock_t qe_muram_lock; +static struct _rh_block qe_boot_muram_rh_block[16]; +static struct _rh_info qe_muram_info; +static u8 __iomem *muram_vbase; +static phys_addr_t muram_pbase; + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 + +int qe_muram_init(void) +{ + struct device_node *np; + struct resource r; + u32 zero[OF_MAX_ADDR_CELLS] = {}; + resource_size_t max = 0; + int i = 0; + int ret = 0; + + if (muram_pbase) + return 0; + + spin_lock_init(&qe_muram_lock); + /* initialize the info header */ + rh_init(&qe_muram_info, 1, + sizeof(qe_boot_muram_rh_block) / + sizeof(qe_boot_muram_rh_block[0]), + qe_boot_muram_rh_block); + + np = of_find_compatible_node(NULL, NULL, "fsl,qe-muram-data"); + if (!np) { + /* try legacy bindings */ + np = of_find_node_by_name(NULL, "data-only"); + if (!np) { + pr_err("Cannot find CPM muram data node"); + ret = -ENODEV; + goto out; + } + } + + muram_pbase = (phys_addr_t)of_translate_address(np, zero); + if (muram_pbase == (phys_addr_t)OF_BAD_ADDR) { + pr_err("Cannot translate zero through CPM muram node"); + ret = -ENODEV; + goto out; + } + + while (of_address_to_resource(np, i++, &r) == 0) { + if (r.end > max) + max = r.end; + + rh_attach_region(&qe_muram_info, r.start - muram_pbase, + resource_size(&r)); + } + + muram_vbase = ioremap(muram_pbase, max - muram_pbase + 1); + if (!muram_vbase) { + pr_err("Cannot map CPM muram"); + ret = -ENOMEM; + } + +out: + of_node_put(np); + return ret; +} + +/** + * qe_muram_alloc - allocate the requested size worth of multi-user ram + * @size: number of bytes to allocate + * @align: requested alignment, in bytes + * + * This function returns an offset into the muram area. + * Use qe_dpram_addr() to get the virtual address of the area. + * Use qe_muram_free() to free the allocation. + */ +unsigned long qe_muram_alloc(unsigned long size, unsigned long align) +{ + unsigned long start; + unsigned long flags; + + spin_lock_irqsave(&qe_muram_lock, flags); + qe_muram_info.alignment = align; + start = rh_alloc(&qe_muram_info, size, "commproc"); + memset(qe_muram_addr(start), 0, size); + spin_unlock_irqrestore(&qe_muram_lock, flags); + + return start; +} +EXPORT_SYMBOL(qe_muram_alloc); + +/** + * qe_muram_free - free a chunk of multi-user ram + * @offset: The beginning of the chunk as returned by qe_muram_alloc(). + */ +int qe_muram_free(unsigned long offset) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&qe_muram_lock, flags); + ret = rh_free(&qe_muram_info, offset); + spin_unlock_irqrestore(&qe_muram_lock, flags); + + return ret; +} +EXPORT_SYMBOL(qe_muram_free); + +/** + * qe_muram_alloc_fixed - reserve a specific region of multi-user ram + * @offset: the offset into the muram area to reserve + * @size: the number of bytes to reserve + * + * This function returns "start" on success, -ENOMEM on failure. + * Use qe_dpram_addr() to get the virtual address of the area. + * Use qe_muram_free() to free the allocation. + */ +unsigned long qe_muram_alloc_fixed(unsigned long offset, unsigned long size) +{ + unsigned long start; + unsigned long flags; + + spin_lock_irqsave(&qe_muram_lock, flags); + qe_muram_info.alignment = 1; + start = rh_alloc_fixed(&qe_muram_info, offset, size, "commproc"); + spin_unlock_irqrestore(&qe_muram_lock, flags); + + return start; +} +EXPORT_SYMBOL(qe_muram_alloc_fixed); + +/** + * qe_muram_addr - turn a muram offset into a virtual address + * @offset: muram offset to convert + */ +void __iomem *qe_muram_addr(unsigned long offset) +{ + return muram_vbase + offset; +} +EXPORT_SYMBOL(qe_muram_addr); + +unsigned long qe_muram_offset(void __iomem *addr) +{ + return addr - (void __iomem *)muram_vbase; +} +EXPORT_SYMBOL(qe_muram_offset); + +/** + * qe_muram_dma - turn a muram virtual address into a DMA address + * @offset: virtual address from qe_muram_addr() to convert + */ +dma_addr_t qe_muram_dma(void __iomem *addr) +{ + return muram_pbase + ((u8 __iomem *)addr - muram_vbase); +} +EXPORT_SYMBOL(qe_muram_dma); diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/drivers/soc/qe/qe_ic.c index b2b87c3..11fe98c 100644 --- a/arch/powerpc/sysdev/qe_lib/qe_ic.c +++ b/drivers/soc/qe/qe_ic.c @@ -16,7 +16,10 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <linux/irqdomain.h> #include <linux/errno.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/reboot.h> #include <linux/slab.h> #include <linux/stddef.h> @@ -26,11 +29,12 @@ #include <linux/bootmem.h> #include <linux/spinlock.h> #include <asm/irq.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/prom.h> -#include <asm/qe_ic.h> +#include <linux/fsl/qe_ic.h> #include "qe_ic.h" +#include "../../irqchip/irqchip.h" static DEFINE_RAW_SPINLOCK(qe_ic_lock); @@ -175,15 +179,15 @@ static struct qe_ic_info qe_ic_info[] = { }, }; -static inline u32 qe_ic_read(volatile __be32 __iomem * base, unsigned int reg) +static inline u32 qe_ic_read(__be32 __iomem *base, unsigned int reg) { - return in_be32(base + (reg >> 2)); + return ioread32be(base + (reg >> 2)); } -static inline void qe_ic_write(volatile __be32 __iomem * base, unsigned int reg, +static inline void qe_ic_write(__be32 __iomem *base, unsigned int reg, u32 value) { - out_be32(base + (reg >> 2), value); + iowrite32be(value, base + (reg >> 2)); } static inline struct qe_ic *qe_ic_from_irq(unsigned int virq) @@ -258,7 +262,7 @@ static int qe_ic_host_map(struct irq_domain *h, unsigned int virq, struct irq_chip *chip; if (qe_ic_info[hw].mask == 0) { - printk(KERN_ERR "Can't map reserved IRQ\n"); + pr_err("Can't map reserved IRQ\n"); return -EINVAL; } /* Default chip */ @@ -341,7 +345,7 @@ void __init qe_ic_init(struct device_node *node, unsigned int flags, qe_ic->virq_low = irq_of_parse_and_map(node, 1); if (qe_ic->virq_low == NO_IRQ) { - printk(KERN_ERR "Failed to map QE_IC low IRQ\n"); + pr_err("Failed to map QE_IC low IRQ\n"); kfree(qe_ic); return; } @@ -483,19 +487,33 @@ static int __init init_qe_ic_sysfs(void) { int rc; - printk(KERN_DEBUG "Registering qe_ic with sysfs...\n"); + pr_debug("Registering qe_ic with sysfs...\n"); rc = subsys_system_register(&qe_ic_subsys, NULL); if (rc) { - printk(KERN_ERR "Failed registering qe_ic sys class\n"); + pr_err("Failed registering qe_ic sys class\n"); return -ENODEV; } rc = device_register(&device_qe_ic); if (rc) { - printk(KERN_ERR "Failed registering qe_ic sys device\n"); + pr_err("Failed registering qe_ic sys device\n"); return -ENODEV; } return 0; } +static int __init qeic_of_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic"); + if (np) { + qe_ic_init(np, 0, qe_ic_cascade_low_mpic, + qe_ic_cascade_high_mpic); + of_node_put(np); + } + return 0; +} +subsys_initcall(qeic_of_init); + subsys_initcall(init_qe_ic_sysfs); diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.h b/drivers/soc/qe/qe_ic.h index efef7ab..aab8abd 100644 --- a/arch/powerpc/sysdev/qe_lib/qe_ic.h +++ b/drivers/soc/qe/qe_ic.h @@ -16,7 +16,7 @@ #ifndef _POWERPC_SYSDEV_QE_IC_H #define _POWERPC_SYSDEV_QE_IC_H -#include <asm/qe_ic.h> +#include <linux/fsl/qe_ic.h> #define NR_QE_IC_INTS 64 @@ -76,7 +76,7 @@ struct qe_ic { /* Control registers offset */ - volatile u32 __iomem *regs; + u32 __iomem *regs; /* The remapper for this QEIC */ struct irq_domain *irqhost; diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/drivers/soc/qe/qe_io.c index a88807b..7f40d3c 100644 --- a/arch/powerpc/sysdev/qe_lib/qe_io.c +++ b/drivers/soc/qe/qe_io.c @@ -21,15 +21,14 @@ #include <linux/module.h> #include <linux/ioport.h> -#include <asm/io.h> -#include <asm/qe.h> +#include <linux/io.h> +#include <linux/fsl/qe.h> #include <asm/prom.h> -#include <sysdev/fsl_soc.h> #undef DEBUG static struct qe_pio_regs __iomem *par_io; -static int num_par_io_ports = 0; +static int num_par_io_ports; int par_io_init(struct device_node *np) { @@ -62,16 +61,16 @@ void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir, pin_mask1bit = (u32) (1 << (QE_PIO_PINS - (pin + 1))); /* Set open drain, if required */ - tmp_val = in_be32(&par_io->cpodr); + tmp_val = ioread32be(&par_io->cpodr); if (open_drain) - out_be32(&par_io->cpodr, pin_mask1bit | tmp_val); + iowrite32be(pin_mask1bit | tmp_val, &par_io->cpodr); else - out_be32(&par_io->cpodr, ~pin_mask1bit & tmp_val); + iowrite32be(~pin_mask1bit & tmp_val, &par_io->cpodr); /* define direction */ tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ? - in_be32(&par_io->cpdir2) : - in_be32(&par_io->cpdir1); + ioread32be(&par_io->cpdir2) : + ioread32be(&par_io->cpdir1); /* get all bits mask for 2 bit per port */ pin_mask2bits = (u32) (0x3 << (QE_PIO_PINS - @@ -83,34 +82,30 @@ void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir, /* clear and set 2 bits mask */ if (pin > (QE_PIO_PINS / 2) - 1) { - out_be32(&par_io->cpdir2, - ~pin_mask2bits & tmp_val); + iowrite32be(~pin_mask2bits & tmp_val, &par_io->cpdir2); tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cpdir2, new_mask2bits | tmp_val); + iowrite32be(new_mask2bits | tmp_val, &par_io->cpdir2); } else { - out_be32(&par_io->cpdir1, - ~pin_mask2bits & tmp_val); + iowrite32be(~pin_mask2bits & tmp_val, &par_io->cpdir1); tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cpdir1, new_mask2bits | tmp_val); + iowrite32be(new_mask2bits | tmp_val, &par_io->cpdir1); } /* define pin assignment */ tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ? - in_be32(&par_io->cppar2) : - in_be32(&par_io->cppar1); + ioread32be(&par_io->cppar2) : + ioread32be(&par_io->cppar1); new_mask2bits = (u32) (assignment << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2)); /* clear and set 2 bits mask */ if (pin > (QE_PIO_PINS / 2) - 1) { - out_be32(&par_io->cppar2, - ~pin_mask2bits & tmp_val); + iowrite32be(~pin_mask2bits & tmp_val, &par_io->cppar2); tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cppar2, new_mask2bits | tmp_val); + iowrite32be(new_mask2bits | tmp_val, &par_io->cppar2); } else { - out_be32(&par_io->cppar1, - ~pin_mask2bits & tmp_val); + iowrite32be(~pin_mask2bits & tmp_val, &par_io->cppar1); tmp_val &= ~pin_mask2bits; - out_be32(&par_io->cppar1, new_mask2bits | tmp_val); + iowrite32be(new_mask2bits | tmp_val, &par_io->cppar1); } } EXPORT_SYMBOL(__par_io_config_pin); @@ -138,12 +133,12 @@ int par_io_data_set(u8 port, u8 pin, u8 val) /* calculate pin location */ pin_mask = (u32) (1 << (QE_PIO_PINS - 1 - pin)); - tmp_val = in_be32(&par_io[port].cpdata); + tmp_val = ioread32be(&par_io[port].cpdata); if (val == 0) /* clear */ - out_be32(&par_io[port].cpdata, ~pin_mask & tmp_val); + iowrite32be(~pin_mask & tmp_val, &par_io[port].cpdata); else /* set */ - out_be32(&par_io[port].cpdata, pin_mask | tmp_val); + iowrite32be(pin_mask | tmp_val, &par_io[port].cpdata); return 0; } @@ -157,13 +152,13 @@ int par_io_of_config(struct device_node *np) const unsigned int *pio_map; if (par_io == NULL) { - printk(KERN_ERR "par_io not initialized\n"); + pr_info("par_io not initialized\n"); return -1; } ph = of_get_property(np, "pio-handle", NULL); if (ph == NULL) { - printk(KERN_ERR "pio-handle not available\n"); + pr_info("pio-handle not available\n"); return -1; } @@ -171,12 +166,12 @@ int par_io_of_config(struct device_node *np) pio_map = of_get_property(pio, "pio-map", &pio_map_len); if (pio_map == NULL) { - printk(KERN_ERR "pio-map is not set!\n"); + pr_info("pio-map is not set!\n"); return -1; } pio_map_len /= sizeof(unsigned int); if ((pio_map_len % 6) != 0) { - printk(KERN_ERR "pio-map format wrong!\n"); + pr_info("pio-map format wrong!\n"); return -1; } @@ -197,22 +192,21 @@ static void dump_par_io(void) { unsigned int i; - printk(KERN_INFO "%s: par_io=%p\n", __func__, par_io); + pr_info("%s: par_io=%p\n", __func__, par_io); for (i = 0; i < num_par_io_ports; i++) { - printk(KERN_INFO " cpodr[%u]=%08x\n", i, - in_be32(&par_io[i].cpodr)); - printk(KERN_INFO " cpdata[%u]=%08x\n", i, - in_be32(&par_io[i].cpdata)); - printk(KERN_INFO " cpdir1[%u]=%08x\n", i, - in_be32(&par_io[i].cpdir1)); - printk(KERN_INFO " cpdir2[%u]=%08x\n", i, - in_be32(&par_io[i].cpdir2)); - printk(KERN_INFO " cppar1[%u]=%08x\n", i, - in_be32(&par_io[i].cppar1)); - printk(KERN_INFO " cppar2[%u]=%08x\n", i, - in_be32(&par_io[i].cppar2)); + pr_info(" cpodr[%u]=%08x\n", i, + ioread32be(&par_io[i].cpodr)); + pr_info(" cpdata[%u]=%08x\n", i, + ioread32be(&par_io[i].cpdata)); + pr_info(" cpdir1[%u]=%08x\n", i, + ioread32be(&par_io[i].cpdir1)); + pr_info(" cpdir2[%u]=%08x\n", i, + ioread32be(&par_io[i].cpdir2)); + pr_info(" cppar1[%u]=%08x\n", i, + ioread32be(&par_io[i].cppar1)); + pr_info(" cppar2[%u]=%08x\n", i, + ioread32be(&par_io[i].cppar2)); } - } EXPORT_SYMBOL(dump_par_io); #endif /* DEBUG */ diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/drivers/soc/qe/ucc.c index 1e27d63..c333387 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc.c +++ b/drivers/soc/qe/ucc.c @@ -5,8 +5,8 @@ * * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * 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 @@ -21,10 +21,10 @@ #include <linux/export.h> #include <asm/irq.h> -#include <asm/io.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> -#include <asm/ucc.h> +#include <linux/io.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/ucc.h> int ucc_set_qe_mux_mii_mng(unsigned int ucc_num) { @@ -59,21 +59,29 @@ int ucc_set_type(unsigned int ucc_num, enum ucc_speed_type speed) /* The GUEMR register is at the same location for both slow and fast devices, so we just use uccX.slow.guemr. */ switch (ucc_num) { - case 0: guemr = &qe_immr->ucc1.slow.guemr; + case 0: + guemr = &qe_immr->ucc1.slow.guemr; break; - case 1: guemr = &qe_immr->ucc2.slow.guemr; + case 1: + guemr = &qe_immr->ucc2.slow.guemr; break; - case 2: guemr = &qe_immr->ucc3.slow.guemr; + case 2: + guemr = &qe_immr->ucc3.slow.guemr; break; - case 3: guemr = &qe_immr->ucc4.slow.guemr; + case 3: + guemr = &qe_immr->ucc4.slow.guemr; break; - case 4: guemr = &qe_immr->ucc5.slow.guemr; + case 4: + guemr = &qe_immr->ucc5.slow.guemr; break; - case 5: guemr = &qe_immr->ucc6.slow.guemr; + case 5: + guemr = &qe_immr->ucc6.slow.guemr; break; - case 6: guemr = &qe_immr->ucc7.slow.guemr; + case 6: + guemr = &qe_immr->ucc7.slow.guemr; break; - case 7: guemr = &qe_immr->ucc8.slow.guemr; + case 7: + guemr = &qe_immr->ucc8.slow.guemr; break; default: return -EINVAL; @@ -136,67 +144,156 @@ int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock, switch (reg_num) { case 1: switch (clock) { - case QE_BRG1: clock_bits = 1; break; - case QE_BRG2: clock_bits = 2; break; - case QE_BRG7: clock_bits = 3; break; - case QE_BRG8: clock_bits = 4; break; - case QE_CLK9: clock_bits = 5; break; - case QE_CLK10: clock_bits = 6; break; - case QE_CLK11: clock_bits = 7; break; - case QE_CLK12: clock_bits = 8; break; - case QE_CLK15: clock_bits = 9; break; - case QE_CLK16: clock_bits = 10; break; - default: break; + case QE_BRG1: + clock_bits = 1; + break; + case QE_BRG2: + clock_bits = 2; + break; + case QE_BRG7: + clock_bits = 3; + break; + case QE_BRG8: + clock_bits = 4; + break; + case QE_CLK9: + clock_bits = 5; + break; + case QE_CLK10: + clock_bits = 6; + break; + case QE_CLK11: + clock_bits = 7; + break; + case QE_CLK12: + clock_bits = 8; + break; + case QE_CLK15: + clock_bits = 9; + break; + case QE_CLK16: + clock_bits = 10; + break; + default: + break; } break; case 2: switch (clock) { - case QE_BRG5: clock_bits = 1; break; - case QE_BRG6: clock_bits = 2; break; - case QE_BRG7: clock_bits = 3; break; - case QE_BRG8: clock_bits = 4; break; - case QE_CLK13: clock_bits = 5; break; - case QE_CLK14: clock_bits = 6; break; - case QE_CLK19: clock_bits = 7; break; - case QE_CLK20: clock_bits = 8; break; - case QE_CLK15: clock_bits = 9; break; - case QE_CLK16: clock_bits = 10; break; - default: break; + case QE_BRG5: + clock_bits = 1; + break; + case QE_BRG6: + clock_bits = 2; + break; + case QE_BRG7: + clock_bits = 3; + break; + case QE_BRG8: + clock_bits = 4; + break; + case QE_CLK13: + clock_bits = 5; + break; + case QE_CLK14: + clock_bits = 6; + break; + case QE_CLK19: + clock_bits = 7; + break; + case QE_CLK20: + clock_bits = 8; + break; + case QE_CLK15: + clock_bits = 9; + break; + case QE_CLK16: + clock_bits = 10; + break; + default: + break; } break; case 3: switch (clock) { - case QE_BRG9: clock_bits = 1; break; - case QE_BRG10: clock_bits = 2; break; - case QE_BRG15: clock_bits = 3; break; - case QE_BRG16: clock_bits = 4; break; - case QE_CLK3: clock_bits = 5; break; - case QE_CLK4: clock_bits = 6; break; - case QE_CLK17: clock_bits = 7; break; - case QE_CLK18: clock_bits = 8; break; - case QE_CLK7: clock_bits = 9; break; - case QE_CLK8: clock_bits = 10; break; - case QE_CLK16: clock_bits = 11; break; - default: break; + case QE_BRG9: + clock_bits = 1; + break; + case QE_BRG10: + clock_bits = 2; + break; + case QE_BRG15: + clock_bits = 3; + break; + case QE_BRG16: + clock_bits = 4; + break; + case QE_CLK3: + clock_bits = 5; + break; + case QE_CLK4: + clock_bits = 6; + break; + case QE_CLK17: + clock_bits = 7; + break; + case QE_CLK18: + clock_bits = 8; + break; + case QE_CLK7: + clock_bits = 9; + break; + case QE_CLK8: + clock_bits = 10; + break; + case QE_CLK16: + clock_bits = 11; + break; + default: + break; } break; case 4: switch (clock) { - case QE_BRG13: clock_bits = 1; break; - case QE_BRG14: clock_bits = 2; break; - case QE_BRG15: clock_bits = 3; break; - case QE_BRG16: clock_bits = 4; break; - case QE_CLK5: clock_bits = 5; break; - case QE_CLK6: clock_bits = 6; break; - case QE_CLK21: clock_bits = 7; break; - case QE_CLK22: clock_bits = 8; break; - case QE_CLK7: clock_bits = 9; break; - case QE_CLK8: clock_bits = 10; break; - case QE_CLK16: clock_bits = 11; break; - default: break; + case QE_BRG13: + clock_bits = 1; + break; + case QE_BRG14: + clock_bits = 2; + break; + case QE_BRG15: + clock_bits = 3; + break; + case QE_BRG16: + clock_bits = 4; + break; + case QE_CLK5: + clock_bits = 5; + break; + case QE_CLK6: + clock_bits = 6; + break; + case QE_CLK21: + clock_bits = 7; + break; + case QE_CLK22: + clock_bits = 8; + break; + case QE_CLK7: + clock_bits = 9; + break; + case QE_CLK8: + clock_bits = 10; + break; + case QE_CLK16: + clock_bits = 11; + break; + default: + break; } break; - default: break; + default: + break; } /* Check for invalid combination of clock and UCC number */ diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/drivers/soc/qe/ucc_fast.c index 67191a0..3ed8d22 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c +++ b/drivers/soc/qe/ucc_fast.c @@ -1,8 +1,8 @@ /* * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * Description: * QE UCC Fast API Set - UCC Fast specific routines implementations. @@ -21,54 +21,54 @@ #include <linux/err.h> #include <linux/export.h> -#include <asm/io.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/io.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> -#include <asm/ucc.h> -#include <asm/ucc_fast.h> +#include <linux/fsl/ucc.h> +#include <linux/fsl/ucc_fast.h> -void ucc_fast_dump_regs(struct ucc_fast_private * uccf) +void ucc_fast_dump_regs(struct ucc_fast_private *uccf) { - printk(KERN_INFO "UCC%u Fast registers:\n", uccf->uf_info->ucc_num); - printk(KERN_INFO "Base address: 0x%p\n", uccf->uf_regs); - - printk(KERN_INFO "gumr : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->gumr, in_be32(&uccf->uf_regs->gumr)); - printk(KERN_INFO "upsmr : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->upsmr, in_be32(&uccf->uf_regs->upsmr)); - printk(KERN_INFO "utodr : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utodr, in_be16(&uccf->uf_regs->utodr)); - printk(KERN_INFO "udsr : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->udsr, in_be16(&uccf->uf_regs->udsr)); - printk(KERN_INFO "ucce : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->ucce, in_be32(&uccf->uf_regs->ucce)); - printk(KERN_INFO "uccm : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->uccm, in_be32(&uccf->uf_regs->uccm)); - printk(KERN_INFO "uccs : addr=0x%p, val=0x%02x\n", - &uccf->uf_regs->uccs, in_8(&uccf->uf_regs->uccs)); - printk(KERN_INFO "urfb : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->urfb, in_be32(&uccf->uf_regs->urfb)); - printk(KERN_INFO "urfs : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->urfs, in_be16(&uccf->uf_regs->urfs)); - printk(KERN_INFO "urfet : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->urfet, in_be16(&uccf->uf_regs->urfet)); - printk(KERN_INFO "urfset: addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->urfset, in_be16(&uccf->uf_regs->urfset)); - printk(KERN_INFO "utfb : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->utfb, in_be32(&uccf->uf_regs->utfb)); - printk(KERN_INFO "utfs : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utfs, in_be16(&uccf->uf_regs->utfs)); - printk(KERN_INFO "utfet : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utfet, in_be16(&uccf->uf_regs->utfet)); - printk(KERN_INFO "utftt : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utftt, in_be16(&uccf->uf_regs->utftt)); - printk(KERN_INFO "utpt : addr=0x%p, val=0x%04x\n", - &uccf->uf_regs->utpt, in_be16(&uccf->uf_regs->utpt)); - printk(KERN_INFO "urtry : addr=0x%p, val=0x%08x\n", - &uccf->uf_regs->urtry, in_be32(&uccf->uf_regs->urtry)); - printk(KERN_INFO "guemr : addr=0x%p, val=0x%02x\n", - &uccf->uf_regs->guemr, in_8(&uccf->uf_regs->guemr)); + pr_info("UCC%u Fast registers:\n", uccf->uf_info->ucc_num); + pr_info("Base address: 0x%p\n", uccf->uf_regs); + + pr_info("gumr : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->gumr, ioread32be(&uccf->uf_regs->gumr)); + pr_info("upsmr : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->upsmr, ioread32be(&uccf->uf_regs->upsmr)); + pr_info("utodr : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utodr, ioread16be(&uccf->uf_regs->utodr)); + pr_info("udsr : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->udsr, ioread16be(&uccf->uf_regs->udsr)); + pr_info("ucce : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->ucce, ioread32be(&uccf->uf_regs->ucce)); + pr_info("uccm : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->uccm, ioread32be(&uccf->uf_regs->uccm)); + pr_info("uccs : addr=0x%p, val=0x%02x\n", + &uccf->uf_regs->uccs, ioread8(&uccf->uf_regs->uccs)); + pr_info("urfb : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->urfb, ioread32be(&uccf->uf_regs->urfb)); + pr_info("urfs : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->urfs, ioread16be(&uccf->uf_regs->urfs)); + pr_info("urfet : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->urfet, ioread16be(&uccf->uf_regs->urfet)); + pr_info("urfset: addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->urfset, ioread16be(&uccf->uf_regs->urfset)); + pr_info("utfb : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->utfb, ioread32be(&uccf->uf_regs->utfb)); + pr_info("utfs : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utfs, ioread16be(&uccf->uf_regs->utfs)); + pr_info("utfet : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utfet, ioread16be(&uccf->uf_regs->utfet)); + pr_info("utftt : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utftt, ioread16be(&uccf->uf_regs->utftt)); + pr_info("utpt : addr=0x%p, val=0x%04x\n", + &uccf->uf_regs->utpt, ioread16be(&uccf->uf_regs->utpt)); + pr_info("urtry : addr=0x%p, val=0x%08x\n", + &uccf->uf_regs->urtry, ioread32be(&uccf->uf_regs->urtry)); + pr_info("guemr : addr=0x%p, val=0x%02x\n", + &uccf->uf_regs->guemr, ioread8(&uccf->uf_regs->guemr)); } EXPORT_SYMBOL(ucc_fast_dump_regs); @@ -88,13 +88,13 @@ u32 ucc_fast_get_qe_cr_subblock(int uccf_num) } EXPORT_SYMBOL(ucc_fast_get_qe_cr_subblock); -void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf) +void ucc_fast_transmit_on_demand(struct ucc_fast_private *uccf) { - out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); + iowrite16be(UCC_FAST_TOD, &uccf->uf_regs->utodr); } EXPORT_SYMBOL(ucc_fast_transmit_on_demand); -void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) +void ucc_fast_enable(struct ucc_fast_private *uccf, enum comm_dir mode) { struct ucc_fast __iomem *uf_regs; u32 gumr; @@ -102,7 +102,7 @@ void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) uf_regs = uccf->uf_regs; /* Enable reception and/or transmission on this UCC. */ - gumr = in_be32(&uf_regs->gumr); + gumr = ioread32be(&uf_regs->gumr); if (mode & COMM_DIR_TX) { gumr |= UCC_FAST_GUMR_ENT; uccf->enabled_tx = 1; @@ -111,11 +111,11 @@ void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode) gumr |= UCC_FAST_GUMR_ENR; uccf->enabled_rx = 1; } - out_be32(&uf_regs->gumr, gumr); + iowrite32be(gumr, &uf_regs->gumr); } EXPORT_SYMBOL(ucc_fast_enable); -void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) +void ucc_fast_disable(struct ucc_fast_private *uccf, enum comm_dir mode) { struct ucc_fast __iomem *uf_regs; u32 gumr; @@ -123,7 +123,7 @@ void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) uf_regs = uccf->uf_regs; /* Disable reception and/or transmission on this UCC. */ - gumr = in_be32(&uf_regs->gumr); + gumr = ioread32be(&uf_regs->gumr); if (mode & COMM_DIR_TX) { gumr &= ~UCC_FAST_GUMR_ENT; uccf->enabled_tx = 0; @@ -132,11 +132,12 @@ void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode) gumr &= ~UCC_FAST_GUMR_ENR; uccf->enabled_rx = 0; } - out_be32(&uf_regs->gumr, gumr); + iowrite32be(gumr, &uf_regs->gumr); } EXPORT_SYMBOL(ucc_fast_disable); -int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret) +int ucc_fast_init(struct ucc_fast_info *uf_info, + struct ucc_fast_private **uccf_ret) { struct ucc_fast_private *uccf; struct ucc_fast __iomem *uf_regs; @@ -148,56 +149,56 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc /* check if the UCC port number is in range. */ if ((uf_info->ucc_num < 0) || (uf_info->ucc_num > UCC_MAX_NUM - 1)) { - printk(KERN_ERR "%s: illegal UCC number\n", __func__); + pr_err("%s: illegal UCC number\n", __func__); return -EINVAL; } /* Check that 'max_rx_buf_length' is properly aligned (4). */ if (uf_info->max_rx_buf_length & (UCC_FAST_MRBLR_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: max_rx_buf_length not aligned\n", + pr_err("%s: max_rx_buf_length not aligned\n", __func__); return -EINVAL; } /* Validate Virtual Fifo register values */ if (uf_info->urfs < UCC_FAST_URFS_MIN_VAL) { - printk(KERN_ERR "%s: urfs is too small\n", __func__); + pr_err("%s: urfs is too small\n", __func__); return -EINVAL; } if (uf_info->urfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: urfs is not aligned\n", __func__); + pr_err("%s: urfs is not aligned\n", __func__); return -EINVAL; } if (uf_info->urfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: urfet is not aligned.\n", __func__); + pr_err("%s: urfet is not aligned.\n", __func__); return -EINVAL; } if (uf_info->urfset & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: urfset is not aligned\n", __func__); + pr_err("%s: urfset is not aligned\n", __func__); return -EINVAL; } if (uf_info->utfs & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: utfs is not aligned\n", __func__); + pr_err("%s: utfs is not aligned\n", __func__); return -EINVAL; } if (uf_info->utfet & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: utfet is not aligned\n", __func__); + pr_err("%s: utfet is not aligned\n", __func__); return -EINVAL; } if (uf_info->utftt & (UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT - 1)) { - printk(KERN_ERR "%s: utftt is not aligned\n", __func__); + pr_err("%s: utftt is not aligned\n", __func__); return -EINVAL; } uccf = kzalloc(sizeof(struct ucc_fast_private), GFP_KERNEL); if (!uccf) { - printk(KERN_ERR "%s: Cannot allocate private data\n", + pr_err("%s: Cannot allocate private data\n", __func__); return -ENOMEM; } @@ -207,7 +208,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc /* Set the PHY base address */ uccf->uf_regs = ioremap(uf_info->regs, sizeof(struct ucc_fast)); if (uccf->uf_regs == NULL) { - printk(KERN_ERR "%s: Cannot map UCC registers\n", __func__); + pr_err("%s: Cannot map UCC registers\n", __func__); kfree(uccf); return -ENOMEM; } @@ -231,7 +232,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc /* Set UCC to fast type */ ret = ucc_set_type(uf_info->ucc_num, UCC_SPEED_TYPE_FAST); if (ret) { - printk(KERN_ERR "%s: cannot set UCC type\n", __func__); + pr_err("%s: cannot set UCC type\n", __func__); ucc_fast_free(uccf); return ret; } @@ -264,13 +265,13 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc gumr |= uf_info->tenc; gumr |= uf_info->tcrc; gumr |= uf_info->mode; - out_be32(&uf_regs->gumr, gumr); + iowrite32be(gumr, &uf_regs->gumr); /* Allocate memory for Tx Virtual Fifo */ uccf->ucc_fast_tx_virtual_fifo_base_offset = qe_muram_alloc(uf_info->utfs, UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); if (IS_ERR_VALUE(uccf->ucc_fast_tx_virtual_fifo_base_offset)) { - printk(KERN_ERR "%s: cannot allocate MURAM for TX FIFO\n", + pr_err("%s: cannot allocate MURAM for TX FIFO\n", __func__); uccf->ucc_fast_tx_virtual_fifo_base_offset = 0; ucc_fast_free(uccf); @@ -283,7 +284,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc UCC_FAST_RECEIVE_VIRTUAL_FIFO_SIZE_FUDGE_FACTOR, UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); if (IS_ERR_VALUE(uccf->ucc_fast_rx_virtual_fifo_base_offset)) { - printk(KERN_ERR "%s: cannot allocate MURAM for RX FIFO\n", + pr_err("%s: cannot allocate MURAM for RX FIFO\n", __func__); uccf->ucc_fast_rx_virtual_fifo_base_offset = 0; ucc_fast_free(uccf); @@ -291,15 +292,15 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc } /* Set Virtual Fifo registers */ - out_be16(&uf_regs->urfs, uf_info->urfs); - out_be16(&uf_regs->urfet, uf_info->urfet); - out_be16(&uf_regs->urfset, uf_info->urfset); - out_be16(&uf_regs->utfs, uf_info->utfs); - out_be16(&uf_regs->utfet, uf_info->utfet); - out_be16(&uf_regs->utftt, uf_info->utftt); + iowrite16be(uf_info->urfs, &uf_regs->urfs); + iowrite16be(uf_info->urfet, &uf_regs->urfet); + iowrite16be(uf_info->urfset, &uf_regs->urfset); + iowrite16be(uf_info->utfs, &uf_regs->utfs); + iowrite16be(uf_info->utfet, &uf_regs->utfet); + iowrite16be(uf_info->utftt, &uf_regs->utftt); /* utfb, urfb are offsets from MURAM base */ - out_be32(&uf_regs->utfb, uccf->ucc_fast_tx_virtual_fifo_base_offset); - out_be32(&uf_regs->urfb, uccf->ucc_fast_rx_virtual_fifo_base_offset); + iowrite32be(uccf->ucc_fast_tx_virtual_fifo_base_offset, &uf_regs->utfb); + iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->urfb); /* Mux clocking */ /* Grant Support */ @@ -314,7 +315,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc if ((uf_info->rx_clock != QE_CLK_NONE) && ucc_set_qe_mux_rxtx(uf_info->ucc_num, uf_info->rx_clock, COMM_DIR_RX)) { - printk(KERN_ERR "%s: illegal value for RX clock\n", + pr_err("%s: illegal value for RX clock\n", __func__); ucc_fast_free(uccf); return -EINVAL; @@ -323,7 +324,7 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc if ((uf_info->tx_clock != QE_CLK_NONE) && ucc_set_qe_mux_rxtx(uf_info->ucc_num, uf_info->tx_clock, COMM_DIR_TX)) { - printk(KERN_ERR "%s: illegal value for TX clock\n", + pr_err("%s: illegal value for TX clock\n", __func__); ucc_fast_free(uccf); return -EINVAL; @@ -367,21 +368,21 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc } /* Set interrupt mask register at UCC level. */ - out_be32(&uf_regs->uccm, uf_info->uccm_mask); + iowrite32be(uf_info->uccm_mask, &uf_regs->uccm); /* First, clear anything pending at UCC level, * otherwise, old garbage may come through * as soon as the dam is opened. */ /* Writing '1' clears */ - out_be32(&uf_regs->ucce, 0xffffffff); + iowrite32be(0xffffffff, &uf_regs->ucce); *uccf_ret = uccf; return 0; } EXPORT_SYMBOL(ucc_fast_init); -void ucc_fast_free(struct ucc_fast_private * uccf) +void ucc_fast_free(struct ucc_fast_private *uccf) { if (!uccf) return; diff --git a/arch/powerpc/sysdev/qe_lib/ucc_slow.c b/drivers/soc/qe/ucc_slow.c index 1c062f4..edb1b2e 100644 --- a/arch/powerpc/sysdev/qe_lib/ucc_slow.c +++ b/drivers/soc/qe/ucc_slow.c @@ -1,8 +1,8 @@ /* * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * Description: * QE UCC Slow API Set - UCC Slow specific routines implementations. @@ -21,12 +21,12 @@ #include <linux/err.h> #include <linux/export.h> -#include <asm/io.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/io.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> -#include <asm/ucc.h> -#include <asm/ucc_slow.h> +#include <linux/fsl/ucc.h> +#include <linux/fsl/ucc_slow.h> u32 ucc_slow_get_qe_cr_subblock(int uccs_num) { @@ -44,12 +44,12 @@ u32 ucc_slow_get_qe_cr_subblock(int uccs_num) } EXPORT_SYMBOL(ucc_slow_get_qe_cr_subblock); -void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs) +void ucc_slow_poll_transmitter_now(struct ucc_slow_private *uccs) { - out_be16(&uccs->us_regs->utodr, UCC_SLOW_TOD); + iowrite16be(UCC_SLOW_TOD, &uccs->us_regs->utodr); } -void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs) +void ucc_slow_graceful_stop_tx(struct ucc_slow_private *uccs) { struct ucc_slow_info *us_info = uccs->us_info; u32 id; @@ -60,7 +60,7 @@ void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs) } EXPORT_SYMBOL(ucc_slow_graceful_stop_tx); -void ucc_slow_stop_tx(struct ucc_slow_private * uccs) +void ucc_slow_stop_tx(struct ucc_slow_private *uccs) { struct ucc_slow_info *us_info = uccs->us_info; u32 id; @@ -70,7 +70,7 @@ void ucc_slow_stop_tx(struct ucc_slow_private * uccs) } EXPORT_SYMBOL(ucc_slow_stop_tx); -void ucc_slow_restart_tx(struct ucc_slow_private * uccs) +void ucc_slow_restart_tx(struct ucc_slow_private *uccs) { struct ucc_slow_info *us_info = uccs->us_info; u32 id; @@ -80,7 +80,7 @@ void ucc_slow_restart_tx(struct ucc_slow_private * uccs) } EXPORT_SYMBOL(ucc_slow_restart_tx); -void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode) +void ucc_slow_enable(struct ucc_slow_private *uccs, enum comm_dir mode) { struct ucc_slow *us_regs; u32 gumr_l; @@ -88,7 +88,7 @@ void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode) us_regs = uccs->us_regs; /* Enable reception and/or transmission on this UCC. */ - gumr_l = in_be32(&us_regs->gumr_l); + gumr_l = ioread32be(&us_regs->gumr_l); if (mode & COMM_DIR_TX) { gumr_l |= UCC_SLOW_GUMR_L_ENT; uccs->enabled_tx = 1; @@ -97,11 +97,11 @@ void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode) gumr_l |= UCC_SLOW_GUMR_L_ENR; uccs->enabled_rx = 1; } - out_be32(&us_regs->gumr_l, gumr_l); + iowrite32be(gumr_l, &us_regs->gumr_l); } EXPORT_SYMBOL(ucc_slow_enable); -void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode) +void ucc_slow_disable(struct ucc_slow_private *uccs, enum comm_dir mode) { struct ucc_slow *us_regs; u32 gumr_l; @@ -109,7 +109,7 @@ void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode) us_regs = uccs->us_regs; /* Disable reception and/or transmission on this UCC. */ - gumr_l = in_be32(&us_regs->gumr_l); + gumr_l = ioread32be(&us_regs->gumr_l); if (mode & COMM_DIR_TX) { gumr_l &= ~UCC_SLOW_GUMR_L_ENT; uccs->enabled_tx = 0; @@ -118,7 +118,7 @@ void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode) gumr_l &= ~UCC_SLOW_GUMR_L_ENR; uccs->enabled_rx = 0; } - out_be32(&us_regs->gumr_l, gumr_l); + iowrite32be(gumr_l, &us_regs->gumr_l); } EXPORT_SYMBOL(ucc_slow_disable); @@ -126,7 +126,8 @@ EXPORT_SYMBOL(ucc_slow_disable); * * The caller should initialize the following us_info */ -int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** uccs_ret) +int ucc_slow_init(struct ucc_slow_info *us_info, + struct ucc_slow_private **uccs_ret) { struct ucc_slow_private *uccs; u32 i; @@ -142,7 +143,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc /* check if the UCC port number is in range. */ if ((us_info->ucc_num < 0) || (us_info->ucc_num > UCC_MAX_NUM - 1)) { - printk(KERN_ERR "%s: illegal UCC number\n", __func__); + pr_err("%s: illegal UCC number\n", __func__); return -EINVAL; } @@ -154,13 +155,13 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc */ if ((!us_info->rfw) && (us_info->max_rx_buf_length & (UCC_SLOW_MRBLR_ALIGNMENT - 1))) { - printk(KERN_ERR "max_rx_buf_length not aligned.\n"); + pr_err("max_rx_buf_length not aligned.\n"); return -EINVAL; } uccs = kzalloc(sizeof(struct ucc_slow_private), GFP_KERNEL); if (!uccs) { - printk(KERN_ERR "%s: Cannot allocate private data\n", + pr_err("%s: Cannot allocate private data\n", __func__); return -ENOMEM; } @@ -170,7 +171,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc /* Set the PHY base address */ uccs->us_regs = ioremap(us_info->regs, sizeof(struct ucc_slow)); if (uccs->us_regs == NULL) { - printk(KERN_ERR "%s: Cannot map UCC registers\n", __func__); + pr_err("%s: Cannot map UCC registers\n", __func__); kfree(uccs); return -ENOMEM; } @@ -178,8 +179,8 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc uccs->saved_uccm = 0; uccs->p_rx_frame = 0; us_regs = uccs->us_regs; - uccs->p_ucce = (u16 *) & (us_regs->ucce); - uccs->p_uccm = (u16 *) & (us_regs->uccm); + uccs->p_ucce = (u16 *)&(us_regs->ucce); + uccs->p_uccm = (u16 *)&(us_regs->uccm); #ifdef STATISTICS uccs->rx_frames = 0; uccs->tx_frames = 0; @@ -190,7 +191,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc uccs->us_pram_offset = qe_muram_alloc(UCC_SLOW_PRAM_SIZE, ALIGNMENT_OF_UCC_SLOW_PRAM); if (IS_ERR_VALUE(uccs->us_pram_offset)) { - printk(KERN_ERR "%s: cannot allocate MURAM for PRAM", __func__); + pr_err("%s: cannot allocate MURAM for PRAM", __func__); ucc_slow_free(uccs); return -ENOMEM; } @@ -203,12 +204,12 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc /* Set UCC to slow type */ ret = ucc_set_type(us_info->ucc_num, UCC_SPEED_TYPE_SLOW); if (ret) { - printk(KERN_ERR "%s: cannot set UCC type", __func__); + pr_err("%s: cannot set UCC type", __func__); ucc_slow_free(uccs); return ret; } - out_be16(&uccs->us_pram->mrblr, us_info->max_rx_buf_length); + iowrite16be(us_info->max_rx_buf_length, &uccs->us_pram->mrblr); INIT_LIST_HEAD(&uccs->confQ); @@ -217,7 +218,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc qe_muram_alloc(us_info->rx_bd_ring_len * sizeof(struct qe_bd), QE_ALIGNMENT_OF_BD); if (IS_ERR_VALUE(uccs->rx_base_offset)) { - printk(KERN_ERR "%s: cannot allocate %u RX BDs\n", __func__, + pr_err("%s: cannot allocate %u RX BDs\n", __func__, us_info->rx_bd_ring_len); uccs->rx_base_offset = 0; ucc_slow_free(uccs); @@ -228,7 +229,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc qe_muram_alloc(us_info->tx_bd_ring_len * sizeof(struct qe_bd), QE_ALIGNMENT_OF_BD); if (IS_ERR_VALUE(uccs->tx_base_offset)) { - printk(KERN_ERR "%s: cannot allocate TX BDs", __func__); + pr_err("%s: cannot allocate TX BDs", __func__); uccs->tx_base_offset = 0; ucc_slow_free(uccs); return -ENOMEM; @@ -238,27 +239,27 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc bd = uccs->confBd = uccs->tx_bd = qe_muram_addr(uccs->tx_base_offset); for (i = 0; i < us_info->tx_bd_ring_len - 1; i++) { /* clear bd buffer */ - out_be32(&bd->buf, 0); + iowrite32be(0, &bd->buf); /* set bd status and length */ - out_be32((u32 *) bd, 0); + iowrite32be(0, (u32 *) bd); bd++; } /* for last BD set Wrap bit */ - out_be32(&bd->buf, 0); - out_be32((u32 *) bd, cpu_to_be32(T_W)); + iowrite32be(0, &bd->buf); + iowrite32be(T_W, (u32 *) bd); /* Init Rx bds */ bd = uccs->rx_bd = qe_muram_addr(uccs->rx_base_offset); for (i = 0; i < us_info->rx_bd_ring_len - 1; i++) { /* set bd status and length */ - out_be32((u32*)bd, 0); + iowrite32be(0, (u32 *)bd); /* clear bd buffer */ - out_be32(&bd->buf, 0); + iowrite32be(0, &bd->buf); bd++; } /* for last BD set Wrap bit */ - out_be32((u32*)bd, cpu_to_be32(R_W)); - out_be32(&bd->buf, 0); + iowrite32be(R_W, (u32 *)bd); + iowrite32be(0, &bd->buf); /* Set GUMR (For more details see the hardware spec.). */ /* gumr_h */ @@ -279,7 +280,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc gumr |= UCC_SLOW_GUMR_H_TXSY; if (us_info->rtsm) gumr |= UCC_SLOW_GUMR_H_RTSM; - out_be32(&us_regs->gumr_h, gumr); + iowrite32be(gumr, &us_regs->gumr_h); /* gumr_l */ gumr = us_info->tdcr | us_info->rdcr | us_info->tenc | us_info->renc | @@ -292,7 +293,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc gumr |= UCC_SLOW_GUMR_L_TINV; if (us_info->tend) gumr |= UCC_SLOW_GUMR_L_TEND; - out_be32(&us_regs->gumr_l, gumr); + iowrite32be(gumr, &us_regs->gumr_l); /* Function code registers */ @@ -302,8 +303,8 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc uccs->us_pram->rbmr = UCC_BMR_BO_BE; /* rbase, tbase are offsets from MURAM base */ - out_be16(&uccs->us_pram->rbase, uccs->rx_base_offset); - out_be16(&uccs->us_pram->tbase, uccs->tx_base_offset); + iowrite16be(uccs->rx_base_offset, &uccs->us_pram->rbase); + iowrite16be(uccs->tx_base_offset, &uccs->us_pram->tbase); /* Mux clocking */ /* Grant Support */ @@ -317,7 +318,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc /* Rx clock routing */ if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->rx_clock, COMM_DIR_RX)) { - printk(KERN_ERR "%s: illegal value for RX clock\n", + pr_err("%s: illegal value for RX clock\n", __func__); ucc_slow_free(uccs); return -EINVAL; @@ -325,7 +326,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc /* Tx clock routing */ if (ucc_set_qe_mux_rxtx(us_info->ucc_num, us_info->tx_clock, COMM_DIR_TX)) { - printk(KERN_ERR "%s: illegal value for TX clock\n", + pr_err("%s: illegal value for TX clock\n", __func__); ucc_slow_free(uccs); return -EINVAL; @@ -333,14 +334,14 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc } /* Set interrupt mask register at UCC level. */ - out_be16(&us_regs->uccm, us_info->uccm_mask); + iowrite16be(us_info->uccm_mask, &us_regs->uccm); /* First, clear anything pending at UCC level, * otherwise, old garbage may come through * as soon as the dam is opened. */ /* Writing '1' clears */ - out_be16(&us_regs->ucce, 0xffff); + iowrite16be(0xffff, &us_regs->ucce); /* Issue QE Init command */ if (us_info->init_tx && us_info->init_rx) @@ -357,7 +358,7 @@ int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** ucc } EXPORT_SYMBOL(ucc_slow_init); -void ucc_slow_free(struct ucc_slow_private * uccs) +void ucc_slow_free(struct ucc_slow_private *uccs) { if (!uccs) return; @@ -377,4 +378,3 @@ void ucc_slow_free(struct ucc_slow_private * uccs) kfree(uccs); } EXPORT_SYMBOL(ucc_slow_free); - diff --git a/arch/powerpc/sysdev/qe_lib/usb.c b/drivers/soc/qe/usb.c index 27f23bd..58f812a 100644 --- a/arch/powerpc/sysdev/qe_lib/usb.c +++ b/drivers/soc/qe/usb.c @@ -17,8 +17,8 @@ #include <linux/errno.h> #include <linux/export.h> #include <linux/io.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> int qe_usb_clock_set(enum qe_clock clk, int rate) { @@ -27,16 +27,36 @@ int qe_usb_clock_set(enum qe_clock clk, int rate) u32 val; switch (clk) { - case QE_CLK3: val = QE_CMXGCR_USBCS_CLK3; break; - case QE_CLK5: val = QE_CMXGCR_USBCS_CLK5; break; - case QE_CLK7: val = QE_CMXGCR_USBCS_CLK7; break; - case QE_CLK9: val = QE_CMXGCR_USBCS_CLK9; break; - case QE_CLK13: val = QE_CMXGCR_USBCS_CLK13; break; - case QE_CLK17: val = QE_CMXGCR_USBCS_CLK17; break; - case QE_CLK19: val = QE_CMXGCR_USBCS_CLK19; break; - case QE_CLK21: val = QE_CMXGCR_USBCS_CLK21; break; - case QE_BRG9: val = QE_CMXGCR_USBCS_BRG9; break; - case QE_BRG10: val = QE_CMXGCR_USBCS_BRG10; break; + case QE_CLK3: + val = QE_CMXGCR_USBCS_CLK3; + break; + case QE_CLK5: + val = QE_CMXGCR_USBCS_CLK5; + break; + case QE_CLK7: + val = QE_CMXGCR_USBCS_CLK7; + break; + case QE_CLK9: + val = QE_CMXGCR_USBCS_CLK9; + break; + case QE_CLK13: + val = QE_CMXGCR_USBCS_CLK13; + break; + case QE_CLK17: + val = QE_CMXGCR_USBCS_CLK17; + break; + case QE_CLK19: + val = QE_CMXGCR_USBCS_CLK19; + break; + case QE_CLK21: + val = QE_CMXGCR_USBCS_CLK21; + break; + case QE_BRG9: + val = QE_CMXGCR_USBCS_BRG9; + break; + case QE_BRG10: + val = QE_CMXGCR_USBCS_BRG10; + break; default: pr_err("%s: requested unknown clock %d\n", __func__, clk); return -EINVAL; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b9c53cc..352f982 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -263,7 +263,8 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" - select SPI_BITBANG + select REGMAP_MMIO + depends on SOC_VF610 || SOC_LS1021A || COMPILE_TEST help This enables support for the Freescale DSPI controller in master mode. VF610 platform uses the controller. diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 9a64c3f..595b62c 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -219,7 +219,7 @@ static int altera_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); /* setup the state for the bitbang driver */ - hw->bitbang.master = spi_master_get(master); + hw->bitbang.master = master; if (!hw->bitbang.master) return err; hw->bitbang.chipselect = altera_spi_chipsel; diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 05dd692..e4a2001 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -231,7 +231,7 @@ static int ath79_spi_probe(struct platform_device *pdev) master->num_chipselect = pdata->num_chipselect; } - sp->bitbang.master = spi_master_get(master); + sp->bitbang.master = master; sp->bitbang.chipselect = ath79_spi_chipselect; sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; sp->bitbang.setup_transfer = spi_bitbang_setup_transfer; diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 1d00d9b3..e06400a 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -775,7 +775,7 @@ static int au1550_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 8c11355..0056623 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -414,10 +414,16 @@ static int spi_bitbang_unprepare_hardware(struct spi_master *spi) * This routine registers the spi_master, which will process requests in a * dedicated task, keeping IRQs unblocked most of the time. To stop * processing those requests, call spi_bitbang_stop(). + * + * On success, this routine will take a reference to master. The caller is + * responsible for calling spi_bitbang_stop() to decrement the reference and + * spi_master_put() as counterpart of spi_alloc_master() to prevent a memory + * leak. */ int spi_bitbang_start(struct spi_bitbang *bitbang) { struct spi_master *master = bitbang->master; + int ret; if (!master || !bitbang->chipselect) return -EINVAL; @@ -449,7 +455,11 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - return spi_register_master(master); + ret = spi_register_master(spi_master_get(master)); + if (ret) + spi_master_put(master); + + return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_start); diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 5ed08e5..b1ecea2 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -225,7 +225,7 @@ static void butterfly_attach(struct parport *p) master->bus_num = 42; master->num_chipselect = 2; - pp->bitbang.master = spi_master_get(master); + pp->bitbang.master = master; pp->bitbang.chipselect = butterfly_chipselect; pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 8fbfe24..af2bb73 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -916,7 +916,7 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto unmap_io; - dspi->bitbang.master = spi_master_get(master); + dspi->bitbang.master = master; if (dspi->bitbang.master == NULL) { ret = -ENODEV; goto irq_free; @@ -925,7 +925,7 @@ static int davinci_spi_probe(struct platform_device *pdev) dspi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dspi->clk)) { ret = -ENODEV; - goto put_master; + goto irq_free; } clk_prepare_enable(dspi->clk); @@ -1015,8 +1015,6 @@ free_dma: free_clk: clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); -put_master: - spi_master_put(master); irq_free: free_irq(dspi->irq, dspi); unmap_io: @@ -1024,7 +1022,7 @@ unmap_io: release_region: release_mem_region(dspi->pbase, resource_size(r)); free_master: - kfree(master); + spi_master_put(master); err: return ret; } @@ -1051,11 +1049,11 @@ static int davinci_spi_remove(struct platform_device *pdev) clk_disable_unprepare(dspi->clk); clk_put(dspi->clk); - spi_master_put(master); free_irq(dspi->irq, dspi); iounmap(dspi->base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(dspi->pbase, resource_size(r)); + spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index 7d84418..d428a40 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -347,7 +347,7 @@ static int efm32_spi_probe(struct platform_device *pdev) ddata = spi_master_get_devdata(master); - ddata->bitbang.master = spi_master_get(master); + ddata->bitbang.master = master; ddata->bitbang.chipselect = efm32_spi_chipselect; ddata->bitbang.setup_transfer = efm32_spi_setup_transfer; ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs; diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index 07971e3..e856d85 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -21,7 +21,7 @@ #include <linux/fsl_devices.h> #include <linux/dma-mapping.h> #include <asm/cpm.h> -#include <asm/qe.h> +#include <linux/fsl/qe.h> #include "spi-fsl-lib.h" #include "spi-fsl-cpm.h" diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index f132234..a6df59e 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/errno.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/io.h> @@ -62,9 +63,11 @@ #define SPI_CTAR0_SLAVE 0x0c #define SPI_SR 0x2c +#define SPI_SR_TCFQF 0x80000000 #define SPI_SR_EOQF 0x10000000 #define SPI_RSER 0x30 +#define SPI_RSER_TCFQE 0x80000000 #define SPI_RSER_EOQFE 0x10000000 #define SPI_PUSHR 0x34 @@ -104,15 +107,22 @@ struct chip_data { u16 void_write_data; }; +enum dspi_trans_mode { + DSPI_EOQ_MODE = 0, + DSPI_TCFQ_MODE, + DSPI_DMA_MODE, /*TODO*/ +}; + struct fsl_dspi { - struct spi_bitbang bitbang; + struct spi_master *master; struct platform_device *pdev; - void *base; + struct regmap *regmap; int irq; - struct clk *clk; + struct clk *clk; - struct spi_transfer *cur_transfer; + struct spi_transfer *cur_transfer; + struct spi_message *cur_msg; struct chip_data *cur_chip; size_t len; void *tx; @@ -122,25 +132,20 @@ struct fsl_dspi { char dataflags; u8 cs; u16 void_write_data; + u32 cs_change; + enum dspi_trans_mode trans_mode; - wait_queue_head_t waitq; - u32 waitflags; + wait_queue_head_t waitq; + u32 waitflags; }; static inline int is_double_byte_mode(struct fsl_dspi *dspi) { - return ((readl(dspi->base + SPI_CTAR(dspi->cs)) & SPI_FRAME_BITS_MASK) - == SPI_FRAME_BITS(8)) ? 0 : 1; -} + unsigned int val; -static void set_bit_mode(struct fsl_dspi *dspi, unsigned char bits) -{ - u32 temp; + regmap_read(dspi->regmap, SPI_CTAR(dspi->cs), &val); - temp = readl(dspi->base + SPI_CTAR(dspi->cs)); - temp &= ~SPI_FRAME_BITS_MASK; - temp |= SPI_FRAME_BITS(bits); - writel(temp, dspi->base + SPI_CTAR(dspi->cs)); + return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1; } static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, @@ -165,18 +170,74 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, } } - pr_warn("Can not find valid buad rate,speed_hz is %d,clkrate is %ld\ + pr_warn("Can not find valid baud rate,speed_hz is %d,clkrate is %ld\ ,we use the max prescaler value.\n", speed_hz, clkrate); *pbr = ARRAY_SIZE(pbr_tbl) - 1; *br = ARRAY_SIZE(brs) - 1; } -static int dspi_transfer_write(struct fsl_dspi *dspi) +static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) +{ + u16 d16; + u8 d8; + + if (tx_word) { + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + d16 = *(u16 *)dspi->tx; + dspi->tx += 2; + } else { + d16 = dspi->void_write_data; + } + + dspi->len -= 2; + + return SPI_PUSHR_TXDATA(d16) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; + } else { + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + + d8 = *(u8 *)dspi->tx; + dspi->tx++; + } else { + d8 = (u8)dspi->void_write_data; + } + + dspi->len--; + + return SPI_PUSHR_TXDATA(d8) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; + } +} + +static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) +{ + u16 d; + unsigned int val; + + if (rx_word) { + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); + + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + *(u16 *)dspi->rx = d; + dspi->rx += 2; + } else { + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + *(u8 *)dspi->rx = d; + dspi->rx++; + } +} + +static int dspi_eoq_write(struct fsl_dspi *dspi) { int tx_count = 0; int tx_word; - u16 d16; - u8 d8; u32 dspi_pushr = 0; int first = 1; @@ -188,48 +249,19 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) */ if (tx_word && (dspi->len == 1)) { dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - set_bit_mode(dspi, 8); + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); tx_word = 0; } while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { - if (tx_word) { - if (dspi->len == 1) - break; - - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { - d16 = *(u16 *)dspi->tx; - dspi->tx += 2; - } else { - d16 = dspi->void_write_data; - } - - dspi_pushr = SPI_PUSHR_TXDATA(d16) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(dspi->cs) | - SPI_PUSHR_CONT; - - dspi->len -= 2; - } else { - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { - - d8 = *(u8 *)dspi->tx; - dspi->tx++; - } else { - d8 = (u8)dspi->void_write_data; - } - - dspi_pushr = SPI_PUSHR_TXDATA(d8) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(dspi->cs) | - SPI_PUSHR_CONT; - - dspi->len--; - } + dspi_pushr = dspi_data_to_pushr(dspi, tx_word); if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { /* last transfer in the transfer */ dspi_pushr |= SPI_PUSHR_EOQ; + if ((dspi->cs_change) && (!dspi->len)) + dspi_pushr &= ~SPI_PUSHR_CONT; } else if (tx_word && (dspi->len == 1)) dspi_pushr |= SPI_PUSHR_EOQ; @@ -238,96 +270,127 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */ } - writel(dspi_pushr, dspi->base + SPI_PUSHR); + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); + tx_count++; } return tx_count * (tx_word + 1); } -static int dspi_transfer_read(struct fsl_dspi *dspi) +static int dspi_eoq_read(struct fsl_dspi *dspi) { int rx_count = 0; int rx_word = is_double_byte_mode(dspi); - u16 d; + while ((dspi->rx < dspi->rx_end) && (rx_count < DSPI_FIFO_SIZE)) { - if (rx_word) { - if ((dspi->rx_end - dspi->rx) == 1) - break; - - d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); - - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) - *(u16 *)dspi->rx = d; - dspi->rx += 2; - - } else { - d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) - *(u8 *)dspi->rx = d; - dspi->rx++; - } + dspi_data_from_popr(dspi, rx_word); rx_count++; } return rx_count; } -static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) +static int dspi_tcfq_write(struct fsl_dspi *dspi) { - struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - dspi->cur_transfer = t; - dspi->cur_chip = spi_get_ctldata(spi); - dspi->cs = spi->chip_select; - dspi->void_write_data = dspi->cur_chip->void_write_data; + int tx_word; + u32 dspi_pushr = 0; - dspi->dataflags = 0; - dspi->tx = (void *)t->tx_buf; - dspi->tx_end = dspi->tx + t->len; - dspi->rx = t->rx_buf; - dspi->rx_end = dspi->rx + t->len; - dspi->len = t->len; + tx_word = is_double_byte_mode(dspi); - if (!dspi->rx) - dspi->dataflags |= TRAN_STATE_RX_VOID; + if (tx_word && (dspi->len == 1)) { + dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); + tx_word = 0; + } - if (!dspi->tx) - dspi->dataflags |= TRAN_STATE_TX_VOID; + dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - writel(dspi->cur_chip->mcr_val, dspi->base + SPI_MCR); - writel(dspi->cur_chip->ctar_val, dspi->base + SPI_CTAR(dspi->cs)); - writel(SPI_RSER_EOQFE, dspi->base + SPI_RSER); + if ((dspi->cs_change) && (!dspi->len)) + dspi_pushr &= ~SPI_PUSHR_CONT; - if (t->speed_hz) - writel(dspi->cur_chip->ctar_val, - dspi->base + SPI_CTAR(dspi->cs)); + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); - dspi_transfer_write(dspi); + return tx_word + 1; +} - if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) - dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); - dspi->waitflags = 0; +static void dspi_tcfq_read(struct fsl_dspi *dspi) +{ + int rx_word = is_double_byte_mode(dspi); - return t->len - dspi->len; + dspi_data_from_popr(dspi, rx_word); } -static void dspi_chipselect(struct spi_device *spi, int value) +static int dspi_transfer_one_message(struct spi_master *master, + struct spi_message *message) { - struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - u32 pushr = readl(dspi->base + SPI_PUSHR); + struct fsl_dspi *dspi = spi_master_get_devdata(master); + struct spi_device *spi = message->spi; + struct spi_transfer *transfer; + int status = 0; + message->actual_length = 0; + + list_for_each_entry(transfer, &message->transfers, transfer_list) { + dspi->cur_transfer = transfer; + dspi->cur_msg = message; + dspi->cur_chip = spi_get_ctldata(spi); + dspi->cs = spi->chip_select; + if (dspi->cur_transfer->transfer_list.next + == &dspi->cur_msg->transfers) + transfer->cs_change = 1; + dspi->cs_change = transfer->cs_change; + dspi->void_write_data = dspi->cur_chip->void_write_data; + + dspi->dataflags = 0; + dspi->tx = (void *)transfer->tx_buf; + dspi->tx_end = dspi->tx + transfer->len; + dspi->rx = transfer->rx_buf; + dspi->rx_end = dspi->rx + transfer->len; + dspi->len = transfer->len; + + if (!dspi->rx) + dspi->dataflags |= TRAN_STATE_RX_VOID; + + if (!dspi->tx) + dspi->dataflags |= TRAN_STATE_TX_VOID; + + regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); + regmap_update_bits(dspi->regmap, SPI_MCR, + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); + regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), + dspi->cur_chip->ctar_val); + if (transfer->speed_hz) + regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), + dspi->cur_chip->ctar_val); + + if (dspi->trans_mode == DSPI_EOQ_MODE) { + regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); + message->actual_length += dspi_eoq_write(dspi); + } else if (dspi->trans_mode == DSPI_TCFQ_MODE) { + regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE); + message->actual_length += dspi_tcfq_write(dspi); + } + + if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) + dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); + dspi->waitflags = 0; + + if (transfer->delay_usecs) + udelay(transfer->delay_usecs); - switch (value) { - case BITBANG_CS_ACTIVE: - pushr |= SPI_PUSHR_CONT; - case BITBANG_CS_INACTIVE: - pushr &= ~SPI_PUSHR_CONT; } - writel(pushr, dspi->base + SPI_PUSHR); + message->status = status; + spi_finalize_current_message(master); + + return status; + } -static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +static int dspi_setup(struct spi_device *spi) { struct chip_data *chip; struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); @@ -368,40 +431,47 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) return 0; } -static int dspi_setup(struct spi_device *spi) +static void dspi_cleanup(struct spi_device *spi) { - if (!spi->max_speed_hz) - return -EINVAL; + struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); - if (!spi->bits_per_word) - spi->bits_per_word = 8; + dev_dbg(&spi->dev, "spi_device %u.%u cleanup\n", + spi->master->bus_num, spi->chip_select); - return dspi_setup_transfer(spi, NULL); + kfree(chip); } static irqreturn_t dspi_interrupt(int irq, void *dev_id) { struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; - - writel(SPI_SR_EOQF, dspi->base + SPI_SR); - - dspi_transfer_read(dspi); + struct spi_message *msg = dspi->cur_msg; + + if (dspi->trans_mode == DSPI_EOQ_MODE) { + regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); + dspi_eoq_read(dspi); + } else if (dspi->trans_mode == DSPI_TCFQ_MODE) { + regmap_write(dspi->regmap, SPI_SR, SPI_SR_TCFQF); + dspi_tcfq_read(dspi); + } if (!dspi->len) { if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) - set_bit_mode(dspi, 16); + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); + dspi->waitflags = 1; wake_up_interruptible(&dspi->waitq); } else { - dspi_transfer_write(dspi); - - return IRQ_HANDLED; + if (dspi->trans_mode == DSPI_EOQ_MODE) + msg->actual_length += dspi_eoq_write(dspi); + else if (dspi->trans_mode == DSPI_TCFQ_MODE) + msg->actual_length += dspi_tcfq_write(dspi); } return IRQ_HANDLED; } -static struct of_device_id fsl_dspi_dt_ids[] = { +static const struct of_device_id fsl_dspi_dt_ids[] = { { .compatible = "fsl,vf610-dspi", .data = NULL, }, { /* sentinel */ } }; @@ -431,8 +501,13 @@ static int dspi_resume(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -static const struct dev_pm_ops dspi_pm = { - SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume) +static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); + +static struct regmap_config dspi_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x88, }; static int dspi_probe(struct platform_device *pdev) @@ -441,6 +516,7 @@ static int dspi_probe(struct platform_device *pdev) struct spi_master *master; struct fsl_dspi *dspi; struct resource *res; + void __iomem *base; int ret = 0, cs_num, bus_num; master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); @@ -449,12 +525,13 @@ static int dspi_probe(struct platform_device *pdev) dspi = spi_master_get_devdata(master); dspi->pdev = pdev; - dspi->bitbang.master = spi_master_get(master); - dspi->bitbang.chipselect = dspi_chipselect; - dspi->bitbang.setup_transfer = dspi_setup_transfer; - dspi->bitbang.txrx_bufs = dspi_txrx_transfer; - dspi->bitbang.master->setup = dspi_setup; - dspi->bitbang.master->dev.of_node = pdev->dev.of_node; + dspi->master = master; + + master->transfer = NULL; + master->cleanup = dspi_cleanup; + master->setup = dspi_setup; + master->transfer_one_message = dspi_transfer_one_message; + master->dev.of_node = pdev->dev.of_node; master->mode_bits = SPI_CPOL | SPI_CPHA; master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) | @@ -474,13 +551,31 @@ static int dspi_probe(struct platform_device *pdev) } master->bus_num = bus_num; + if (of_property_read_bool(np, "eoq-mode")) + dspi->trans_mode = DSPI_EOQ_MODE; + else if (of_property_read_bool(np, "tcfq-mode")) + dspi->trans_mode = DSPI_TCFQ_MODE; + else { + dev_err(&pdev->dev, "can't get dspi transfer mode\n"); + goto out_master_put; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dspi->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dspi->base)) { - ret = PTR_ERR(dspi->base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); goto out_master_put; } + dspi_regmap_config.lock_arg = dspi; + dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + &dspi_regmap_config); + if (IS_ERR(dspi->regmap)) { + dev_err(&pdev->dev, "failed to init regmap: %ld\n", + PTR_ERR(dspi->regmap)); + return PTR_ERR(dspi->regmap); + } + dspi->irq = platform_get_irq(pdev, 0); if (dspi->irq < 0) { dev_err(&pdev->dev, "can't get platform irq\n"); @@ -506,7 +601,7 @@ static int dspi_probe(struct platform_device *pdev) init_waitqueue_head(&dspi->waitq); platform_set_drvdata(pdev, master); - ret = spi_bitbang_start(&dspi->bitbang); + ret = spi_register_master(master); if (ret != 0) { dev_err(&pdev->dev, "Problem registering DSPI master\n"); goto out_clk_put; @@ -519,7 +614,6 @@ out_clk_put: clk_disable_unprepare(dspi->clk); out_master_put: spi_master_put(master); - platform_set_drvdata(pdev, NULL); return ret; } @@ -530,8 +624,9 @@ static int dspi_remove(struct platform_device *pdev) struct fsl_dspi *dspi = spi_master_get_devdata(master); /* Disconnect from the SPI framework */ - spi_bitbang_stop(&dspi->bitbang); - spi_master_put(dspi->bitbang.master); + clk_disable_unprepare(dspi->clk); + spi_unregister_master(dspi->master); + spi_master_put(dspi->master); return 0; } @@ -547,5 +642,5 @@ static struct platform_driver fsl_dspi_driver = { module_platform_driver(fsl_dspi_driver); MODULE_DESCRIPTION("Freescale DSPI Controller Driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 68b69fe..14c01b4 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -467,7 +467,7 @@ static int spi_gpio_probe(struct platform_device *pdev) } #endif - spi_gpio->bitbang.master = spi_master_get(master); + spi_gpio->bitbang.master = master; spi_gpio->bitbang.chipselect = spi_gpio_chipselect; if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) { @@ -486,7 +486,6 @@ static int spi_gpio_probe(struct platform_device *pdev) status = spi_bitbang_start(&spi_gpio->bitbang); if (status < 0) { - spi_master_put(spi_gpio->bitbang.master); gpio_free: if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); @@ -510,13 +509,13 @@ static int spi_gpio_remove(struct platform_device *pdev) /* stop() unregisters child devices too */ status = spi_bitbang_stop(&spi_gpio->bitbang); - spi_master_put(spi_gpio->bitbang.master); if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) gpio_free(SPI_MOSI_GPIO); gpio_free(SPI_SCK_GPIO); + spi_master_put(spi_gpio->bitbang.master); return status; } diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 9410695..954d071 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -786,7 +786,7 @@ static int spi_imx_probe(struct platform_device *pdev) master->num_chipselect = num_cs; spi_imx = spi_master_get_devdata(master); - spi_imx->bitbang.master = spi_master_get(master); + spi_imx->bitbang.master = master; for (i = 0; i < master->num_chipselect; i++) { int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index 0759b5d..41c5765 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -222,7 +222,7 @@ static void spi_lm70llp_attach(struct parport *p) /* * SPI and bitbang hookup. */ - pp->bitbang.master = spi_master_get(master); + pp->bitbang.master = master; pp->bitbang.chipselect = lm70_chipselect; pp->bitbang.txrx_word[SPI_MODE_0] = lm70_txrx; pp->bitbang.flags = SPI_3WIRE; diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index ba4e99a..8497df2 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -349,7 +349,7 @@ static int nuc900_spi_probe(struct platform_device *pdev) } hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; @@ -437,7 +437,6 @@ err_iomap: kfree(hw->ioarea); err_pdata: spi_master_put(hw->master); - err_nomem: return err; } diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index 333cb1b..91c6685 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -306,7 +306,7 @@ static int tiny_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hw); /* setup the state for the bitbang driver */ - hw->bitbang.master = spi_master_get(master); + hw->bitbang.master = master; if (!hw->bitbang.master) return err; hw->bitbang.setup_transfer = tiny_spi_setup_transfer; diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 0ee53c2..c57740bb7 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -396,7 +396,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op) master->dev.of_node = np; platform_set_drvdata(op, master); hw = spi_master_get_devdata(master); - hw->master = spi_master_get(master); + hw->master = master; hw->dev = dev; init_completion(&hw->done); @@ -558,6 +558,7 @@ static int spi_ppc4xx_of_remove(struct platform_device *op) free_irq(hw->irqnum, hw); iounmap(hw->regs); free_gpios(hw); + spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index ce318d9..c9cfdfa 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -524,7 +524,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); memset(hw, 0, sizeof(struct s3c24xx_spi)); - hw->master = spi_master_get(master); + hw->master = master; hw->pdata = pdata = dev_get_platdata(&pdev->dev); hw->dev = &pdev->dev; diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index 8eefeb6..38eb24d 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -133,7 +133,7 @@ static int sh_sci_spi_probe(struct platform_device *dev) sp->info = dev_get_platdata(&dev->dev); /* setup spi bitbang adaptor */ - sp->bitbang.master = spi_master_get(master); + sp->bitbang.master = master; sp->bitbang.master->bus_num = sp->info->bus_num; sp->bitbang.master->num_chipselect = sp->info->num_chipselect; sp->bitbang.chipselect = sh_sci_spi_chipselect; diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index a1f21b7..592b4af 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -632,7 +632,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) if (ret) goto free_master; - sspi->bitbang.master = spi_master_get(master); + sspi->bitbang.master = master; sspi->bitbang.chipselect = spi_sirfsoc_chipselect; sspi->bitbang.setup_transfer = spi_sirfsoc_setup_transfer; sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer; diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 0bf1b2c..ec3a83f 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -372,7 +372,7 @@ static int xilinx_spi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA; xspi = spi_master_get_devdata(master); - xspi->bitbang.master = spi_master_get(master); + xspi->bitbang.master = master; xspi->bitbang.chipselect = xilinx_spi_chipselect; xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; diff --git a/drivers/tdm/device/Kconfig b/drivers/tdm/device/Kconfig index af6a710..5490e1d 100644 --- a/drivers/tdm/device/Kconfig +++ b/drivers/tdm/device/Kconfig @@ -6,7 +6,7 @@ menu "TDM Device support" config TDM_FSL tristate "Driver for Freescale TDM controller" - depends on FSL_SOC + depends on FSL_SOC || ARCH_MXC ---help--- This is a driver for Freescale TDM controller. The controller is found in various Freescale SOCs viz MPC8315, P1020. The TDM driver @@ -15,7 +15,7 @@ config TDM_FSL config FSL_UCC_TDM tristate "UCC TDM driver for Freescale QE engine" - depends on FSL_SOC || CONFIG_QE + depends on FSL_SOC || QUICC_ENGINE ---help--- This is a driver for Freescale QE UCC working with TDM interface. diff --git a/drivers/tdm/device/fsl_ucc_tdm.c b/drivers/tdm/device/fsl_ucc_tdm.c index ad25c0a..a6abe71 100644 --- a/drivers/tdm/device/fsl_ucc_tdm.c +++ b/drivers/tdm/device/fsl_ucc_tdm.c @@ -29,6 +29,7 @@ #include <linux/stddef.h> #include <linux/sched.h> #include <linux/init.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/tdm.h> @@ -37,8 +38,6 @@ #include <linux/dma-mapping.h> #include <linux/spinlock.h> #include <linux/delay.h> -#include <sysdev/fsl_soc.h> -#include <sysdev/fsl_soc.h> #include <linux/slab.h> #include "fsl_ucc_tdm.h" @@ -143,27 +142,27 @@ static void dump_ucc(struct ucc_tdm_private *priv) ucc_fast_dump_regs(priv->uccf); dev_info(priv->dev, "Dumping UCC %d Parameter RAM\n", priv->ut_info->uf_info.ucc_num); - dev_info(priv->dev, "rbase = 0x%x\n", in_be32(&ucc_pram->rbase)); - dev_info(priv->dev, "rbptr = 0x%x\n", in_be32(&ucc_pram->rbptr)); - dev_info(priv->dev, "mrblr = 0x%x\n", in_be16(&ucc_pram->mrblr)); - dev_info(priv->dev, "rbdlen = 0x%x\n", in_be16(&ucc_pram->rbdlen)); - dev_info(priv->dev, "rbdstat = 0x%x\n", in_be16(&ucc_pram->rbdstat)); - dev_info(priv->dev, "rstate = 0x%x\n", in_be32(&ucc_pram->rstate)); - dev_info(priv->dev, "rdptr = 0x%x\n", in_be32(&ucc_pram->rdptr)); - dev_info(priv->dev, "riptr = 0x%x\n", in_be16(&ucc_pram->riptr)); - dev_info(priv->dev, "tbase = 0x%x\n", in_be32(&ucc_pram->tbase)); - dev_info(priv->dev, "tbptr = 0x%x\n", in_be32(&ucc_pram->tbptr)); - dev_info(priv->dev, "tbdlen = 0x%x\n", in_be16(&ucc_pram->tbdlen)); - dev_info(priv->dev, "tbdstat = 0x%x\n", in_be16(&ucc_pram->tbdstat)); - dev_info(priv->dev, "tstate = 0x%x\n", in_be32(&ucc_pram->tstate)); - dev_info(priv->dev, "tdptr = 0x%x\n", in_be32(&ucc_pram->tdptr)); - dev_info(priv->dev, "tiptr = 0x%x\n", in_be16(&ucc_pram->tiptr)); - dev_info(priv->dev, "rcrc = 0x%x\n", in_be32(&ucc_pram->rcrc)); - dev_info(priv->dev, "tcrc = 0x%x\n", in_be32(&ucc_pram->tcrc)); - dev_info(priv->dev, "c_mask = 0x%x\n", in_be32(&ucc_pram->c_mask)); - dev_info(priv->dev, "c_pers = 0x%x\n", in_be32(&ucc_pram->c_pres)); - dev_info(priv->dev, "disfc = 0x%x\n", in_be16(&ucc_pram->disfc)); - dev_info(priv->dev, "crcec = 0x%x\n", in_be16(&ucc_pram->crcec)); + dev_info(priv->dev, "rbase = 0x%x\n", ioread32be(&ucc_pram->rbase)); + dev_info(priv->dev, "rbptr = 0x%x\n", ioread32be(&ucc_pram->rbptr)); + dev_info(priv->dev, "mrblr = 0x%x\n", ioread16be(&ucc_pram->mrblr)); + dev_info(priv->dev, "rbdlen = 0x%x\n", ioread16be(&ucc_pram->rbdlen)); + dev_info(priv->dev, "rbdstat = 0x%x\n", ioread16be(&ucc_pram->rbdstat)); + dev_info(priv->dev, "rstate = 0x%x\n", ioread32be(&ucc_pram->rstate)); + dev_info(priv->dev, "rdptr = 0x%x\n", ioread32be(&ucc_pram->rdptr)); + dev_info(priv->dev, "riptr = 0x%x\n", ioread16be(&ucc_pram->riptr)); + dev_info(priv->dev, "tbase = 0x%x\n", ioread32be(&ucc_pram->tbase)); + dev_info(priv->dev, "tbptr = 0x%x\n", ioread32be(&ucc_pram->tbptr)); + dev_info(priv->dev, "tbdlen = 0x%x\n", ioread16be(&ucc_pram->tbdlen)); + dev_info(priv->dev, "tbdstat = 0x%x\n", ioread16be(&ucc_pram->tbdstat)); + dev_info(priv->dev, "tstate = 0x%x\n", ioread32be(&ucc_pram->tstate)); + dev_info(priv->dev, "tdptr = 0x%x\n", ioread32be(&ucc_pram->tdptr)); + dev_info(priv->dev, "tiptr = 0x%x\n", ioread16be(&ucc_pram->tiptr)); + dev_info(priv->dev, "rcrc = 0x%x\n", ioread32be(&ucc_pram->rcrc)); + dev_info(priv->dev, "tcrc = 0x%x\n", ioread32be(&ucc_pram->tcrc)); + dev_info(priv->dev, "c_mask = 0x%x\n", ioread32be(&ucc_pram->c_mask)); + dev_info(priv->dev, "c_pers = 0x%x\n", ioread32be(&ucc_pram->c_pres)); + dev_info(priv->dev, "disfc = 0x%x\n", ioread16be(&ucc_pram->disfc)); + dev_info(priv->dev, "crcec = 0x%x\n", ioread16be(&ucc_pram->crcec)); } static void dump_bds(struct ucc_tdm_private *priv) @@ -243,18 +242,18 @@ static void init_si(struct ucc_tdm_private *priv) mask = 0x01 << i; if (priv->tx_ts_mask & mask) - out_be16(&siram[siram_entry_id * 32 + i], - siram_entry_valid); + iowrite16be(siram_entry_valid, + &siram[siram_entry_id * 32 + i]); else - out_be16(&siram[siram_entry_id * 32 + i], - siram_entry_closed); + iowrite16be(siram_entry_closed, + &siram[siram_entry_id * 32 + i]); if (priv->rx_ts_mask & mask) - out_be16(&siram[siram_entry_id * 32 + 0x200 + i], - siram_entry_valid); + iowrite16be(siram_entry_valid, + &siram[siram_entry_id * 32 + 0x200 + i]); else - out_be16(&siram[siram_entry_id * 32 + 0x200 + i], - siram_entry_closed); + iowrite16be(siram_entry_closed, + &siram[siram_entry_id * 32 + 0x200 + i]); } setbits16(&siram[(siram_entry_id * 32) + (priv->num_of_ts - 1)], @@ -288,16 +287,16 @@ static void init_si(struct ucc_tdm_private *priv) switch (tdm_port) { case 0: - out_be16(&si_regs->sixmr1[0], sixmr); + iowrite16be(sixmr, &si_regs->sixmr1[0]); break; case 1: - out_be16(&si_regs->sixmr1[1], sixmr); + iowrite16be(sixmr, &si_regs->sixmr1[1]); break; case 2: - out_be16(&si_regs->sixmr1[2], sixmr); + iowrite16be(sixmr, &si_regs->sixmr1[2]); break; case 3: - out_be16(&si_regs->sixmr1[3], sixmr); + iowrite16be(sixmr, &si_regs->sixmr1[3]); break; default: dev_err(priv->dev, "can not find tdm sixmr reg\n"); @@ -348,7 +347,7 @@ static int utdm_init(struct ucc_tdm_private *priv) (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0); /* Set UPSMR normal mode */ - out_be32(&priv->uf_regs->upsmr, 0); + iowrite32be(0, &priv->uf_regs->upsmr); priv->tx_bd = dma_alloc_coherent(priv->dev, NUM_OF_BUF * MAX_RX_BUF_LENGTH, @@ -408,27 +407,27 @@ static int utdm_init(struct ucc_tdm_private *priv) } /* Set RIPTR, TIPTR */ - out_be16(&priv->ucc_pram->riptr, (u16)riptr); - out_be16(&priv->ucc_pram->tiptr, (u16)tiptr); + iowrite16be((u16)riptr, &priv->ucc_pram->riptr); + iowrite16be((u16)tiptr, &priv->ucc_pram->tiptr); /* Set MRBLR */ - out_be16(&priv->ucc_pram->mrblr, (u16)MAX_RX_BUF_LENGTH); + iowrite16be((u16)MAX_RX_BUF_LENGTH, &priv->ucc_pram->mrblr); /* Set RBASE, TBASE */ - out_be32(&priv->ucc_pram->rbase, (u32)priv->dma_rx_bd); - out_be32(&priv->ucc_pram->tbase, (u32)priv->dma_tx_bd); + iowrite32be((u32)priv->dma_rx_bd, &priv->ucc_pram->rbase); + iowrite32be((u32)priv->dma_tx_bd, &priv->ucc_pram->tbase); /* Set RSTATE, TSTATE */ - out_be32(&priv->ucc_pram->rstate, 0x30000000); - out_be32(&priv->ucc_pram->tstate, 0x30000000); + iowrite32be(0x30000000, &priv->ucc_pram->rstate); + iowrite32be(0x30000000, &priv->ucc_pram->tstate); /* Set C_MASK, C_PRES for 16bit CRC */ - out_be32(&priv->ucc_pram->c_mask, 0x0000F0B8); - out_be32(&priv->ucc_pram->c_pres, 0x0000FFFF); + iowrite32be(0x0000F0B8, &priv->ucc_pram->c_mask); + iowrite32be(0x0000FFFF, &priv->ucc_pram->c_pres); - out_be16(&priv->ucc_pram->res0, 0); + iowrite16be(0, &priv->ucc_pram->res0); for (i = 0; i < 4; i++) - out_be32(&priv->ucc_pram->res4[i], 0x0); + iowrite32be(0x0, &priv->ucc_pram->res4[i]); /* Get BD buffer */ bd_buffer = dma_alloc_coherent(priv->dev, @@ -454,18 +453,18 @@ static int utdm_init(struct ucc_tdm_private *priv) else bd_status = R_E | R_I | R_W | R_CM; - out_be32((u32 *)(priv->rx_bd + i), bd_status); - out_be32(&priv->rx_bd[i].buf, priv->dma_rx_addr - + i * MAX_RX_BUF_LENGTH); + iowrite32be(bd_status, (u32 *)(priv->rx_bd + i)); + iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH, + &priv->rx_bd[i].buf); if (i < (NUM_OF_BUF - 1)) bd_status = T_I; else bd_status = T_I | T_W; - out_be32((u32 *)(priv->tx_bd + i), bd_status); - out_be32(&priv->tx_bd[i].buf, priv->dma_tx_addr - + i * MAX_RX_BUF_LENGTH); + iowrite32be(bd_status, (u32 *)(priv->tx_bd + i)); + iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH, + &priv->tx_bd[i].buf); } priv->phase_rx = 0; @@ -546,7 +545,7 @@ static int ucc_tdm_write(struct tdm_adapter *adap, u8 *write_buf, priv->phase_tx = 0; bd = (priv->tx_bd + priv->phase_tx); - bd_stat_len = in_be32((u32 __iomem *)bd); + bd_stat_len = ioread32be((u32 __iomem *)bd); tdm_send_buf = priv->tx_buffer + priv->phase_tx * MAX_RX_BUF_LENGTH; @@ -557,7 +556,7 @@ static int ucc_tdm_write(struct tdm_adapter *adap, u8 *write_buf, copy_len = MAX_RX_BUF_LENGTH; ret = spin_event_timeout(((bd_stat_len = - in_be32((u32 __iomem *)bd)) & T_R) != T_R , + ioread32be((u32 __iomem *)bd)) & T_R) != T_R , 1000000, 500); if (!ret) { dev_err(priv->dev, "TDM write data error!\n"); @@ -570,11 +569,11 @@ static int ucc_tdm_write(struct tdm_adapter *adap, u8 *write_buf, bd_stat_len &= ~(T_L | BD_LEN_MASK); if (i == (buf_num - 1)) - out_be32((u32 __iomem *)(bd), - bd_stat_len | T_R | T_L | T_I | copy_len); + iowrite32be(bd_stat_len | T_R | T_L | T_I | copy_len, + (u32 __iomem *)(bd)); else - out_be32((u32 __iomem *)(bd), - bd_stat_len | T_R | T_I | copy_len); + iowrite32be(bd_stat_len | T_R | T_I | copy_len, + (u32 __iomem *)(bd)); priv->phase_tx++; } @@ -593,8 +592,8 @@ static irqreturn_t ucc_tdm_irq_handler(int irq, void *dev_id) ut_info = priv->ut_info; uccf = priv->uccf; - ucce = in_be32(uccf->p_ucce); - uccm = in_be32(uccf->p_uccm); + ucce = ioread32be(uccf->p_ucce); + uccm = ioread32be(uccf->p_uccm); if ((ucce >> 16) & UCC_TRANS_UCCE_RXB) { if (priv->phase_rx == NUM_OF_BUF - 1) @@ -607,7 +606,7 @@ static irqreturn_t ucc_tdm_irq_handler(int irq, void *dev_id) } - out_be32(uccf->p_ucce, ucce); + iowrite32be(ucce, uccf->p_ucce); return IRQ_HANDLED; @@ -763,18 +762,18 @@ static int ucc_tdm_probe(struct platform_device *pdev) struct ucc_tdm_info *ut_info; struct resource res; int ucc_num; - const unsigned int *prop; + u32 val; const char *sprop; struct device_node *np2; int ret; - prop = of_get_property(np, "cell-index", NULL); - if (!prop) { + ret = of_property_read_u32_index(np, "cell-index", 0, &val); + if (ret) { dev_err(&pdev->dev, "Invalid ucc property\n"); return -ENODEV; } - ucc_num = *prop - 1; + ucc_num = val - 1; if ((ucc_num > 7) || (ucc_num < 0)) { dev_err(&pdev->dev, ": Invalid UCC num\n"); return -EINVAL; @@ -859,54 +858,54 @@ static int ucc_tdm_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, utdm_priv); utdm_priv->dev = &pdev->dev; - prop = of_get_property(np, "fsl,tx-timeslot", NULL); - if (!prop) { + ret = of_property_read_u32_index(np, "fsl,tx-timeslot", 0, &val); + if (ret) { ret = -EINVAL; dev_err(&pdev->dev, "Invalid tx-timeslot property\n"); goto err_miss_property; } - utdm_priv->tx_ts_mask = *prop; + utdm_priv->tx_ts_mask = val; - prop = of_get_property(np, "fsl,rx-timeslot", NULL); - if (!prop) { + ret = of_property_read_u32_index(np, "fsl,rx-timeslot", 0, &val); + if (ret) { ret = -EINVAL; dev_err(&pdev->dev, "Invalid rx-timeslot property\n"); goto err_miss_property; } - utdm_priv->rx_ts_mask = *prop; + utdm_priv->rx_ts_mask = val; - prop = of_get_property(np, "fsl,tdm-id", NULL); - if (!prop) { + ret = of_property_read_u32_index(np, "fsl,tdm-id", 0, &val); + if (ret) { ret = -EINVAL; dev_err(&pdev->dev, "No fsl,tdm-id property for this UCC\n"); goto err_miss_property; } - utdm_priv->tdm_port = *prop; + utdm_priv->tdm_port = val; ut_info->uf_info.tdm_num = utdm_priv->tdm_port ; - prop = of_get_property(np, "fsl,tdm-mode", NULL); - if (!prop) { + sprop = of_get_property(np, "fsl,tdm-mode", NULL); + if (!sprop) { ret = -EINVAL; dev_err(&pdev->dev, "No tdm-mode property for UCC\n"); goto err_miss_property; } - utdm_priv->tdm_mode = set_tdm_mode((const char *)prop); + utdm_priv->tdm_mode = set_tdm_mode(sprop); - prop = of_get_property(np, "fsl,tdm-framer-type", NULL); - if (!prop) { + sprop = of_get_property(np, "fsl,tdm-framer-type", NULL); + if (!sprop) { ret = -EINVAL; dev_err(&pdev->dev, "No tdm-framer-type property for UCC\n"); goto err_miss_property; } - utdm_priv->tdm_framer_type = set_tdm_framer((const char *)prop); + utdm_priv->tdm_framer_type = set_tdm_framer(sprop); - prop = of_get_property(np, "fsl,siram-entry-id", NULL); - if (!prop) { + ret = of_property_read_u32_index(np, "fsl,siram-entry-id", 0, &val); + if (ret) { ret = -EINVAL; dev_err(&pdev->dev, "No siram entry id for UCC\n"); goto err_miss_property; } - utdm_priv->siram_entry_id = *(const u32 *)prop; + utdm_priv->siram_entry_id = val; np2 = of_find_node_by_name(NULL, "si"); if (!np2) { @@ -1008,11 +1007,11 @@ static void store_clk_config(struct ucc_tdm_private *utdm_priv) struct qe_mux *qe_mux_reg = &qe_immr->qmx; /* store si clk */ - utdm_priv->cmxsi1cr_h = in_be32(&qe_mux_reg->cmxsi1cr_h); - utdm_priv->cmxsi1cr_l = in_be32(&qe_mux_reg->cmxsi1cr_l); + utdm_priv->cmxsi1cr_h = ioread32be(&qe_mux_reg->cmxsi1cr_h); + utdm_priv->cmxsi1cr_l = ioread32be(&qe_mux_reg->cmxsi1cr_l); /* store si sync */ - utdm_priv->cmxsi1syr = in_be32(&qe_mux_reg->cmxsi1syr); + utdm_priv->cmxsi1syr = ioread32be(&qe_mux_reg->cmxsi1syr); /* store ucc clk */ memcpy_fromio(utdm_priv->cmxucr, qe_mux_reg->cmxucr, 4 * sizeof(u32)); @@ -1024,10 +1023,10 @@ static void resume_clk_config(struct ucc_tdm_private *utdm_priv) memcpy_toio(qe_mux_reg->cmxucr, utdm_priv->cmxucr, 4 * sizeof(u32)); - out_be32(&qe_mux_reg->cmxsi1cr_h, utdm_priv->cmxsi1cr_h); - out_be32(&qe_mux_reg->cmxsi1cr_l, utdm_priv->cmxsi1cr_l); + iowrite32be(utdm_priv->cmxsi1cr_h, &qe_mux_reg->cmxsi1cr_h); + iowrite32be(utdm_priv->cmxsi1cr_l, &qe_mux_reg->cmxsi1cr_l); - out_be32(&qe_mux_reg->cmxsi1syr, utdm_priv->cmxsi1syr); + iowrite32be(utdm_priv->cmxsi1syr, &qe_mux_reg->cmxsi1syr); } @@ -1044,8 +1043,8 @@ static int ucc_tdm_suspend(struct device *dev) uf_regs = utdm_priv->uf_regs; /* backup gumr guemr*/ - utdm_priv->gumr = in_be32(&uf_regs->gumr); - utdm_priv->guemr = in_8(&uf_regs->guemr); + utdm_priv->gumr = ioread32be(&uf_regs->gumr); + utdm_priv->guemr = ioread8(&uf_regs->guemr); utdm_priv->ucc_pram_bak = kmalloc(sizeof(struct ucc_transparent_param), GFP_KERNEL); @@ -1086,25 +1085,25 @@ static int ucc_tdm_resume(struct device *dev) uccf = utdm_priv->uccf; /* restore gumr guemr */ - out_8(&uf_regs->guemr, utdm_priv->guemr); - out_be32(&uf_regs->gumr, utdm_priv->gumr); + iowrite8(utdm_priv->guemr, &uf_regs->guemr); + iowrite32be(utdm_priv->gumr, &uf_regs->gumr); /* Set Virtual Fifo registers */ - out_be16(&uf_regs->urfs, uf_info->urfs); - out_be16(&uf_regs->urfet, uf_info->urfet); - out_be16(&uf_regs->urfset, uf_info->urfset); - out_be16(&uf_regs->utfs, uf_info->utfs); - out_be16(&uf_regs->utfet, uf_info->utfet); - out_be16(&uf_regs->utftt, uf_info->utftt); + iowrite16be(uf_info->urfs, &uf_regs->urfs); + iowrite16be(uf_info->urfet, &uf_regs->urfet); + iowrite16be(uf_info->urfset, &uf_regs->urfset); + iowrite16be(uf_info->utfs, &uf_regs->utfs); + iowrite16be(uf_info->utfet, &uf_regs->utfet); + iowrite16be(uf_info->utftt, &uf_regs->utftt); /* utfb, urfb are offsets from MURAM base */ - out_be32(&uf_regs->utfb, uccf->ucc_fast_tx_virtual_fifo_base_offset); - out_be32(&uf_regs->urfb, uccf->ucc_fast_rx_virtual_fifo_base_offset); + iowrite32be(uccf->ucc_fast_tx_virtual_fifo_base_offset, &uf_regs->utfb); + iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->urfb); /* tdm Rx Tx and sync clock routing */ resume_clk_config(utdm_priv); - out_be32(&uf_regs->uccm, uf_info->uccm_mask); - out_be32(&uf_regs->ucce, 0xffffffff); + iowrite32be(uf_info->uccm_mask, &uf_regs->uccm); + iowrite32be(0xffffffff, &uf_regs->ucce); ucc_fast_disable(utdm_priv->uccf, COMM_DIR_RX | COMM_DIR_TX); @@ -1117,7 +1116,7 @@ static int ucc_tdm_resume(struct device *dev) (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); /* Set UPSMR normal mode */ - out_be32(&uf_regs->upsmr, 0); + iowrite32be(0, &uf_regs->upsmr); /* init parameter base */ cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); @@ -1139,18 +1138,18 @@ static int ucc_tdm_resume(struct device *dev) else bd_status = R_E | R_I | R_W | R_CM; - out_be32((u32 *)(utdm_priv->rx_bd + i), bd_status); - out_be32(&utdm_priv->rx_bd[i].buf, utdm_priv->dma_rx_addr - + i * MAX_RX_BUF_LENGTH); + iowrite32be(bd_status, (u32 *)(utdm_priv->rx_bd + i)); + iowrite32be(utdm_priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH, + &utdm_priv->rx_bd[i].buf); if (i < (NUM_OF_BUF - 1)) bd_status = T_I; else bd_status = T_I | T_W; - out_be32((u32 *)(utdm_priv->tx_bd + i), bd_status); - out_be32(&utdm_priv->tx_bd[i].buf, utdm_priv->dma_tx_addr - + i * MAX_RX_BUF_LENGTH); + iowrite32be(bd_status, (u32 *)(utdm_priv->tx_bd + i)); + iowrite32be(utdm_priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH, + &utdm_priv->tx_bd[i].buf); } /* if tdm is busy enable TX and RX */ diff --git a/drivers/tdm/device/fsl_ucc_tdm.h b/drivers/tdm/device/fsl_ucc_tdm.h index 1a1b161..a1213fa 100644 --- a/drivers/tdm/device/fsl_ucc_tdm.h +++ b/drivers/tdm/device/fsl_ucc_tdm.h @@ -31,11 +31,11 @@ #include <linux/kernel.h> #include <linux/list.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> -#include <asm/ucc.h> -#include <asm/ucc_fast.h> +#include <linux/fsl/ucc.h> +#include <linux/fsl/ucc_fast.h> /* SI RAM entries */ #define SIR_LAST 0x0001 diff --git a/drivers/tdm/test/Kconfig b/drivers/tdm/test/Kconfig index 39f9581..243c934 100644 --- a/drivers/tdm/test/Kconfig +++ b/drivers/tdm/test/Kconfig @@ -14,7 +14,7 @@ config TDM_TEST config UCC_TDM_TEST tristate "UCC TDM test Module" - depends on FSL_SOC || CONFIG_QE + depends on FSL_SOC || QUICC_ENGINE ---help--- This UCC TDM test module is a small test module to test ucc tdm transfer and receive data via ucc1. diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 8978dc9..1c5d020 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1,7 +1,7 @@ /* * Freescale lpuart serial port driver * - * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2012-2014 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 @@ -112,6 +112,113 @@ #define UARTSFIFO_TXOF 0x02 #define UARTSFIFO_RXUF 0x01 +/* 32-bit register defination */ +#define UARTBAUD 0x00 +#define UARTSTAT 0x04 +#define UARTCTRL 0x08 +#define UARTDATA 0x0C +#define UARTMATCH 0x10 +#define UARTMODIR 0x14 +#define UARTFIFO 0x18 +#define UARTWATER 0x1c + +#define UARTBAUD_MAEN1 0x80000000 +#define UARTBAUD_MAEN2 0x40000000 +#define UARTBAUD_M10 0x20000000 +#define UARTBAUD_TDMAE 0x00800000 +#define UARTBAUD_RDMAE 0x00200000 +#define UARTBAUD_MATCFG 0x00400000 +#define UARTBAUD_BOTHEDGE 0x00020000 +#define UARTBAUD_RESYNCDIS 0x00010000 +#define UARTBAUD_LBKDIE 0x00008000 +#define UARTBAUD_RXEDGIE 0x00004000 +#define UARTBAUD_SBNS 0x00002000 +#define UARTBAUD_SBR 0x00000000 +#define UARTBAUD_SBR_MASK 0x1fff + +#define UARTSTAT_LBKDIF 0x80000000 +#define UARTSTAT_RXEDGIF 0x40000000 +#define UARTSTAT_MSBF 0x20000000 +#define UARTSTAT_RXINV 0x10000000 +#define UARTSTAT_RWUID 0x08000000 +#define UARTSTAT_BRK13 0x04000000 +#define UARTSTAT_LBKDE 0x02000000 +#define UARTSTAT_RAF 0x01000000 +#define UARTSTAT_TDRE 0x00800000 +#define UARTSTAT_TC 0x00400000 +#define UARTSTAT_RDRF 0x00200000 +#define UARTSTAT_IDLE 0x00100000 +#define UARTSTAT_OR 0x00080000 +#define UARTSTAT_NF 0x00040000 +#define UARTSTAT_FE 0x00020000 +#define UARTSTAT_PE 0x00010000 +#define UARTSTAT_MA1F 0x00008000 +#define UARTSTAT_M21F 0x00004000 + +#define UARTCTRL_R8T9 0x80000000 +#define UARTCTRL_R9T8 0x40000000 +#define UARTCTRL_TXDIR 0x20000000 +#define UARTCTRL_TXINV 0x10000000 +#define UARTCTRL_ORIE 0x08000000 +#define UARTCTRL_NEIE 0x04000000 +#define UARTCTRL_FEIE 0x02000000 +#define UARTCTRL_PEIE 0x01000000 +#define UARTCTRL_TIE 0x00800000 +#define UARTCTRL_TCIE 0x00400000 +#define UARTCTRL_RIE 0x00200000 +#define UARTCTRL_ILIE 0x00100000 +#define UARTCTRL_TE 0x00080000 +#define UARTCTRL_RE 0x00040000 +#define UARTCTRL_RWU 0x00020000 +#define UARTCTRL_SBK 0x00010000 +#define UARTCTRL_MA1IE 0x00008000 +#define UARTCTRL_MA2IE 0x00004000 +#define UARTCTRL_IDLECFG 0x00000100 +#define UARTCTRL_LOOPS 0x00000080 +#define UARTCTRL_DOZEEN 0x00000040 +#define UARTCTRL_RSRC 0x00000020 +#define UARTCTRL_M 0x00000010 +#define UARTCTRL_WAKE 0x00000008 +#define UARTCTRL_ILT 0x00000004 +#define UARTCTRL_PE 0x00000002 +#define UARTCTRL_PT 0x00000001 + +#define UARTDATA_NOISY 0x00008000 +#define UARTDATA_PARITYE 0x00004000 +#define UARTDATA_FRETSC 0x00002000 +#define UARTDATA_RXEMPT 0x00001000 +#define UARTDATA_IDLINE 0x00000800 +#define UARTDATA_MASK 0x3ff + +#define UARTMODIR_IREN 0x00020000 +#define UARTMODIR_TXCTSSRC 0x00000020 +#define UARTMODIR_TXCTSC 0x00000010 +#define UARTMODIR_RXRTSE 0x00000008 +#define UARTMODIR_TXRTSPOL 0x00000004 +#define UARTMODIR_TXRTSE 0x00000002 +#define UARTMODIR_TXCTSE 0x00000001 + +#define UARTFIFO_TXEMPT 0x00800000 +#define UARTFIFO_RXEMPT 0x00400000 +#define UARTFIFO_TXOF 0x00020000 +#define UARTFIFO_RXUF 0x00010000 +#define UARTFIFO_TXFLUSH 0x00008000 +#define UARTFIFO_RXFLUSH 0x00004000 +#define UARTFIFO_TXOFE 0x00000200 +#define UARTFIFO_RXUFE 0x00000100 +#define UARTFIFO_TXFE 0x00000080 +#define UARTFIFO_FIFOSIZE_MASK 0x7 +#define UARTFIFO_TXSIZE_OFF 4 +#define UARTFIFO_RXFE 0x00000008 +#define UARTFIFO_RXSIZE_OFF 0 + +#define UARTWATER_COUNT_MASK 0xff +#define UARTWATER_TXCNT_OFF 8 +#define UARTWATER_RXCNT_OFF 24 +#define UARTWATER_WATER_MASK 0xff +#define UARTWATER_TXWATER_OFF 0 +#define UARTWATER_RXWATER_OFF 16 + #define DRIVER_NAME "fsl-lpuart" #define DEV_NAME "ttyLP" #define UART_NR 6 @@ -121,16 +228,30 @@ struct lpuart_port { struct clk *clk; unsigned int txfifo_size; unsigned int rxfifo_size; + bool lpuart32; }; static struct of_device_id lpuart_dt_ids[] = { { .compatible = "fsl,vf610-lpuart", }, + { + .compatible = "fsl,ls1021a-lpuart", + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lpuart_dt_ids); +static u32 lpuart32_read(void __iomem *addr) +{ + return ioread32be(addr); +} + +static void lpuart32_write(u32 val, void __iomem *addr) +{ + iowrite32be(val, addr); +} + static void lpuart_stop_tx(struct uart_port *port) { unsigned char temp; @@ -140,6 +261,15 @@ static void lpuart_stop_tx(struct uart_port *port) writeb(temp, port->membase + UARTCR2); } +static void lpuart32_stop_tx(struct uart_port *port) +{ + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTCTRL); + temp &= ~(UARTCTRL_TIE | UARTCTRL_TCIE); + lpuart32_write(temp, port->membase + UARTCTRL); +} + static void lpuart_stop_rx(struct uart_port *port) { unsigned char temp; @@ -148,6 +278,14 @@ static void lpuart_stop_rx(struct uart_port *port) writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2); } +static void lpuart32_stop_rx(struct uart_port *port) +{ + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTCTRL); + lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL); +} + static void lpuart_enable_ms(struct uart_port *port) { } @@ -170,6 +308,30 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport) lpuart_stop_tx(&sport->port); } +static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long txcnt; + + txcnt = lpuart32_read(sport->port.membase + UARTWATER); + txcnt = txcnt >> UARTWATER_TXCNT_OFF; + txcnt &= UARTWATER_COUNT_MASK; + while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) { + lpuart32_write(xmit->buf[xmit->tail], sport->port.membase + UARTDATA); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + txcnt = lpuart32_read(sport->port.membase + UARTWATER); + txcnt = txcnt >> UARTWATER_TXCNT_OFF; + txcnt &= UARTWATER_COUNT_MASK; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + lpuart32_stop_tx(&sport->port); +} + static void lpuart_start_tx(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -182,6 +344,18 @@ static void lpuart_start_tx(struct uart_port *port) lpuart_transmit_buffer(sport); } +static void lpuart32_start_tx(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTCTRL); + lpuart32_write(temp | UARTCTRL_TIE, port->membase + UARTCTRL); + + if (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE) + lpuart32_transmit_buffer(sport); +} + static irqreturn_t lpuart_txint(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -190,16 +364,25 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id) spin_lock_irqsave(&sport->port.lock, flags); if (sport->port.x_char) { - writeb(sport->port.x_char, sport->port.membase + UARTDR); + if (sport->lpuart32) + lpuart32_write(sport->port.x_char, sport->port.membase + UARTDATA); + else + writeb(sport->port.x_char, sport->port.membase + UARTDR); goto out; } if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - lpuart_stop_tx(&sport->port); + if (sport->lpuart32) + lpuart32_stop_tx(&sport->port); + else + lpuart_stop_tx(&sport->port); goto out; } - lpuart_transmit_buffer(sport); + if (sport->lpuart32) + lpuart32_transmit_buffer(sport); + else + lpuart_transmit_buffer(sport); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); @@ -272,6 +455,70 @@ out: return IRQ_HANDLED; } +static irqreturn_t lpuart32_rxint(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned int flg, ignored = 0; + struct tty_port *port = &sport->port.state->port; + unsigned long flags; + unsigned long rx, sr; + + spin_lock_irqsave(&sport->port.lock, flags); + + while (!(lpuart32_read(sport->port.membase + UARTFIFO) & UARTFIFO_RXEMPT)) { + flg = TTY_NORMAL; + sport->port.icount.rx++; + /* + * to clear the FE, OR, NF, FE, PE flags, + * read STAT then read DATA reg + */ + sr = lpuart32_read(sport->port.membase + UARTSTAT); + rx = lpuart32_read(sport->port.membase + UARTDATA); + rx &= 0x3ff; + + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) + continue; + + if (sr & (UARTSTAT_PE | UARTSTAT_OR | UARTSTAT_FE)) { + if (sr & UARTSTAT_PE) + sport->port.icount.parity++; + else if (sr & UARTSTAT_FE) + sport->port.icount.frame++; + + if (sr & UARTSTAT_OR) + sport->port.icount.overrun++; + + if (sr & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + continue; + } + + sr &= sport->port.read_status_mask; + + if (sr & UARTSTAT_PE) + flg = TTY_PARITY; + else if (sr & UARTSTAT_FE) + flg = TTY_FRAME; + + if (sr & UARTSTAT_OR) + flg = TTY_OVERRUN; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + tty_insert_flip_char(port, rx, flg); + } + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + + tty_flip_buffer_push(port); + return IRQ_HANDLED; +} + static irqreturn_t lpuart_int(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -289,6 +536,26 @@ static irqreturn_t lpuart_int(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t lpuart32_int(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned long sts, rxcount; + + sts = lpuart32_read(sport->port.membase + UARTSTAT); + rxcount = lpuart32_read(sport->port.membase + UARTWATER); + rxcount = rxcount >> UARTWATER_RXCNT_OFF; + + if (sts & UARTSTAT_RDRF || rxcount > 0) + lpuart32_rxint(irq, dev_id); + + if ((sts & UARTSTAT_TDRE) && + !(lpuart32_read(sport->port.membase + UARTBAUD) & UARTBAUD_TDMAE)) + lpuart_txint(irq, dev_id); + + lpuart32_write(sts, sport->port.membase + UARTSTAT); + return IRQ_HANDLED; +} + /* return TIOCSER_TEMT when transmitter is not busy */ static unsigned int lpuart_tx_empty(struct uart_port *port) { @@ -296,6 +563,12 @@ static unsigned int lpuart_tx_empty(struct uart_port *port) TIOCSER_TEMT : 0; } +static unsigned int lpuart32_tx_empty(struct uart_port *port) +{ + return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ? + TIOCSER_TEMT : 0; +} + static unsigned int lpuart_get_mctrl(struct uart_port *port) { unsigned int temp = 0; @@ -311,6 +584,21 @@ static unsigned int lpuart_get_mctrl(struct uart_port *port) return temp; } +static unsigned int lpuart32_get_mctrl(struct uart_port *port) +{ + unsigned int temp = 0; + unsigned long reg; + + reg = lpuart32_read(port->membase + UARTMODIR); + if (reg & UARTMODIR_TXCTSE) + temp |= TIOCM_CTS; + + if (reg & UARTMODIR_RXRTSE) + temp |= TIOCM_RTS; + + return temp; +} + static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned char temp; @@ -327,6 +615,22 @@ static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) writeb(temp, port->membase + UARTMODEM); } +static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTMODIR) & + ~(UARTMODIR_RXRTSE | UARTMODIR_TXCTSE); + + if (mctrl & TIOCM_RTS) + temp |= UARTMODIR_RXRTSE; + + if (mctrl & TIOCM_CTS) + temp |= UARTMODIR_TXCTSE; + + lpuart32_write(temp, port->membase + UARTMODIR); +} + static void lpuart_break_ctl(struct uart_port *port, int break_state) { unsigned char temp; @@ -339,6 +643,18 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state) writeb(temp, port->membase + UARTCR2); } +static void lpuart32_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long temp; + + temp = lpuart32_read(port->membase + UARTCTRL) & ~UARTCTRL_SBK; + + if (break_state != 0) + temp |= UARTCTRL_SBK; + + lpuart32_write(temp, port->membase + UARTCTRL); +} + static void lpuart_setup_watermark(struct lpuart_port *sport) { unsigned char val, cr2; @@ -373,6 +689,31 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) writeb(cr2_saved, sport->port.membase + UARTCR2); } +static void lpuart32_setup_watermark(struct lpuart_port *sport) +{ + unsigned long val, ctrl; + unsigned long ctrl_saved; + + ctrl = lpuart32_read(sport->port.membase + UARTCTRL); + ctrl_saved = ctrl; + ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE | + UARTCTRL_RIE | UARTCTRL_RE); + lpuart32_write(ctrl, sport->port.membase + UARTCTRL); + + /* enable FIFO mode */ + val = lpuart32_read(sport->port.membase + UARTFIFO); + val |= UARTFIFO_TXFE | UARTFIFO_RXFE; + val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; + lpuart32_write(val, sport->port.membase + UARTFIFO); + + /* set the watermark */ + val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF); + lpuart32_write(val, sport->port.membase + UARTWATER); + + /* Restore cr2 */ + lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL); +} + static int lpuart_startup(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -397,6 +738,40 @@ static int lpuart_startup(struct uart_port *port) return 0; } +static int lpuart32_startup(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + int ret; + unsigned long flags; + unsigned long temp; + + /* determine FIFO size */ + temp = lpuart32_read(sport->port.membase + UARTFIFO); + + sport->txfifo_size = 0x1 << (((temp >> UARTFIFO_TXSIZE_OFF) & + UARTFIFO_FIFOSIZE_MASK) - 1); + + sport->rxfifo_size = 0x1 << (((temp >> UARTFIFO_RXSIZE_OFF) & + UARTFIFO_FIFOSIZE_MASK) - 1); + + ret = devm_request_irq(port->dev, port->irq, lpuart32_int, 0, + DRIVER_NAME, sport); + if (ret) + return ret; + + spin_lock_irqsave(&sport->port.lock, flags); + + lpuart32_setup_watermark(sport); + + temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE); + temp |= UARTCTRL_ILIE; + lpuart32_write(temp, sport->port.membase + UARTCTRL); + + spin_unlock_irqrestore(&sport->port.lock, flags); + return 0; +} + static void lpuart_shutdown(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); @@ -416,6 +791,25 @@ static void lpuart_shutdown(struct uart_port *port) devm_free_irq(port->dev, port->irq, sport); } +static void lpuart32_shutdown(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long temp; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable Rx/Tx and interrupts */ + temp = lpuart32_read(port->membase + UARTCTRL); + temp &= ~(UARTCTRL_TE | UARTCTRL_RE | + UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); + lpuart32_write(temp, port->membase + UARTCTRL); + + spin_unlock_irqrestore(&port->lock, flags); + + devm_free_irq(port->dev, port->irq, sport); +} + static void lpuart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) @@ -542,6 +936,125 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&sport->port.lock, flags); } +static void +lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long flags; + unsigned long ctrl, old_ctrl, bd, modem; + unsigned int baud; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned int sbr; + + ctrl = old_ctrl = lpuart32_read(sport->port.membase + UARTCTRL); + bd = lpuart32_read(sport->port.membase + UARTBAUD); + modem = lpuart32_read(sport->port.membase + UARTMODIR); + /* + * only support CS8 and CS7, and for CS7 must enable PE. + * supported mode: + * - (7,e/o,1) + * - (8,n,1) + * - (8,m/s,1) + * - (8,e/o,1) + */ + while ((termios->c_cflag & CSIZE) != CS8 && + (termios->c_cflag & CSIZE) != CS7) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8 || + (termios->c_cflag & CSIZE) == CS7) + ctrl = old_ctrl & ~UARTCTRL_M; + + if (termios->c_cflag & CMSPAR) { + if ((termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + } + ctrl |= UARTCTRL_M; + } + + if (termios->c_cflag & CRTSCTS) { + modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } else { + termios->c_cflag &= ~CRTSCTS; + modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } + + if (termios->c_cflag & CSTOPB) + termios->c_cflag &= ~CSTOPB; + + /* parity must be enabled when CS7 to match 8-bits format */ + if ((termios->c_cflag & CSIZE) == CS7) + termios->c_cflag |= PARENB; + + if ((termios->c_cflag & PARENB)) { + if (termios->c_cflag & CMSPAR) { + ctrl &= ~UARTCTRL_PE; + ctrl |= UARTCTRL_M; + } else { + ctrl |= UARTCR1_PE; + if ((termios->c_cflag & CSIZE) == CS8) + ctrl |= UARTCTRL_M; + if (termios->c_cflag & PARODD) + ctrl |= UARTCTRL_PT; + else + ctrl &= ~UARTCTRL_PT; + } + } + + /* ask the core to calculate the divisor */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= (UARTSTAT_FE | UARTSTAT_PE); + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + sport->port.read_status_mask |= UARTSTAT_FE; + + /* characters to ignore */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSTAT_PE; + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= UARTSTAT_FE; + /* + * if we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSTAT_OR; + } + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* wait transmit engin complete */ + while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC)) + barrier(); + + /* disable transmit and receive */ + lpuart32_write(old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE), + sport->port.membase + UARTCTRL); + + sbr = sport->port.uartclk / (16 * baud); + bd &= ~UARTBAUD_SBR_MASK; + bd |= sbr & UARTBAUD_SBR_MASK; + bd |= UARTBAUD_BOTHEDGE; + bd &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE); + lpuart32_write(bd, sport->port.membase + UARTBAUD); + lpuart32_write(modem, sport->port.membase + UARTMODIR); + lpuart32_write(ctrl, sport->port.membase + UARTCTRL); + /* restore control register */ + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + static const char *lpuart_type(struct uart_port *port) { return "FSL_LPUART"; @@ -602,6 +1115,24 @@ static struct uart_ops lpuart_pops = { .verify_port = lpuart_verify_port, }; +static struct uart_ops lpuart32_pops = { + .tx_empty = lpuart32_tx_empty, + .set_mctrl = lpuart32_set_mctrl, + .get_mctrl = lpuart32_get_mctrl, + .stop_tx = lpuart32_stop_tx, + .start_tx = lpuart32_start_tx, + .stop_rx = lpuart32_stop_rx, + .break_ctl = lpuart32_break_ctl, + .startup = lpuart32_startup, + .shutdown = lpuart32_shutdown, + .set_termios = lpuart32_set_termios, + .type = lpuart_type, + .request_port = lpuart_request_port, + .release_port = lpuart_release_port, + .config_port = lpuart_config_port, + .verify_port = lpuart_verify_port, +}; + static struct lpuart_port *lpuart_ports[UART_NR]; #ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE @@ -613,6 +1144,14 @@ static void lpuart_console_putchar(struct uart_port *port, int ch) writeb(ch, port->membase + UARTDR); } +static void lpuart32_console_putchar(struct uart_port *port, int ch) +{ + while (!(lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TDRE)) + barrier(); + + lpuart32_write(ch, port->membase + UARTDATA); +} + static void lpuart_console_write(struct console *co, const char *s, unsigned int count) { @@ -634,6 +1173,27 @@ lpuart_console_write(struct console *co, const char *s, unsigned int count) writeb(old_cr2, sport->port.membase + UARTCR2); } +static void +lpuart32_console_write(struct console *co, const char *s, unsigned int count) +{ + struct lpuart_port *sport = lpuart_ports[co->index]; + unsigned long old_cr, cr; + + /* first save CR2 and then disable interrupts */ + cr = old_cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr |= (UARTCTRL_TE | UARTCTRL_RE); + cr &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE); + lpuart32_write(cr, sport->port.membase + UARTCTRL); + + uart_console_write(&sport->port, s, count, lpuart32_console_putchar); + + /* wait for transmitter finish complete and restore CR2 */ + while (!(lpuart32_read(sport->port.membase + UARTSTAT) & UARTSTAT_TC)) + barrier(); + + lpuart32_write(old_cr, sport->port.membase + UARTCTRL); +} + /* * if the port was already initialised (eg, by a boot loader), * try to determine the current setup. @@ -687,6 +1247,49 @@ lpuart_console_get_options(struct lpuart_port *sport, int *baud, "from %d to %d\n", baud_raw, *baud); } +static void __init +lpuart32_console_get_options(struct lpuart_port *sport, int *baud, + int *parity, int *bits) +{ + unsigned long cr, bd; + unsigned int sbr, uartclk, baud_raw; + + cr = lpuart32_read(sport->port.membase + UARTCTRL); + cr &= UARTCTRL_TE | UARTCTRL_RE; + if (!cr) + return; + + /* ok, the port was enabled */ + + cr = lpuart32_read(sport->port.membase + UARTCTRL); + + *parity = 'n'; + if (cr & UARTCTRL_PE) { + if (cr & UARTCTRL_PT) + *parity = 'o'; + else + *parity = 'e'; + } + + if (cr & UARTCTRL_M) + *bits = 9; + else + *bits = 8; + + bd = lpuart32_read(sport->port.membase + UARTBAUD); + bd &= UARTBAUD_SBR_MASK; + sbr = bd; + uartclk = clk_get_rate(sport->clk); + /* + * baud = mod_clk/(16*(sbr[13]+(brfa)/32) + */ + baud_raw = uartclk / (16 * sbr); + + if (*baud != baud_raw) + printk(KERN_INFO "Serial: Console lpuart rounded baud rate" + "from %d to %d\n", baud_raw, *baud); +} + static int __init lpuart_console_setup(struct console *co, char *options) { struct lpuart_port *sport; @@ -710,9 +1313,15 @@ static int __init lpuart_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - lpuart_console_get_options(sport, &baud, &parity, &bits); + if (sport->lpuart32) + lpuart32_console_get_options(sport, &baud, &parity, &bits); + else + lpuart_console_get_options(sport, &baud, &parity, &bits); - lpuart_setup_watermark(sport); + if (sport->lpuart32) + lpuart32_setup_watermark(sport); + else + lpuart_setup_watermark(sport); return uart_set_options(&sport->port, co, baud, parity, bits, flow); } @@ -728,9 +1337,21 @@ static struct console lpuart_console = { .data = &lpuart_reg, }; +static struct console lpuart32_console = { + .name = DEV_NAME, + .write = lpuart32_console_write, + .device = uart_console_device, + .setup = lpuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lpuart_reg, +}; + #define LPUART_CONSOLE (&lpuart_console) +#define LPUART32_CONSOLE (&lpuart32_console) #else #define LPUART_CONSOLE NULL +#define LPUART32_CONSOLE NULL #endif static struct uart_driver lpuart_reg = { @@ -760,7 +1381,7 @@ static int lpuart_probe(struct platform_device *pdev) return ret; } sport->port.line = ret; - + sport->lpuart32 = of_device_is_compatible(np, "fsl,ls1021a-lpuart"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; @@ -774,7 +1395,10 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.type = PORT_LPUART; sport->port.iotype = UPIO_MEM; sport->port.irq = platform_get_irq(pdev, 0); - sport->port.ops = &lpuart_pops; + if (sport->lpuart32) + sport->port.ops = &lpuart32_pops; + else + sport->port.ops = &lpuart_pops; sport->port.flags = UPF_BOOT_AUTOCONF; sport->clk = devm_clk_get(&pdev->dev, "ipg"); @@ -796,6 +1420,11 @@ static int lpuart_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &sport->port); + if (sport->lpuart32) + lpuart_reg.cons = LPUART32_CONSOLE; + else + lpuart_reg.cons = LPUART_CONSOLE; + ret = uart_add_one_port(&lpuart_reg, &sport->port); if (ret) { clk_disable_unprepare(sport->clk); @@ -829,6 +1458,20 @@ static int lpuart_suspend(struct device *dev) static int lpuart_resume(struct device *dev) { struct lpuart_port *sport = dev_get_drvdata(dev); + unsigned long temp; + + if (sport->lpuart32) { + lpuart32_setup_watermark(sport); + temp = lpuart32_read(sport->port.membase + UARTCTRL); + temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | + UARTCTRL_TE | UARTCTRL_ILIE); + lpuart32_write(temp, sport->port.membase + UARTCTRL); + } else { + lpuart_setup_watermark(sport); + temp = readb(sport->port.membase + UARTCR2); + temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); + writeb(temp, sport->port.membase + UARTCR2); + } uart_resume_port(&lpuart_reg, &sport->port); diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 2caf9c6..58be230 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -159,7 +159,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL)) return -EBUSY; - info = kmalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) return -ENOMEM; @@ -240,6 +240,32 @@ static int of_platform_serial_remove(struct platform_device *ofdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int of_serial_suspend(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + serial8250_suspend_port(info->line); + if (info->clk) + clk_disable_unprepare(info->clk); + + return 0; +} + +static int of_serial_resume(struct device *dev) +{ + struct of_serial_info *info = dev_get_drvdata(dev); + + if (info->clk) + clk_prepare_enable(info->clk); + + serial8250_resume_port(info->line); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume); + /* * A few common types, add more as needed. */ @@ -262,6 +288,8 @@ static struct of_device_id of_platform_serial_table[] = { { .compatible = "ibm,qpace-nwp-serial", .data = (void *)PORT_NWPSERIAL, }, #endif + { .compatible = "fsl,16550-FIFO64", + .data = (void *)PORT_16550A_FSL64, }, { .type = "serial", .data = (void *)PORT_UNKNOWN, }, { /* end of list */ }, }; @@ -271,6 +299,7 @@ static struct platform_driver of_platform_serial_driver = { .name = "of_serial", .owner = THIS_MODULE, .of_match_table = of_platform_serial_table, + .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 8831748..2c9a87c 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -26,13 +26,13 @@ #include <linux/tty_flip.h> #include <linux/io.h> #include <linux/of_platform.h> +#include <linux/of_irq.h> #include <linux/dma-mapping.h> #include <linux/fs_uart_pd.h> -#include <asm/ucc_slow.h> +#include <linux/fsl/ucc_slow.h> #include <linux/firmware.h> -#include <asm/reg.h> /* * The GUMR flag for Soft UART. This would normally be defined in qe.h, @@ -257,11 +257,11 @@ static unsigned int qe_uart_tx_empty(struct uart_port *port) struct qe_bd *bdp = qe_port->tx_bd_base; while (1) { - if (in_be16(&bdp->status) & BD_SC_READY) + if (ioread16be(&bdp->status) & BD_SC_READY) /* This BD is not done, so return "not done" */ return 0; - if (in_be16(&bdp->status) & BD_SC_WRAP) + if (ioread16be(&bdp->status) & BD_SC_WRAP) /* * This BD is done and it's the last one, so return * "done" @@ -339,13 +339,13 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) /* Pick next descriptor and fill from buffer */ bdp = qe_port->tx_cur; - p = qe2cpu_addr(bdp->buf, qe_port); + p = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); *p++ = port->x_char; - out_be16(&bdp->length, 1); + iowrite16be(1, &bdp->length); setbits16(&bdp->status, BD_SC_READY); /* Get next BD. */ - if (in_be16(&bdp->status) & BD_SC_WRAP) + if (ioread16be(&bdp->status) & BD_SC_WRAP) bdp = qe_port->tx_bd_base; else bdp++; @@ -364,10 +364,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) /* Pick next descriptor and fill from buffer */ bdp = qe_port->tx_cur; - while (!(in_be16(&bdp->status) & BD_SC_READY) && + while (!(ioread16be(&bdp->status) & BD_SC_READY) && (xmit->tail != xmit->head)) { count = 0; - p = qe2cpu_addr(bdp->buf, qe_port); + p = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); while (count < qe_port->tx_fifosize) { *p++ = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -377,11 +377,11 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) break; } - out_be16(&bdp->length, count); + iowrite16be(count, &bdp->length); setbits16(&bdp->status, BD_SC_READY); /* Get next BD. */ - if (in_be16(&bdp->status) & BD_SC_WRAP) + if (ioread16be(&bdp->status) & BD_SC_WRAP) bdp = qe_port->tx_bd_base; else bdp++; @@ -414,7 +414,7 @@ static void qe_uart_start_tx(struct uart_port *port) container_of(port, struct uart_qe_port, port); /* If we currently are transmitting, then just return */ - if (in_be16(&qe_port->uccp->uccm) & UCC_UART_UCCE_TX) + if (ioread16be(&qe_port->uccp->uccm) & UCC_UART_UCCE_TX) return; /* Otherwise, pump the port and start transmission */ @@ -479,14 +479,14 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) */ bdp = qe_port->rx_cur; while (1) { - status = in_be16(&bdp->status); + status = ioread16be(&bdp->status); /* If this one is empty, then we assume we've read them all */ if (status & BD_SC_EMPTY) break; /* get number of characters, and check space in RX buffer */ - i = in_be16(&bdp->length); + i = ioread16be(&bdp->length); /* If we don't have enough room in RX buffer for the entire BD, * then we try later, which will be the next RX interrupt. @@ -497,7 +497,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) } /* get pointer */ - cp = qe2cpu_addr(bdp->buf, qe_port); + cp = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port); /* loop through the buffer */ while (i-- > 0) { @@ -519,7 +519,7 @@ error_return: /* This BD is ready to be used again. Clear status. get next */ clrsetbits_be16(&bdp->status, BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID, BD_SC_EMPTY); - if (in_be16(&bdp->status) & BD_SC_WRAP) + if (ioread16be(&bdp->status) & BD_SC_WRAP) bdp = qe_port->rx_bd_base; else bdp++; @@ -578,8 +578,8 @@ static irqreturn_t qe_uart_int(int irq, void *data) u16 events; /* Clear the interrupts */ - events = in_be16(&uccp->ucce); - out_be16(&uccp->ucce, events); + events = ioread16be(&uccp->ucce); + iowrite16be(events, &uccp->ucce); if (events & UCC_UART_UCCE_BRKE) uart_handle_break(&qe_port->port); @@ -610,17 +610,17 @@ static void qe_uart_initbd(struct uart_qe_port *qe_port) bdp = qe_port->rx_bd_base; qe_port->rx_cur = qe_port->rx_bd_base; for (i = 0; i < (qe_port->rx_nrfifos - 1); i++) { - out_be16(&bdp->status, BD_SC_EMPTY | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); + iowrite16be(BD_SC_EMPTY | BD_SC_INTRPT, &bdp->status); + iowrite32be(cpu2qe_addr(bd_virt, qe_port), &bdp->buf); + iowrite16be(0, &bdp->length); bd_virt += qe_port->rx_fifosize; bdp++; } /* */ - out_be16(&bdp->status, BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); + iowrite16be(BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT, &bdp->status); + iowrite32be(cpu2qe_addr(bd_virt, qe_port), &bdp->buf); + iowrite16be(0, &bdp->length); /* Set the physical address of the host memory * buffers in the buffer descriptors, and the @@ -631,9 +631,9 @@ static void qe_uart_initbd(struct uart_qe_port *qe_port) qe_port->tx_cur = qe_port->tx_bd_base; bdp = qe_port->tx_bd_base; for (i = 0; i < (qe_port->tx_nrfifos - 1); i++) { - out_be16(&bdp->status, BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); + iowrite16be(BD_SC_INTRPT, &bdp->status); + iowrite32be(cpu2qe_addr(bd_virt, qe_port), &bdp->buf); + iowrite16be(0, &bdp->length); bd_virt += qe_port->tx_fifosize; bdp++; } @@ -643,9 +643,9 @@ static void qe_uart_initbd(struct uart_qe_port *qe_port) setbits16(&qe_port->tx_cur->status, BD_SC_P); #endif - out_be16(&bdp->status, BD_SC_WRAP | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); + iowrite16be(BD_SC_WRAP | BD_SC_INTRPT, &bdp->status); + iowrite32be(cpu2qe_addr(bd_virt, qe_port), &bdp->buf); + iowrite16be(0, &bdp->length); } /* @@ -667,21 +667,21 @@ static void qe_uart_init_ucc(struct uart_qe_port *qe_port) ucc_slow_disable(qe_port->us_private, COMM_DIR_RX_AND_TX); /* Program the UCC UART parameter RAM */ - out_8(&uccup->common.rbmr, UCC_BMR_GBL | UCC_BMR_BO_BE); - out_8(&uccup->common.tbmr, UCC_BMR_GBL | UCC_BMR_BO_BE); - out_be16(&uccup->common.mrblr, qe_port->rx_fifosize); - out_be16(&uccup->maxidl, 0x10); - out_be16(&uccup->brkcr, 1); - out_be16(&uccup->parec, 0); - out_be16(&uccup->frmec, 0); - out_be16(&uccup->nosec, 0); - out_be16(&uccup->brkec, 0); - out_be16(&uccup->uaddr[0], 0); - out_be16(&uccup->uaddr[1], 0); - out_be16(&uccup->toseq, 0); + iowrite8(UCC_BMR_GBL | UCC_BMR_BO_BE, &uccup->common.rbmr); + iowrite8(UCC_BMR_GBL | UCC_BMR_BO_BE, &uccup->common.tbmr); + iowrite16be(qe_port->rx_fifosize, &uccup->common.mrblr); + iowrite16be(0x10, &uccup->maxidl); + iowrite16be(1, &uccup->brkcr); + iowrite16be(0, &uccup->parec); + iowrite16be(0, &uccup->frmec); + iowrite16be(0, &uccup->nosec); + iowrite16be(0, &uccup->brkec); + iowrite16be(0, &uccup->uaddr[0]); + iowrite16be(0, &uccup->uaddr[1]); + iowrite16be(0, &uccup->toseq); for (i = 0; i < 8; i++) - out_be16(&uccup->cchars[i], 0xC000); - out_be16(&uccup->rccm, 0xc0ff); + iowrite16be(0xC000, &uccup->cchars[i]); + iowrite16be(0xc0ff, &uccup->rccm); /* Configure the GUMR registers for UART */ if (soft_uart) { @@ -715,30 +715,30 @@ static void qe_uart_init_ucc(struct uart_qe_port *qe_port) #endif /* Disable rx interrupts and clear all pending events. */ - out_be16(&uccp->uccm, 0); - out_be16(&uccp->ucce, 0xffff); - out_be16(&uccp->udsr, 0x7e7e); + iowrite16be(0, &uccp->uccm); + iowrite16be(0xffff, &uccp->ucce); + iowrite16be(0x7e7e, &uccp->udsr); /* Initialize UPSMR */ - out_be16(&uccp->upsmr, 0); + iowrite16be(0, &uccp->upsmr); if (soft_uart) { - out_be16(&uccup->supsmr, 0x30); - out_be16(&uccup->res92, 0); - out_be32(&uccup->rx_state, 0); - out_be32(&uccup->rx_cnt, 0); - out_8(&uccup->rx_bitmark, 0); - out_8(&uccup->rx_length, 10); - out_be32(&uccup->dump_ptr, 0x4000); - out_8(&uccup->rx_temp_dlst_qe, 0); - out_be32(&uccup->rx_frame_rem, 0); - out_8(&uccup->rx_frame_rem_size, 0); + iowrite16be(0x30, &uccup->supsmr); + iowrite16be(0, &uccup->res92); + iowrite32be(0, &uccup->rx_state); + iowrite32be(0, &uccup->rx_cnt); + iowrite8(0, &uccup->rx_bitmark); + iowrite8(10, &uccup->rx_length); + iowrite32be(0x4000, &uccup->dump_ptr); + iowrite8(0, &uccup->rx_temp_dlst_qe); + iowrite32be(0, &uccup->rx_frame_rem); + iowrite8(0, &uccup->rx_frame_rem_size); /* Soft-UART requires TX to be 1X */ - out_8(&uccup->tx_mode, - UCC_UART_TX_STATE_UART | UCC_UART_TX_STATE_X1); - out_be16(&uccup->tx_state, 0); - out_8(&uccup->resD4, 0); - out_be16(&uccup->resD5, 0); + iowrite8(UCC_UART_TX_STATE_UART | UCC_UART_TX_STATE_X1, + &uccup->tx_mode); + iowrite16be(0, &uccup->tx_state); + iowrite8(0, &uccup->resD4); + iowrite16be(0, &uccup->resD5); /* Set UART mode. * Enable receive and transmit. @@ -866,9 +866,9 @@ static void qe_uart_set_termios(struct uart_port *port, struct ucc_slow __iomem *uccp = qe_port->uccp; unsigned int baud; unsigned long flags; - u16 upsmr = in_be16(&uccp->upsmr); + u16 upsmr = ioread16be(&uccp->upsmr); struct ucc_uart_pram __iomem *uccup = qe_port->uccup; - u16 supsmr = in_be16(&uccup->supsmr); + u16 supsmr = ioread16be(&uccup->supsmr); u8 char_length = 2; /* 1 + CL + PEN + 1 + SL */ /* Character length programmed into the mode register is the @@ -966,10 +966,10 @@ static void qe_uart_set_termios(struct uart_port *port, /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); - out_be16(&uccp->upsmr, upsmr); + iowrite16be(upsmr, &uccp->upsmr); if (soft_uart) { - out_be16(&uccup->supsmr, supsmr); - out_8(&uccup->rx_length, char_length); + iowrite16be(supsmr, &uccup->supsmr); + iowrite8(char_length, &uccup->rx_length); /* Soft-UART requires a 1X multiplier for TX */ qe_setbrg(qe_port->us_info.rx_clock, baud, 16); @@ -1139,7 +1139,9 @@ static unsigned int soc_info(unsigned int *rev_h, unsigned int *rev_l) { struct device_node *np; const char *soc_string; +#ifdef CONFIG_PPC_85xx unsigned int svr; +#endif unsigned int soc; /* Find the CPU node */ @@ -1156,10 +1158,12 @@ static unsigned int soc_info(unsigned int *rev_h, unsigned int *rev_l) if ((sscanf(soc_string, "PowerPC,%u", &soc) != 1) || !soc) return 0; +#ifdef CONFIG_PPC_85xx /* Get the revision from the SVR */ svr = mfspr(SPRN_SVR); *rev_h = (svr >> 4) & 0xf; *rev_l = svr & 0xf; +#endif return soc; } @@ -1202,7 +1206,7 @@ static void uart_firmware_cont(const struct firmware *fw, void *context) static int ucc_uart_probe(struct platform_device *ofdev) { struct device_node *np = ofdev->dev.of_node; - const unsigned int *iprop; /* Integer OF properties */ + u32 val; const char *sprop; /* String OF properties */ struct uart_qe_port *qe_port = NULL; struct resource res; @@ -1285,10 +1289,10 @@ static int ucc_uart_probe(struct platform_device *ofdev) /* Get the UCC number (device ID) */ /* UCCs are numbered 1-7 */ - iprop = of_get_property(np, "cell-index", NULL); - if (!iprop) { - iprop = of_get_property(np, "device-id", NULL); - if (!iprop) { + ret = of_property_read_u32_index(np, "cell-index", 0, &val); + if (ret) { + ret = of_property_read_u32_index(np, "device-id", 0, &val); + if (ret) { dev_err(&ofdev->dev, "UCC is unspecified in " "device tree\n"); ret = -EINVAL; @@ -1296,12 +1300,12 @@ static int ucc_uart_probe(struct platform_device *ofdev) } } - if ((*iprop < 1) || (*iprop > UCC_MAX_NUM)) { - dev_err(&ofdev->dev, "no support for UCC%u\n", *iprop); + if ((val < 1) || (val > UCC_MAX_NUM)) { + dev_err(&ofdev->dev, "no support for UCC%u\n", val); ret = -ENODEV; goto out_free; } - qe_port->ucc_num = *iprop - 1; + qe_port->ucc_num = val - 1; /* * In the future, we should not require the BRG to be specified in the @@ -1345,13 +1349,13 @@ static int ucc_uart_probe(struct platform_device *ofdev) } /* Get the port number, numbered 0-3 */ - iprop = of_get_property(np, "port-number", NULL); - if (!iprop) { + ret = of_property_read_u32_index(np, "port-number", 0, &val); + if (ret) { dev_err(&ofdev->dev, "missing port-number in device tree\n"); ret = -EINVAL; goto out_free; } - qe_port->port.line = *iprop; + qe_port->port.line = val; if (qe_port->port.line >= UCC_MAX_UART) { dev_err(&ofdev->dev, "port-number must be 0-%u\n", UCC_MAX_UART - 1); @@ -1381,31 +1385,31 @@ static int ucc_uart_probe(struct platform_device *ofdev) } } - iprop = of_get_property(np, "brg-frequency", NULL); - if (!iprop) { + ret = of_property_read_u32_index(np, "brg-frequency", 0, &val); + if (ret) { dev_err(&ofdev->dev, "missing brg-frequency in device tree\n"); ret = -EINVAL; goto out_np; } - if (*iprop) - qe_port->port.uartclk = *iprop; + if (val) + qe_port->port.uartclk = val; else { /* * Older versions of U-Boot do not initialize the brg-frequency * property, so in this case we assume the BRG frequency is * half the QE bus frequency. */ - iprop = of_get_property(np, "bus-frequency", NULL); - if (!iprop) { + ret = of_property_read_u32_index(np, "bus-frequency", 0, &val); + if (ret) { dev_err(&ofdev->dev, "missing QE bus-frequency in device tree\n"); ret = -EINVAL; goto out_np; } - if (*iprop) - qe_port->port.uartclk = *iprop / 2; + if (val) + qe_port->port.uartclk = val / 2; else { dev_err(&ofdev->dev, "invalid QE bus-frequency in device tree\n"); diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index f3bb363..d41d8e2 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -38,7 +38,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> -#include <asm/qe.h> +#include <linux/fsl/qe.h> #include <asm/cpm.h> #include <asm/dma.h> #include <asm/reg.h> diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index c540dfa..ef4a00f 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -245,10 +245,10 @@ static int dr_controller_setup(struct fsl_udc *udc) if (udc->pdata->have_sysif_regs) { if (udc->pdata->controller_ver) { /* controller version 1.6 or above */ - ctrl = __raw_readl(&usb_sys_regs->control); + ctrl = ioread32be(&usb_sys_regs->control); ctrl &= ~USB_CTRL_UTMI_PHY_EN; ctrl |= USB_CTRL_USB_EN; - __raw_writel(ctrl, &usb_sys_regs->control); + iowrite32be(ctrl, &usb_sys_regs->control); } } portctrl |= PORTSCX_PTS_ULPI; @@ -260,10 +260,10 @@ static int dr_controller_setup(struct fsl_udc *udc) if (udc->pdata->have_sysif_regs) { if (udc->pdata->controller_ver) { /* controller version 1.6 or above */ - ctrl = __raw_readl(&usb_sys_regs->control); + ctrl = ioread32be(&usb_sys_regs->control); ctrl |= (USB_CTRL_UTMI_PHY_EN | USB_CTRL_USB_EN); - __raw_writel(ctrl, &usb_sys_regs->control); + iowrite32be(ctrl, &usb_sys_regs->control); mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI PHY CLK to become stable - 10ms*/ } @@ -329,22 +329,22 @@ static int dr_controller_setup(struct fsl_udc *udc) /* Config control enable i/o output, cpu endian register */ #ifndef CONFIG_ARCH_MXC if (udc->pdata->have_sysif_regs) { - ctrl = __raw_readl(&usb_sys_regs->control); + ctrl = ioread32be(&usb_sys_regs->control); ctrl |= USB_CTRL_IOENB; - __raw_writel(ctrl, &usb_sys_regs->control); + iowrite32be(ctrl, &usb_sys_regs->control); } #endif -#if !defined(CONFIG_NOT_COHERENT_CACHE) +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) /* Turn on cache snooping hardware, since some PowerPC platforms * wholly rely on hardware to deal with cache coherent. */ if (udc->pdata->have_sysif_regs) { /* Setup Snooping for all the 4GB space */ tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ - __raw_writel(tmp, &usb_sys_regs->snoop1); + iowrite32be(tmp, &usb_sys_regs->snoop1); tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ - __raw_writel(tmp, &usb_sys_regs->snoop2); + iowrite32be(tmp, &usb_sys_regs->snoop2); } #endif diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index c6703bb..e4c4c01 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -17,6 +17,10 @@ #define USB_MAX_CTRL_PAYLOAD 64 #define USB_DR_SYS_OFFSET 0x400 +#ifdef CONFIG_SOC_LS1021A +#undef CONFIG_ARCH_MXC +#endif + /* USB DR device mode registers (Little Endian) */ struct usb_dr_device { /* Capability register */ diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index c307ab0..e6386c0 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -30,7 +30,10 @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/fsl_devices.h> +#include <linux/of_platform.h> +#ifdef CONFIG_PPC #include <sysdev/fsl_soc.h> +#endif #include "ehci-fsl.h" @@ -58,7 +61,7 @@ static bool usb_phy_clk_valid(struct usb_hcd *hcd) void __iomem *non_ehci = hcd->regs; bool ret = true; - if (!(in_be32(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID)) + if (!(ioread32be(non_ehci + FSL_SOC_USB_CTRL) & PHY_CLK_VALID)) ret = false; return ret; @@ -194,12 +197,17 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->have_sysif_regs && pdata->controller_ver < FSL_USB_VER_1_6) setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4); + /* Set USB_EN bit to select ULPI phy for USB controller version 2.5 */ + if (pdata->controller_ver == FSL_USB_VER_2_5 && + pdata->phy_mode == FSL_USB2_PHY_ULPI) + iowrite32be(USB_CTRL_USB_EN, hcd->regs + FSL_SOC_USB_CTRL); + /* * Enable UTMI phy and program PTS field in UTMI mode before asserting * controller reset for USB Controller version 2.5 */ if (pdata->has_fsl_erratum_a007792) { - writel_be(CTRL_UTMI_PHY_EN, hcd->regs + FSL_SOC_USB_CTRL); + setbits32(hcd->regs + FSL_SOC_USB_CTRL, CTRL_UTMI_PHY_EN); writel(PORT_PTS_UTMI, hcd->regs + FSL_SOC_USB_PORTSC1); } @@ -378,9 +386,11 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) /* Setup Snooping for all the 4GB space */ /* SNOOP1 starts from 0x0, size 2G */ - out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0 | SNOOP_SIZE_2GB); + iowrite32be(0x0 | SNOOP_SIZE_2GB, non_ehci + + FSL_SOC_USB_SNOOP1); /* SNOOP2 starts from 0x80000000, size 2G */ - out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB); + iowrite32be(0x80000000 | SNOOP_SIZE_2GB, non_ehci + + FSL_SOC_USB_SNOOP2); } /* Deal with USB erratum A-005275 */ @@ -396,14 +406,9 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) return -EINVAL; if (pdata->operating_mode == FSL_USB2_MPH_HOST) { - unsigned int chip, rev, svr; - - svr = mfspr(SPRN_SVR); - chip = svr >> 16; - rev = (svr >> 4) & 0xf; /* Deal with USB Erratum #14 on MPC834x Rev 1.0 & 1.1 chips */ - if ((rev == 1) && (chip >= 0x8050) && (chip <= 0x8055)) + if (pdata->has_fsl_erratum_14 == 1) ehci->has_fsl_port_bug = 1; if (pdata->port_enables & FSL_USB2_PORT0_ENABLED) @@ -417,13 +422,13 @@ static int ehci_fsl_usb_setup(struct ehci_hcd *ehci) if (pdata->have_sysif_regs) { #ifdef CONFIG_FSL_SOC_BOOKE - out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008); - out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080); + iowrite32be(0x00000008, non_ehci + FSL_SOC_USB_PRICTRL); + iowrite32be(0x00000080, non_ehci + FSL_SOC_USB_AGECNTTHRSH); #else - out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); - out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); + iowrite32be(0x0000000c, non_ehci + FSL_SOC_USB_PRICTRL); + iowrite32be(0x00000040, non_ehci + FSL_SOC_USB_AGECNTTHRSH); #endif - out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); + iowrite32be(0x00000040, non_ehci + FSL_SOC_USB_SICTRL); } return 0; @@ -499,7 +504,7 @@ static int ehci_fsl_save_context(struct usb_hcd *hcd) return -ENOMEM; _memcpy_fromio(ehci_fsl->saved_regs, ehci->regs, sizeof(struct ehci_regs)); - ehci_fsl->usb_ctrl = in_be32(non_ehci + FSL_SOC_USB_CTRL); + ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL); return 0; } @@ -514,7 +519,7 @@ static int ehci_fsl_restore_context(struct usb_hcd *hcd) if (ehci_fsl->saved_regs) { _memcpy_toio(ehci->regs, ehci_fsl->saved_regs, sizeof(struct ehci_regs)); - out_be32(non_ehci + FSL_SOC_USB_CTRL, ehci_fsl->usb_ctrl); + iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL); kfree(ehci_fsl->saved_regs); ehci_fsl->saved_regs = NULL; } @@ -691,7 +696,9 @@ static int ehci_fsl_drv_suspend(struct device *dev) return 0; } #endif +#ifdef CONFIG_PPC mpc85xx_pmc_set_wake(dev, true); +#endif ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), device_may_wakeup(dev)); @@ -701,7 +708,7 @@ static int ehci_fsl_drv_suspend(struct device *dev) if (!fsl_deep_sleep()) return 0; - ehci_fsl->usb_ctrl = in_be32(non_ehci + FSL_SOC_USB_CTRL); + ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL); return 0; } @@ -734,7 +741,9 @@ static int ehci_fsl_drv_resume(struct device *dev) } #endif +#ifdef CONFIG_PPC mpc85xx_pmc_set_wake(dev, false); +#endif ehci_prepare_ports_for_controller_resume(ehci); if (!fsl_deep_sleep()) @@ -743,7 +752,7 @@ static int ehci_fsl_drv_resume(struct device *dev) usb_root_hub_lost_power(hcd->self.root_hub); /* Restore USB PHY settings and enable the controller. */ - out_be32(non_ehci + FSL_SOC_USB_CTRL, ehci_fsl->usb_ctrl); + iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL); ehci_reset(ehci); ehci_fsl_reinit(ehci); diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 0b4654259..fe5fa06 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -29,7 +29,7 @@ #include <linux/of_platform.h> #include <linux/of_gpio.h> #include <linux/slab.h> -#include <asm/qe.h> +#include <linux/fsl/qe.h> #include <asm/fsl_gtm.h> #include "fhci.h" diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c index 6af2512..31b4402 100644 --- a/drivers/usb/host/fhci-hub.c +++ b/drivers/usb/host/fhci-hub.c @@ -24,7 +24,7 @@ #include <linux/usb.h> #include <linux/usb/hcd.h> #include <linux/gpio.h> -#include <asm/qe.h> +#include <linux/fsl/qe.h> #include "fhci.h" /* virtual root hub specific descriptor */ diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index 95ca598..6f1d4ad 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -25,7 +25,7 @@ #include <linux/io.h> #include <linux/usb.h> #include <linux/usb/hcd.h> -#include <asm/qe.h> +#include <linux/fsl/qe.h> #include <asm/fsl_gtm.h> #include "fhci.h" diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h index 154e6a0..d7c49531 100644 --- a/drivers/usb/host/fhci.h +++ b/drivers/usb/host/fhci.h @@ -27,8 +27,8 @@ #include <linux/io.h> #include <linux/usb.h> #include <linux/usb/hcd.h> -#include <asm/qe.h> -#include <asm/immap_qe.h> +#include <linux/fsl/qe.h> +#include <linux/fsl/immap_qe.h> #define USB_CLOCK 48000000 diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 8cb82cc..7640426 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -17,7 +17,7 @@ #include <linux/of_platform.h> #include <linux/clk.h> #include <linux/module.h> -#include <asm/mpc85xx.h> +#include <linux/dma-mapping.h> struct fsl_usb2_dev_data { char *dr_mode; /* controller mode */ @@ -95,7 +95,11 @@ static struct platform_device *fsl_usb2_device_register( pdev->dev.parent = &ofdev->dev; pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask; - *pdev->dev.dma_mask = *ofdev->dev.dma_mask; + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &ofdev->dev.coherent_dma_mask; + else + dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); retval = platform_device_add_data(pdev, pdata, sizeof(*pdata)); if (retval) @@ -120,92 +124,6 @@ error: static const struct of_device_id fsl_usb2_mph_dr_of_match[]; -static bool has_erratum_a005275(struct device_node *node) -{ - unsigned int svr = mfspr(SPRN_SVR); - bool flag = false; - /* Deal with USB Erratum USB A-005275 - * Packet corruption in HS mode, default to - * FS mode for the following - * P3041 and P2041 rev 1.0 and 1.1 - * P5020 and P5010 rev 1.0 and 2.0 - * P5040 and P1010 rev 1.0 - */ - if ((SVR_SOC_VER(svr) == SVR_P2041) || - (SVR_SOC_VER(svr) == SVR_P3041)) - flag = (SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x11); - else if ((SVR_SOC_VER(svr) == SVR_P5010) || - (SVR_SOC_VER(svr) == SVR_P5020)) - flag = (SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20); - else if ((SVR_SOC_VER(svr) == SVR_P5040) || - (SVR_SOC_VER(svr) == SVR_P1010)) - flag = (SVR_REV(svr) == 0x10); - - return flag; -} - -static bool has_erratum_a005697(void) -{ - unsigned int svr = mfspr(SPRN_SVR); - bool flag = false; - - switch (SVR_SOC_VER(svr)) { - case SVR_P1014: - case SVR_T1040: - case SVR_T2080: - case SVR_T2081: - if (SVR_REV(svr) == 0x10) - flag = true; - break; - case SVR_9132: - if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x11)) - flag = true; - break; - case SVR_P5040: - case SVR_P5021: - if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20) || - (SVR_REV(svr) == 0x21)) - flag = true; - break; - case SVR_P1010: - case SVR_T4240: - case SVR_T4160: - case SVR_P5020: - case SVR_P5010: - if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20)) - flag = true; - break; - case SVR_P2040: - case SVR_P2041: - case SVR_P3041: - if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x11) || - (SVR_REV(svr) == 0x20)) - flag = true; - break; - case SVR_P4080: - if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20) || - (SVR_REV(svr) == 0x30)) - flag = true; - break; - case SVR_B4860: - case SVR_B4420: - if ((SVR_REV(svr) == 0x10) || (SVR_REV(svr) == 0x20) || - (SVR_REV(svr) == 0x21) || (SVR_REV(svr) == 0x22)) - flag = true; - break; - } - - return flag; -} - -static bool has_erratum_a007792(int controller_ver) -{ - if (controller_ver == FSL_USB_VER_2_5) - return true; - else - return false; -} - static int usb_get_ver_info(struct device_node *np) { int ver = -1; @@ -299,31 +217,35 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) pdata->phy_mode = determine_usb_phy(prop); pdata->controller_ver = usb_get_ver_info(np); - /* Activate workaround for USB erratum-A005275 if - * fsl,no-erratum-a005275 property not defined for + /* Activate workaround for USB erratum-A00XXXX if + * fsl,erratum-a00XXXX property is defined for * affected socs */ - if (!of_get_property(np, "fsl,no-erratum-a005275", NULL) && - has_erratum_a005275(np)) + if (of_get_property(np, "fsl,usb_erratum-a005275", NULL)) pdata->has_fsl_erratum_a005275 = 1; else pdata->has_fsl_erratum_a005275 = 0; - if (has_erratum_a005697()) + if (of_get_property(np, "fsl,usb_erratum-a005697", NULL)) pdata->has_fsl_erratum_a005697 = 1; else pdata->has_fsl_erratum_a005697 = 0; - if (has_erratum_a007792(pdata->controller_ver)) + if (of_get_property(np, "fsl,usb_erratum-a007792", NULL)) pdata->has_fsl_erratum_a007792 = 1; else pdata->has_fsl_erratum_a007792 = 0; - if (of_get_property(np, "fsl,erratum_a006918", NULL)) + if (of_get_property(np, "fsl,usb_erratum_a006918", NULL)) pdata->has_fsl_erratum_a006918 = 1; else pdata->has_fsl_erratum_a006918 = 0; + if (of_get_property(np, "fsl,usb_erratum_14", NULL)) + pdata->has_fsl_erratum_14 = 1; + else + pdata->has_fsl_erratum_14 = 0; + if (pdata->have_sysif_regs) { if (pdata->controller_ver < 0) { dev_warn(&ofdev->dev, "Could not get controller version\n"); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2a46153..76eff63 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1965,6 +1965,39 @@ config FB_MBX_DEBUG If unsure, say N. +config FB_FSL_SII902X + tristate "Si Image SII9022 DVI/HDMI Interface Chip" + depends on FB + select FB_MODE_HELPERS + ---help--- + Driver for the SII9022 DVI/HDMI controller. + + This driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read + <file:Documentation/kbuild/modules.txt>. + + If unsure, say N. + +config FB_FSL_DCU + tristate "Freescale DCU framebuffer support" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select FB_MODE_HELPERS + select VIDEOMODE_HELPERS + select REGMAP_MMIO + ---help--- + Framebuffer driver for the Freescale SoC DCU. + + This driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read + <file:Documentation/kbuild/modules.txt>. + + If unsure, say N. + config FB_FSL_DIU tristate "Freescale DIU framebuffer support" depends on FB && FSL_SOC @@ -1972,7 +2005,7 @@ config FB_FSL_DIU select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select PPC_LIB_RHEAP + select LIB_RHEAP ---help--- Framebuffer driver for the Freescale SoC DIU diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f5ef0ed..3535ed9 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -129,6 +129,8 @@ obj-$(CONFIG_FB_IMX) += imxfb.o obj-$(CONFIG_FB_S3C) += s3c-fb.o obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o obj-$(CONFIG_FB_FSL_DIU) += fsl-diu-fb.o +obj-$(CONFIG_FB_FSL_SII902X) += fsl-sii902x.o +obj-$(CONFIG_FB_FSL_DCU) += fsl-dcu-fb.o obj-$(CONFIG_FB_COBALT) += cobalt_lcdfb.o obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o diff --git a/drivers/video/fsl-dcu-fb.c b/drivers/video/fsl-dcu-fb.c new file mode 100644 index 0000000..3fc0b79 --- /dev/null +++ b/drivers/video/fsl-dcu-fb.c @@ -0,0 +1,1258 @@ +/* + * Copyright 2012-2014 Freescale Semiconductor, Inc. + * + * Freescale DCU framebuffer device driver + * + * 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 <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/fb.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/uaccess.h> +#include <video/of_display_timing.h> +#include <video/videomode.h> + +#define DRIVER_NAME "fsl-dcu-fb" + +#define DCU_DCU_MODE 0x10 +#define DCU_MODE_BLEND_ITER(x) ((x) << 20) +#define DCU_MODE_RASTER_EN BIT(14) +#define DCU_MODE_DCU_MODE(x) (x) +#define DCU_MODE_DCU_MODE_MASK 0x03 +#define DCU_MODE_OFF 0 +#define DCU_MODE_NORMAL 1 +#define DCU_MODE_TEST 2 +#define DCU_MODE_COLORBAR 3 + +#define DCU_BGND 0x14 +#define DCU_BGND_R(x) ((x) << 16) +#define DCU_BGND_G(x) ((x) << 8) +#define DCU_BGND_B(x) (x) + +#define DCU_DISP_SIZE 0x18 +#define DCU_DISP_SIZE_DELTA_Y(x) ((x) << 16) +#define DCU_DISP_SIZE_DELTA_X(x) (x) + +#define DCU_HSYN_PARA 0x1c +#define DCU_HSYN_PARA_BP(x) ((x) << 22) +#define DCU_HSYN_PARA_PW(x) ((x) << 11) +#define DCU_HSYN_PARA_FP(x) (x) + +#define DCU_VSYN_PARA 0x20 +#define DCU_VSYN_PARA_BP(x) ((x) << 22) +#define DCU_VSYN_PARA_PW(x) ((x) << 11) +#define DCU_VSYN_PARA_FP(x) (x) + +#define DCU_SYN_POL 0x24 +#define DCU_SYN_POL_INV_PXCK_FALL (0 << 6) +#define DCU_SYN_POL_NEG_REMAIN (0 << 5) +#define DCU_SYN_POL_INV_VS_LOW BIT(1) +#define DCU_SYN_POL_INV_HS_LOW BIT(0) + +#define DCU_THRESHOLD 0x28 +#define DCU_THRESHOLD_LS_BF_VS(x) ((x) << 16) +#define DCU_THRESHOLD_OUT_BUF_HIGH(x) ((x) << 8) +#define DCU_THRESHOLD_OUT_BUF_LOW(x) (x) + +#define DCU_INT_STATUS 0x2C +#define DCU_INT_STATUS_UNDRUN BIT(1) + +#define DCU_INT_MASK 0x30 +#define DCU_INT_MASK_UNDRUN BIT(1) +#define DCU_INT_MASK_ALL 0xffffffff + +#define DCU_DIV_RATIO 0x54 + +#define DCU_UPDATE_MODE 0xcc +#define DCU_UPDATE_MODE_MODE BIT(31) +#define DCU_UPDATE_MODE_READREG BIT(30) + +#define DCU_CTRLDESCLN_1(x) (0x200 + (x) * 0x40) +#define DCU_CTRLDESCLN_1_HEIGHT(x) ((x) << 16) +#define DCU_CTRLDESCLN_1_WIDTH(x) (x) + +#define DCU_CTRLDESCLN_2(x) (0x204 + (x) * 0x40) +#define DCU_CTRLDESCLN_2_POSY(x) ((x) << 16) +#define DCU_CTRLDESCLN_2_POSX(x) (x) + +#define DCU_CTRLDESCLN_3(x) (0x208 + (x) * 0x40) + +#define DCU_CTRLDESCLN_4(x) (0x20c + (x) * 0x40) +#define DCU_CTRLDESCLN_4_EN BIT(31) +#define DCU_CTRLDESCLN_4_TILE_EN BIT(30) +#define DCU_CTRLDESCLN_4_DATA_SEL_CLUT BIT(29) +#define DCU_CTRLDESCLN_4_SAFETY_EN BIT(28) +#define DCU_CTRLDESCLN_4_TRANS(x) ((x) << 20) +#define DCU_CTRLDESCLN_4_BPP(x) ((x) << 16) +#define DCU_CTRLDESCLN_4_RLE_EN BIT(15) +#define DCU_CTRLDESCLN_4_LUOFFS(x) ((x) << 4) +#define DCU_CTRLDESCLN_4_BB_ON BIT(2) +#define DCU_CTRLDESCLN_4_AB(x) (x) + +#define DCU_CTRLDESCLN_5(x) (0x210 + (x) * 0x40) +#define DCU_CTRLDESCLN_5_CKMAX_R(x) ((x) << 16) +#define DCU_CTRLDESCLN_5_CKMAX_G(x) ((x) << 8) +#define DCU_CTRLDESCLN_5_CKMAX_B(x) (x) + +#define DCU_CTRLDESCLN_6(x) (0x214 + (x) * 0x40) +#define DCU_CTRLDESCLN_6_CKMIN_R(x) ((x) << 16) +#define DCU_CTRLDESCLN_6_CKMIN_G(x) ((x) << 8) +#define DCU_CTRLDESCLN_6_CKMIN_B(x) (x) + +#define DCU_CTRLDESCLN_7(x) (0x218 + (x) * 0x40) +#define DCU_CTRLDESCLN_7_TILE_VER(x) ((x) << 16) +#define DCU_CTRLDESCLN_7_TILE_HOR(x) (x) + +#define DCU_CTRLDESCLN_8(x) (0x21c + (x) * 0x40) +#define DCU_CTRLDESCLN_8_FG_FCOLOR(x) (x) + +#define DCU_CTRLDESCLN_9(x) (0x220 + (x) * 0x40) +#define DCU_CTRLDESCLN_9_BG_BCOLOR(x) (x) + +#define DCU_CTRLDESCLN_10(x) (0x224 + (x) * 0x40) + +#ifdef CONFIG_SOC_VF610 +#define DCU_TOTAL_LAYER_NUM 64 +#define DCU_LAYER_NUM_MAX 6 +#else +#define DCU_TOTAL_LAYER_NUM 16 +#define DCU_LAYER_NUM_MAX 4 +#endif + +#define BPP_16_RGB565 4 +#define BPP_24_RGB888 5 +#define BPP_32_ARGB8888 6 + +#define TCON_CTRL1 0x00 +#define TCON_BYPASS_ENABLE BIT(29) + +#define MFB_SET_ALPHA _IOW('M', 0, __u8) +#define MFB_GET_ALPHA _IOR('M', 0, __u8) +#define MFB_SET_LAYER _IOW('M', 4, struct layer_display_offset) +#define MFB_GET_LAYER _IOR('M', 4, struct layer_display_offset) + +struct dcu_fb_data { + struct fb_info *fsl_dcu_info[DCU_LAYER_NUM_MAX]; + struct device *dev; + struct regmap *regmap; + struct regmap *tcon_regmap; + struct regmap *scfg_regmap; + unsigned int irq; + struct clk *clk; + struct clk *tcon_clk; +}; + +struct layer_display_offset { + int x_layer_d; + int y_layer_d; +}; + +struct mfb_info { + int index; + char *id; + unsigned long pseudo_palette[16]; + unsigned char alpha; + unsigned char blend; + unsigned int count; + int x_layer_d; /* layer display x offset to physical screen */ + int y_layer_d; /* layer display y offset to physical screen */ + struct dcu_fb_data *parent; +}; + +enum mfb_index { + LAYER0 = 0, + LAYER1, + LAYER2, + LAYER3, + LAYER4, + LAYER5, +}; + +static struct mfb_info mfb_template[] = { + { + .index = LAYER0, + .id = "Layer0", + .alpha = 0xff, + .blend = 0, + .count = 0, + .x_layer_d = 0, + .y_layer_d = 0, + }, + { + .index = LAYER1, + .id = "Layer1", + .alpha = 0xff, + .blend = 0, + .count = 0, + .x_layer_d = 50, + .y_layer_d = 50, + }, + { + .index = LAYER2, + .id = "Layer2", + .alpha = 0xff, + .blend = 0, + .count = 0, + .x_layer_d = 100, + .y_layer_d = 100, + }, + { + .index = LAYER3, + .id = "Layer3", + .alpha = 0xff, + .blend = 0, + .count = 0, + .x_layer_d = 150, + .y_layer_d = 150, + }, + { + .index = LAYER4, + .id = "Layer4", + .alpha = 0xff, + .blend = 0, + .count = 0, + .x_layer_d = 200, + .y_layer_d = 200, + }, + { + .index = LAYER5, + .id = "Layer5", + .alpha = 0xff, + .blend = 0, + .count = 0, + .x_layer_d = 250, + .y_layer_d = 250, + }, +}; + +static void reset_total_layers(struct device_node *np, + struct dcu_fb_data *dcufb) +{ + int i; + + for (i = 0; i < DCU_TOTAL_LAYER_NUM; i++) { + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_1(i), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_2(i), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_3(i), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_4(i), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_5(i), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_6(i), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_7(i), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_8(i), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_9(i), 0); + if (of_device_is_compatible(np, "fsl,ls1021a-dcu")) + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_10(i), 0); + } + + regmap_write(dcufb->regmap, DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); +} + +static int enable_panel(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + unsigned int bpp; + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_1(mfbi->index), + DCU_CTRLDESCLN_1_HEIGHT(var->yres) | + DCU_CTRLDESCLN_1_WIDTH(var->xres)); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_2(mfbi->index), + DCU_CTRLDESCLN_2_POSY(mfbi->y_layer_d) | + DCU_CTRLDESCLN_2_POSX(mfbi->x_layer_d)); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_3(mfbi->index), + info->fix.smem_start); + + switch (var->bits_per_pixel) { + case 16: + bpp = BPP_16_RGB565; + break; + case 24: + bpp = BPP_24_RGB888; + break; + case 32: + bpp = BPP_32_ARGB8888; + break; + default: + dev_err(dcufb->dev, "unsupported color depth: %u\n", + var->bits_per_pixel); + return -EINVAL; + } + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_4(mfbi->index), + DCU_CTRLDESCLN_4_EN | + DCU_CTRLDESCLN_4_TRANS(mfbi->alpha) | + DCU_CTRLDESCLN_4_BPP(bpp) | + DCU_CTRLDESCLN_4_AB(mfbi->blend)); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_5(mfbi->index), + DCU_CTRLDESCLN_5_CKMAX_R(0xff) | + DCU_CTRLDESCLN_5_CKMAX_G(0xff) | + DCU_CTRLDESCLN_5_CKMAX_B(0xff)); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_6(mfbi->index), + DCU_CTRLDESCLN_6_CKMIN_R(0) | + DCU_CTRLDESCLN_6_CKMIN_G(0) | + DCU_CTRLDESCLN_6_CKMIN_B(0)); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_7(mfbi->index), + DCU_CTRLDESCLN_7_TILE_VER(0) | + DCU_CTRLDESCLN_7_TILE_HOR(0)); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_8(mfbi->index), + DCU_CTRLDESCLN_8_FG_FCOLOR(0)); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_9(mfbi->index), + DCU_CTRLDESCLN_9_BG_BCOLOR(0)); + + regmap_write(dcufb->regmap, DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); + + return 0; +} + +static int disable_panel(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_1(mfbi->index), + DCU_CTRLDESCLN_1_HEIGHT(0) | + DCU_CTRLDESCLN_1_WIDTH(0)); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_2(mfbi->index), + DCU_CTRLDESCLN_2_POSY(0) | + DCU_CTRLDESCLN_2_POSX(0)); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_3(mfbi->index), 0); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_4(mfbi->index), 0); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_5(mfbi->index), + DCU_CTRLDESCLN_5_CKMAX_R(0) | + DCU_CTRLDESCLN_5_CKMAX_G(0) | + DCU_CTRLDESCLN_5_CKMAX_B(0)); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_6(mfbi->index), + DCU_CTRLDESCLN_6_CKMIN_R(0) | + DCU_CTRLDESCLN_6_CKMIN_G(0) | + DCU_CTRLDESCLN_6_CKMIN_B(0)); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_7(mfbi->index), + DCU_CTRLDESCLN_7_TILE_VER(0) | + DCU_CTRLDESCLN_7_TILE_HOR(0)); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_8(mfbi->index), + DCU_CTRLDESCLN_8_FG_FCOLOR(0)); + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_9(mfbi->index), + DCU_CTRLDESCLN_9_BG_BCOLOR(0)); + + regmap_write(dcufb->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); + return 0; +} + +static void enable_controller(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + + regmap_update_bits(dcufb->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); +} + +static void disable_controller(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + + regmap_update_bits(dcufb->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_OFF)); +} + +static int fsl_dcu_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if (var->xoffset + info->var.xres > info->var.xres_virtual) + var->xoffset = info->var.xres_virtual - info->var.xres; + + if (var->yoffset + info->var.yres > info->var.yres_virtual) + var->yoffset = info->var.yres_virtual - info->var.yres; + + switch (var->bits_per_pixel) { + case 16: + var->red.length = 5; + var->red.offset = 11; + var->red.msb_right = 0; + + var->green.length = 6; + var->green.offset = 5; + var->green.msb_right = 0; + + var->blue.length = 5; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 24: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 32: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 8; + var->transp.offset = 24; + var->transp.msb_right = 0; + break; + default: + dev_err(dcufb->dev, "unsupported color depth: %u\n", + var->bits_per_pixel); + return -EINVAL; + } + + return 0; +} + +static int fsl_dcu_calc_div(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + unsigned long long div; + + div = (unsigned long long)(clk_get_rate(dcufb->clk) / 1000); + div *= info->var.pixclock; + do_div(div, 1000000000); + + return div; +} + +static void update_controller(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + unsigned int div; + + div = fsl_dcu_calc_div(info); + regmap_write(dcufb->regmap, DCU_DIV_RATIO, div); + + regmap_write(dcufb->regmap, DCU_DISP_SIZE, + DCU_DISP_SIZE_DELTA_Y(var->yres) | + DCU_DISP_SIZE_DELTA_X(var->xres / 16)); + + /* Horizontal and vertical sync parameters */ + regmap_write(dcufb->regmap, DCU_HSYN_PARA, + DCU_HSYN_PARA_BP(var->left_margin) | + DCU_HSYN_PARA_PW(var->hsync_len) | + DCU_HSYN_PARA_FP(var->right_margin)); + + regmap_write(dcufb->regmap, DCU_VSYN_PARA, + DCU_VSYN_PARA_BP(var->upper_margin) | + DCU_VSYN_PARA_PW(var->vsync_len) | + DCU_VSYN_PARA_FP(var->lower_margin)); + + regmap_write(dcufb->regmap, DCU_SYN_POL, + DCU_SYN_POL_INV_PXCK_FALL | DCU_SYN_POL_NEG_REMAIN | + DCU_SYN_POL_INV_VS_LOW | DCU_SYN_POL_INV_HS_LOW); + + regmap_write(dcufb->regmap, DCU_BGND, DCU_BGND_R(0) | + DCU_BGND_G(0) | DCU_BGND_B(0)); + + regmap_write(dcufb->regmap, DCU_DCU_MODE, + DCU_MODE_BLEND_ITER(DCU_LAYER_NUM_MAX) | + DCU_MODE_RASTER_EN); + + regmap_write(dcufb->regmap, DCU_THRESHOLD, + DCU_THRESHOLD_LS_BF_VS(0x3) | + DCU_THRESHOLD_OUT_BUF_HIGH(0x78) | + DCU_THRESHOLD_OUT_BUF_LOW(0xa)); + + regmap_write(dcufb->regmap, DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); +} + +static int map_video_memory(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + u32 smem_len = info->fix.line_length * info->var.yres_virtual; + + info->fix.smem_len = smem_len; + + info->screen_base = dma_alloc_writecombine(info->device, + info->fix.smem_len, (dma_addr_t *)&info->fix.smem_start, + GFP_KERNEL); + if (!info->screen_base) { + dev_err(dcufb->dev, "unable to allocate fb memory\n"); + return -ENOMEM; + } + + memset(info->screen_base, 0, info->fix.smem_len); + + return 0; +} + +static void unmap_video_memory(struct fb_info *info) +{ + if (!info->screen_base) + return; + + dma_free_writecombine(info->device, info->fix.smem_len, + info->screen_base, info->fix.smem_start); + + info->screen_base = NULL; + info->fix.smem_start = 0; + info->fix.smem_len = 0; +} + +static int fsl_dcu_set_layer(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct fb_var_screeninfo *var = &info->var; + struct dcu_fb_data *dcufb = mfbi->parent; + int pixel_offset; + unsigned long addr; + + pixel_offset = (var->yoffset * var->xres_virtual) + var->xoffset; + addr = info->fix.smem_start + + (pixel_offset * (var->bits_per_pixel >> 3)); + + regmap_write(dcufb->regmap, DCU_CTRLDESCLN_3(mfbi->index), addr); + regmap_write(dcufb->regmap, DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); + + return 0; +} + +static int fsl_dcu_set_par(struct fb_info *info) +{ + unsigned long len; + struct fb_var_screeninfo *var = &info->var; + struct fb_fix_screeninfo *fix = &info->fix; + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->accel = FB_ACCEL_NONE; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ypanstep = 1; + + len = info->var.yres_virtual * info->fix.line_length; + if (len != info->fix.smem_len) { + if (info->fix.smem_start) + unmap_video_memory(info); + + if (map_video_memory(info)) { + dev_err(dcufb->dev, "unable to allocate fb memory\n"); + return -ENOMEM; + } + } + + /* Only layer 0 could update LCD controller */ + if (mfbi->index == LAYER0) { + update_controller(info); + enable_controller(info); + } + + enable_panel(info); + return 0; +} + +static inline __u32 CNVT_TOHW(__u32 val, __u32 width) +{ + return ((val << width) + 0x7FFF - val) >> 16; +} + +static int fsl_dcu_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + unsigned int val; + int ret = -EINVAL; + + /* + * If greyscale is true, then we convert the RGB value + * to greyscale no matter what visual we are using. + */ + if (info->var.grayscale) + red = green = blue = (19595 * red + 38470 * green + + 7471 * blue) >> 16; + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + /* + * 16-bit True Colour. We encode the RGB value + * according to the RGB bitfield information. + */ + if (regno < 16) { + u32 *pal = info->pseudo_palette; + + red = CNVT_TOHW(red, info->var.red.length); + green = CNVT_TOHW(green, info->var.green.length); + blue = CNVT_TOHW(blue, info->var.blue.length); + transp = CNVT_TOHW(transp, info->var.transp.length); + + val = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + pal[regno] = val; + ret = 0; + } + break; + case FB_VISUAL_STATIC_PSEUDOCOLOR: + case FB_VISUAL_PSEUDOCOLOR: + break; + } + + return ret; +} + +static int fsl_dcu_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + if ((info->var.xoffset == var->xoffset) && + (info->var.yoffset == var->yoffset)) + return 0; + + if ((var->xoffset + info->var.xres) > info->var.xres_virtual + || (var->yoffset + info->var.yres) > info->var.yres_virtual) + return -EINVAL; + + info->var.xoffset = var->xoffset; + info->var.yoffset = var->yoffset; + + if (var->vmode & FB_VMODE_YWRAP) + info->var.vmode |= FB_VMODE_YWRAP; + else + info->var.vmode &= ~FB_VMODE_YWRAP; + + fsl_dcu_set_layer(info); + + return 0; +} + +static int fsl_dcu_blank(int blank_mode, struct fb_info *info) +{ + switch (blank_mode) { + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + disable_panel(info); + break; + case FB_BLANK_POWERDOWN: + disable_controller(info); + break; + case FB_BLANK_UNBLANK: + enable_panel(info); + break; + } + + return 0; +} + +static int fsl_dcu_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + struct layer_display_offset layer_d; + void __user *buf = (void __user *)arg; + unsigned char alpha; + + switch (cmd) { + case MFB_SET_LAYER: + if (copy_from_user(&layer_d, buf, sizeof(layer_d))) + return -EFAULT; + mfbi->x_layer_d = layer_d.x_layer_d; + mfbi->y_layer_d = layer_d.y_layer_d; + fsl_dcu_set_par(info); + break; + case MFB_GET_LAYER: + layer_d.x_layer_d = mfbi->x_layer_d; + layer_d.y_layer_d = mfbi->y_layer_d; + if (copy_to_user(buf, &layer_d, sizeof(layer_d))) + return -EFAULT; + break; + case MFB_GET_ALPHA: + alpha = mfbi->alpha; + if (copy_to_user(buf, &alpha, sizeof(alpha))) + return -EFAULT; + break; + case MFB_SET_ALPHA: + if (copy_from_user(&alpha, buf, sizeof(alpha))) + return -EFAULT; + mfbi->blend = 1; + mfbi->alpha = alpha; + fsl_dcu_set_par(info); + break; + default: + dev_err(dcufb->dev, "unknown ioctl command (0x%08X)\n", cmd); + return -ENOIOCTLCMD; + } + + return 0; +} + +static int fsl_dcu_open(struct fb_info *info, int user) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + int ret = 0; + + mfbi->index = info->node; + + mfbi->count++; + if (mfbi->count == 1) { + fsl_dcu_check_var(&info->var, info); + ret = fsl_dcu_set_par(info); + if (ret < 0) + mfbi->count--; + else + regmap_update_bits(dcufb->regmap, DCU_INT_MASK, + DCU_INT_MASK_ALL, DCU_INT_MASK_ALL); + } + + return ret; +} + +static int fsl_dcu_release(struct fb_info *info, int user) +{ + struct mfb_info *mfbi = info->par; + int ret = 0; + + mfbi->count--; + if (mfbi->count == 0) + ret = disable_panel(info); + + return ret; +} + +static struct fb_ops fsl_dcu_ops = { + .owner = THIS_MODULE, + .fb_check_var = fsl_dcu_check_var, + .fb_set_par = fsl_dcu_set_par, + .fb_setcolreg = fsl_dcu_setcolreg, + .fb_blank = fsl_dcu_blank, + .fb_pan_display = fsl_dcu_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_ioctl = fsl_dcu_ioctl, + .fb_open = fsl_dcu_open, + .fb_release = fsl_dcu_release, +}; + +static int fsl_dcu_init_fbinfo(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct fb_var_screeninfo *var = &info->var; + struct dcu_fb_data *dcufb = mfbi->parent; + struct device_node *np = dcufb->dev->of_node; + struct device_node *display_np; + struct device_node *timings_np; + struct display_timings *timings; + int i; + int ret = 0; + + display_np = of_parse_phandle(np, "display", 0); + if (!display_np) { + dev_err(dcufb->dev, "failed to find display phandle\n"); + return -ENOENT; + } + + ret = of_property_read_u32(display_np, "bits-per-pixel", + &var->bits_per_pixel); + if (ret < 0) { + dev_err(dcufb->dev, "failed to get property bits-per-pixel\n"); + goto put_display_node; + } + + timings = of_get_display_timings(display_np); + if (!timings) { + dev_err(dcufb->dev, "failed to get display timings\n"); + ret = -ENOENT; + goto put_display_node; + } + + timings_np = of_find_node_by_name(display_np, + "display-timings"); + if (!timings_np) { + dev_err(dcufb->dev, "failed to find display-timings node\n"); + ret = -ENOENT; + goto put_display_node; + } + + for (i = 0; i < of_get_child_count(timings_np); i++) { + struct videomode vm; + struct fb_videomode fb_vm; + + ret = videomode_from_timings(timings, &vm, i); + if (ret < 0) + goto put_timings_node; + + ret = fb_videomode_from_videomode(&vm, &fb_vm); + if (ret < 0) + goto put_timings_node; + + fb_add_videomode(&fb_vm, &info->modelist); + } + + return 0; +put_timings_node: + of_node_put(timings_np); +put_display_node: + of_node_put(display_np); + return ret; +} + +static int install_framebuffer(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct dcu_fb_data *dcufb = mfbi->parent; + struct fb_modelist *modelist; + int ret; + + info->var.activate = FB_ACTIVATE_NOW; + info->fbops = &fsl_dcu_ops; + info->flags = FBINFO_FLAG_DEFAULT; + info->pseudo_palette = &mfbi->pseudo_palette; + + fb_alloc_cmap(&info->cmap, 16, 0); + + INIT_LIST_HEAD(&info->modelist); + + ret = fsl_dcu_init_fbinfo(info); + if (ret) + return ret; + + modelist = list_first_entry(&info->modelist, + struct fb_modelist, list); + fb_videomode_to_var(&info->var, &modelist->mode); + + fsl_dcu_check_var(&info->var, info); + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(dcufb->dev, "failed to register framebuffer device\n"); + return ret; + } + + pr_info("fb%d: fb device registered successfully.\n", info->node); + return 0; +} + +static void uninstall_framebuffer(struct fb_info *info) +{ + unregister_framebuffer(info); + unmap_video_memory(info); + + if (&info->cmap) + fb_dealloc_cmap(&info->cmap); +} + +static irqreturn_t fsl_dcu_irq(int irq, void *dev_id) +{ + struct dcu_fb_data *dcufb = dev_id; + unsigned int status; + + regmap_read(dcufb->regmap, DCU_INT_STATUS, &status); + + if (status & DCU_INT_STATUS_UNDRUN) { + regmap_update_bits(dcufb->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_OFF)); + udelay(1); + regmap_update_bits(dcufb->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); + } + + regmap_write(dcufb->regmap, DCU_INT_STATUS, status); + + return IRQ_HANDLED; +} + +static const struct regmap_config fsl_tcon_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = TCON_CTRL1, + .cache_type = REGCACHE_FLAT, +}; + +static int bypass_tcon(struct dcu_fb_data *dcufb, struct device_node *np) +{ + struct device_node *tcon_np; + struct platform_device *pdev; + struct resource *res; + void __iomem *base; + + tcon_np = of_parse_phandle(np, "tcon-controller", 0); + if (!tcon_np) + return 0; + + pdev = of_find_device_by_node(tcon_np); + if (!pdev) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "could not ioremap resource\n"); + return PTR_ERR(base); + } + + dcufb->tcon_clk = devm_clk_get(&pdev->dev, "tcon"); + if (IS_ERR(dcufb->tcon_clk)) + return PTR_ERR(dcufb->tcon_clk); + clk_prepare_enable(dcufb->tcon_clk); + + dcufb->tcon_regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "tcon", base, &fsl_tcon_regmap_config); + if (IS_ERR(dcufb->tcon_regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(dcufb->tcon_regmap); + } + + regmap_write(dcufb->tcon_regmap, TCON_CTRL1, TCON_BYPASS_ENABLE); + + return 0; +} + +static const struct regmap_config fsl_scfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = 0x28, + .cache_type = REGCACHE_FLAT, +}; + +static int scfg_config(struct dcu_fb_data *dcufb, struct device_node *np) +{ + struct device_node *scfg_np; + struct platform_device *pdev; + struct resource *res; + void __iomem *base; + + scfg_np = of_parse_phandle(np, "scfg-controller", 0); + if (!scfg_np) + return 0; + + pdev = of_find_device_by_node(scfg_np); + if (!pdev) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "could not ioremap resource\n"); + return PTR_ERR(base); + } + + dcufb->scfg_regmap = devm_regmap_init_mmio_clk(&pdev->dev, + NULL, base, &fsl_scfg_regmap_config); + if (IS_ERR(dcufb->scfg_regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(dcufb->scfg_regmap); + } + + regmap_write(dcufb->scfg_regmap, 0x028, 0x80000000); + + return 0; +} + +static const struct regmap_config fsl_dcu_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = 0x5e8, + .cache_type = REGCACHE_RBTREE, +}; + +static int fsl_dcu_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct dcu_fb_data *dcufb; + struct mfb_info *mfbi; + struct resource *res; + void __iomem *base; + int ret = 0; + int i; + + dcufb = devm_kzalloc(&pdev->dev, + sizeof(struct dcu_fb_data), GFP_KERNEL); + if (!dcufb) + return -ENOMEM; + + dcufb->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, dcufb); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "could not get memory IO resource\n"); + return -ENODEV; + } + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "could not ioremap resource\n"); + return PTR_ERR(base); + } + + dcufb->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + NULL, base, &fsl_dcu_regmap_config); + if (IS_ERR(dcufb->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(dcufb->regmap); + } + + dcufb->irq = platform_get_irq(pdev, 0); + if (!dcufb->irq) { + dev_err(&pdev->dev, "could not get irq\n"); + return -EINVAL; + } + + ret = devm_request_irq(&pdev->dev, dcufb->irq, fsl_dcu_irq, + 0, DRIVER_NAME, dcufb); + if (ret) { + dev_err(&pdev->dev, "could not request irq\n"); + return -EINVAL; + } + + /* Put TCON in bypass mode, so the input signals from DCU are passed + * through TCON unchanged */ + ret = bypass_tcon(dcufb, np); + if (ret) { + dev_err(&pdev->dev, "could not bypass TCON\n"); + return -EINVAL; + } + + ret = scfg_config(dcufb, np); + if (ret) { + dev_err(&pdev->dev, "could not config scfg\n"); + return -EINVAL; + } + + dcufb->clk = devm_clk_get(&pdev->dev, "dcu"); + if (IS_ERR(dcufb->clk)) { + ret = PTR_ERR(dcufb->clk); + dev_err(&pdev->dev, "could not get clock\n"); + return -EINVAL; + } + clk_prepare_enable(dcufb->clk); + + pm_runtime_enable(dcufb->dev); + pm_runtime_get_sync(dcufb->dev); + + reset_total_layers(np, dcufb); + + for (i = 0; i < ARRAY_SIZE(dcufb->fsl_dcu_info); i++) { + dcufb->fsl_dcu_info[i] = + framebuffer_alloc(sizeof(struct mfb_info), &pdev->dev); + if (!dcufb->fsl_dcu_info[i]) { + ret = -ENOMEM; + goto err; + } + + dcufb->fsl_dcu_info[i]->fix.smem_start = 0; + + mfbi = dcufb->fsl_dcu_info[i]->par; + memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); + mfbi->parent = dcufb; + + ret = install_framebuffer(dcufb->fsl_dcu_info[i]); + if (ret) { + dev_err(&pdev->dev, + "could not register framebuffer %d\n", i); + goto err; + } + } + + goto out; +err: + for (i = 0; i < ARRAY_SIZE(dcufb->fsl_dcu_info); i++) { + if (dcufb->fsl_dcu_info[i]) + framebuffer_release(dcufb->fsl_dcu_info[i]); + } + + clk_disable_unprepare(dcufb->clk); +out: + pm_runtime_put_sync(dcufb->dev); + pm_runtime_disable(dcufb->dev); + return ret; +} + +static int fsl_dcu_remove(struct platform_device *pdev) +{ + struct dcu_fb_data *dcufb = dev_get_drvdata(&pdev->dev); + int i; + + pm_runtime_get_sync(dcufb->dev); + + disable_controller(dcufb->fsl_dcu_info[0]); + + clk_disable_unprepare(dcufb->tcon_clk); + clk_disable_unprepare(dcufb->clk); + free_irq(dcufb->irq, dcufb); + + for (i = 0; i < ARRAY_SIZE(dcufb->fsl_dcu_info); i++) { + uninstall_framebuffer(dcufb->fsl_dcu_info[i]); + framebuffer_release(dcufb->fsl_dcu_info[i]); + } + + pm_runtime_put_sync(dcufb->dev); + pm_runtime_disable(dcufb->dev); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int fsl_dcu_runtime_suspend(struct device *dev) +{ + struct dcu_fb_data *dcufb = dev_get_drvdata(dev); + + clk_disable_unprepare(dcufb->clk); + clk_disable_unprepare(dcufb->tcon_clk); + + return 0; +} + +static int fsl_dcu_runtime_resume(struct device *dev) +{ + struct dcu_fb_data *dcufb = dev_get_drvdata(dev); + + clk_prepare_enable(dcufb->tcon_clk); + clk_prepare_enable(dcufb->clk); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int fsl_dcu_suspend(struct device *dev) +{ + struct dcu_fb_data *dcufb = dev_get_drvdata(dev); + + regcache_cache_only(dcufb->regmap, true); + regcache_mark_dirty(dcufb->regmap); + clk_disable_unprepare(dcufb->clk); + + if (dcufb->tcon_regmap) { + regcache_cache_only(dcufb->tcon_regmap, true); + regcache_mark_dirty(dcufb->tcon_regmap); + clk_disable_unprepare(dcufb->tcon_clk); + } + + if (dcufb->scfg_regmap) { + regcache_cache_only(dcufb->scfg_regmap, true); + regcache_mark_dirty(dcufb->scfg_regmap); + } + + return 0; +} + +static int fsl_dcu_resume(struct device *dev) +{ + struct dcu_fb_data *dcufb = dev_get_drvdata(dev); + struct device_node *np = dev->of_node; + + /* Enable clocks and restore all registers */ + if (dcufb->tcon_regmap) { + clk_prepare_enable(dcufb->tcon_clk); + regcache_cache_only(dcufb->tcon_regmap, false); + regcache_sync(dcufb->tcon_regmap); + } + + if (dcufb->scfg_regmap) { + regcache_cache_only(dcufb->scfg_regmap, false); + regcache_sync(dcufb->scfg_regmap); + } + + clk_prepare_enable(dcufb->clk); + reset_total_layers(np, dcufb); + regcache_cache_only(dcufb->regmap, false); + regcache_sync(dcufb->regmap); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_dcu_pm_ops = { + SET_RUNTIME_PM_OPS(fsl_dcu_runtime_suspend, + fsl_dcu_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(fsl_dcu_suspend, fsl_dcu_resume) +}; + +static struct of_device_id fsl_dcu_dt_ids[] = { + { .compatible = "fsl,vf610-dcu", }, + { .compatible = "fsl,ls1021a-dcu", }, + {} +}; + +static struct platform_driver fsl_dcu_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = fsl_dcu_dt_ids, + .pm = &fsl_dcu_pm_ops, + }, + .probe = fsl_dcu_probe, + .remove = fsl_dcu_remove, +}; + +module_platform_driver(fsl_dcu_driver); + +MODULE_AUTHOR("Alison Wang"); +MODULE_DESCRIPTION("Freescale DCU framebuffer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/fsl-sii902x.c b/drivers/video/fsl-sii902x.c new file mode 100644 index 0000000..7b0145f --- /dev/null +++ b/drivers/video/fsl-sii902x.c @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2010-2014 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 <linux/console.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/fsl_devices.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include "edid.h" + +#define SII902X_INPUT_BUS_FMT 0x08 +#define SII902X_TPI_AVI_INPUT_FMT 0x09 +#define SII902X_TPI_AVI_OUTPUT_FMT 0x0A +#define SII902X_SYS_CONTROL 0x1A +#define SII902X_SYS_CTR_DDC_REQ BIT(2) +#define SII902X_SYS_CTR_DDC_BUS_AVAI (BIT(2) | BIT(1)) +#define SII902X_TPI_FAMILY_DEV_ID 0x1B +#define SII902X_TPI_DEV_REV_ID 0x1C +#define SII902X_TPI_REV_LEVEL_ID 0x1D +#define SII902X_POWER_STATE 0x1E +#define SII902X_TPI_AUDIO_CFG0 0x24 +#define SII902X_TPI_AUDIO_CFG1 0x25 +#define SII902X_TPI_AUDIO_CFG2 0x26 +#define SII902X_TPI_AUDIO_CFG3 0x27 +#define SII902X_TPI_HDCP_REV 0x30 +#define SII902X_TPI_INT_ENABLE 0x3C +#define SII902X_TPI_INT_STATUS 0x3D +#define SII902X_TPI_INT_PLUG_IN BIT(2) +#define SII902X_GENERAL_PURPOSE_IO0 0xBC +#define SII902X_GENERAL_PURPOSE_IO1 0xBD +#define SII902X_GENERAL_PURPOSE_IO2 0xBE +#define SII902X_TRANS_MODE_DIFF 0xC7 + +bool g_enable_hdmi; + +struct sii902x_data { + struct i2c_client *client; + struct delayed_work det_work; + struct fb_info *fbi; + struct regmap *regmap; + unsigned int irq; + u8 cable_plugin; +} *sii902x; + +static struct i2c_client *sii902x_to_i2c(struct sii902x_data *sii902x) +{ + return sii902x->client; +} + +static s32 sii902x_write(const struct i2c_client *client, + u8 command, u8 value) +{ + return i2c_smbus_write_byte_data(client, command, value); +} + +static s32 sii902x_read(const struct i2c_client *client, u8 command) +{ + int val; + + val = i2c_smbus_read_word_data(client, command); + + return val & 0xff; +} + +static ssize_t sii902x_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + strcpy(buf, sii902x->fbi->fix.id); + sprintf(buf+strlen(buf), "\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(fb_name, S_IRUGO, sii902x_show_name, NULL); + +static ssize_t sii902x_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (sii902x->cable_plugin == 0) + strcpy(buf, "plugout\n"); + else + strcpy(buf, "plugin\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(cable_state, S_IRUGO, sii902x_show_state, NULL); + +static void sii902x_power_up_tx(struct sii902x_data *sii902x) +{ + struct i2c_client *client = sii902x_to_i2c(sii902x); + int val; + + val = sii902x_read(client, SII902X_POWER_STATE); + val &= ~0x3; + sii902x_write(client, SII902X_POWER_STATE, val); +} + +static void sii902x_setup(struct fb_info *fbi) +{ + u16 data[4]; + u32 refresh; + u8 *tmp; + int i; + + /* Power up */ + sii902x_power_up_tx(sii902x); + + /* set TPI video mode */ + data[0] = PICOS2KHZ(fbi->var.pixclock) / 10; + data[2] = fbi->var.hsync_len + fbi->var.left_margin + + fbi->var.xres + fbi->var.right_margin; + data[3] = fbi->var.vsync_len + fbi->var.upper_margin + + fbi->var.yres + fbi->var.lower_margin; + refresh = data[2] * data[3]; + refresh = (PICOS2KHZ(fbi->var.pixclock) * 1000) / refresh; + data[1] = refresh * 100; + tmp = (u8 *)data; + for (i = 0; i < 8; i++) + sii902x_write(sii902x->client, i, tmp[i]); + + /* input bus/pixel: full pixel wide (24bit), rising edge */ + sii902x_write(sii902x->client, SII902X_INPUT_BUS_FMT, 0x70); + /* Set input format to RGB */ + sii902x_write(sii902x->client, SII902X_TPI_AVI_INPUT_FMT, 0x00); + /* set output format to RGB */ + sii902x_write(sii902x->client, SII902X_TPI_AVI_OUTPUT_FMT, 0x00); + /* audio setup */ + sii902x_write(sii902x->client, SII902X_TPI_AUDIO_CFG1, 0x00); + sii902x_write(sii902x->client, SII902X_TPI_AUDIO_CFG2, 0x40); + sii902x_write(sii902x->client, SII902X_TPI_AUDIO_CFG3, 0x00); +} + +static int __sii902x_read_edid(struct i2c_adapter *adp, + unsigned char *edid, u8 *buf) +{ + unsigned short addr = 0x50; + int ret; + + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = 0, + .len = 1, + .buf = buf, + }, { + .addr = addr, + .flags = I2C_M_RD, + .len = EDID_LENGTH, + .buf = edid, + }, + }; + + if (adp == NULL) + return -EINVAL; + + memset(edid, 0, EDID_LENGTH); + + ret = i2c_transfer(adp, msg, 2); + if (ret < 0) + return ret; + + /* If 0x50 fails, try 0x37. */ + if (edid[1] == 0x00) { + msg[0].addr = msg[1].addr = 0x37; + ret = i2c_transfer(adp, msg, 2); + if (ret < 0) + return ret; + } + + if (edid[1] == 0x00) + return -ENOENT; + + return 0; +} +/* make sure edid has 256 bytes*/ +static int __sii902x_get_edid(struct i2c_adapter *adp, + struct fb_info *fbi) +{ + u8 *edid; + u8 buf[2] = {0, 0}; + int num, ret; + + edid = kzalloc(EDID_LENGTH, GFP_KERNEL); + if (!edid) + return -ENOMEM; + + ret = __sii902x_read_edid(adp, edid, buf); + if (ret) + return ret; + + /* edid first block parsing */ + memset(&fbi->monspecs, 0, sizeof(fbi->monspecs)); + fb_edid_to_monspecs(edid, &fbi->monspecs); + + /* need read ext block? Only support one more blk now */ + num = edid[0x7E]; + if (num) { + buf[0] = 0x80; + ret = __sii902x_read_edid(adp, edid, buf); + if (ret) + return ret; + + fb_edid_add_monspecs(edid, &fbi->monspecs); + } + + kfree(edid); + return 0; +} +static int sii902x_get_edid(struct fb_info *fbi) +{ + int old, dat, ret, cnt = 100; + + old = sii902x_read(sii902x->client, SII902X_SYS_CONTROL); + + sii902x_write(sii902x->client, SII902X_SYS_CONTROL, + old | SII902X_SYS_CTR_DDC_REQ); + do { + cnt--; + msleep(20); + dat = sii902x_read(sii902x->client, SII902X_SYS_CONTROL); + } while ((!(dat & 0x2)) && cnt); + + if (!cnt) { + ret = -1; + goto done; + } + + sii902x_write(sii902x->client, SII902X_SYS_CONTROL, + old | SII902X_SYS_CTR_DDC_BUS_AVAI); + + /* edid reading */ + ret = __sii902x_get_edid(sii902x->client->adapter, fbi); + + cnt = 100; + do { + cnt--; + sii902x_write(sii902x->client, SII902X_SYS_CONTROL, + old & ~SII902X_SYS_CTR_DDC_BUS_AVAI); + msleep(20); + dat = sii902x_read(sii902x->client, SII902X_SYS_CONTROL); + } while ((dat & 0x6) && cnt); + + if (!cnt) + ret = -1; + +done: + sii902x_write(sii902x->client, SII902X_SYS_CONTROL, old); + return ret; +} + +static void det_worker(struct work_struct *work) +{ + struct fb_info *fbi = sii902x->fbi; + struct fb_monspecs *monspecs = &fbi->monspecs; + int val, ret; + char event_string[16]; + char *envp[] = { event_string, NULL }; + + val = sii902x_read(sii902x->client, SII902X_TPI_INT_STATUS); + if (!(val & 0x1) && !g_enable_hdmi) + goto err; + + /* cable connection changes */ + if (val & SII902X_TPI_INT_PLUG_IN || g_enable_hdmi) { + sii902x->cable_plugin = 1; + sprintf(event_string, "EVENT=plugin"); + + ret = sii902x_get_edid(fbi); + if (ret < 0) { + dev_err(&sii902x->client->dev, "read edid fail\n"); + goto err; + } + + /* make sure fb is powerdown */ + console_lock(); + fb_blank(fbi, FB_BLANK_POWERDOWN); + console_unlock(); + + if (monspecs->modedb_len > 0) { + int i; + const struct fb_videomode *mode; + struct fb_videomode m; + + fb_destroy_modelist(&fbi->modelist); + + for (i = 0; i < monspecs->modedb_len; i++) { + /* We do not support interlaced mode for now */ + if (!(monspecs->modedb[i].vmode & + FB_VMODE_INTERLACED)) + fb_add_videomode(&monspecs->modedb[i], + &fbi->modelist); + } + + fb_var_to_videomode(&m, &fbi->var); + mode = fb_find_nearest_mode(&m, + &fbi->modelist); + + fb_videomode_to_var(&fbi->var, mode); + + fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + fb_set_var(fbi, &fbi->var); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + } + + console_lock(); + fb_blank(fbi, FB_BLANK_UNBLANK); + console_unlock(); + } else { + sii902x->cable_plugin = 0; + sprintf(event_string, "EVENT=plugout"); + console_lock(); + fb_blank(fbi, FB_BLANK_POWERDOWN); + console_unlock(); + } + kobject_uevent_env(&sii902x->client->dev.kobj, + KOBJ_CHANGE, envp); + +err: + sii902x_write(sii902x->client, SII902X_TPI_INT_STATUS, val); +} + +static irqreturn_t sii902x_detect_handler(int irq, void *data) +{ + if (g_enable_hdmi) + g_enable_hdmi = false; + + if (sii902x->fbi) + schedule_delayed_work(&(sii902x->det_work), + msecs_to_jiffies(20)); + return IRQ_HANDLED; +} + +static void sii902x_poweron(void) +{ + /* Turn on DVI or HDMI */ + sii902x_write(sii902x->client, SII902X_SYS_CONTROL, 0x00); +} + +static void sii902x_poweroff(void) +{ + /* disable tmds before changing resolution */ + sii902x_write(sii902x->client, SII902X_SYS_CONTROL, 0x10); +} + +static int sii902x_fb_event(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct fb_event *event = v; + struct fb_info *fbi = event->info; + + switch (val) { + case FB_EVENT_FB_REGISTERED: + if (sii902x->fbi != NULL) + break; + sii902x->fbi = fbi; + if (g_enable_hdmi && sii902x->fbi) { + schedule_delayed_work(&(sii902x->det_work), + msecs_to_jiffies(20)); + } + break; + case FB_EVENT_MODE_CHANGE: + sii902x_setup(fbi); + break; + case FB_EVENT_BLANK: + if (*((int *)event->data) == FB_BLANK_UNBLANK) + sii902x_poweron(); + else + sii902x_poweroff(); + break; + } + + return 0; +} + +static void sii902x_chip_id(struct sii902x_data *sii902x) +{ + struct i2c_client *client = sii902x_to_i2c(sii902x); + int val; + + /* read device ID */ + val = sii902x_read(client, SII902X_TPI_FAMILY_DEV_ID); + pr_info("Sii902x: read id = 0x%02X", val); + val = sii902x_read(client, SII902X_TPI_DEV_REV_ID); + pr_info("-0x%02X", val); + val = sii902x_read(client, SII902X_TPI_REV_LEVEL_ID); + pr_info("-0x%02X", val); + val = sii902x_read(client, SII902X_TPI_HDCP_REV); + pr_info("-0x%02X\n", val); +} + +static int sii902x_initialize(struct sii902x_data *sii902x) +{ + struct i2c_client *client = sii902x_to_i2c(sii902x); + int ret, cnt; + + for (cnt = 0; cnt < 5; cnt++) { + /* Set 902x in hardware TPI mode on and jump out of D3 state */ + ret = sii902x_write(client, SII902X_TRANS_MODE_DIFF, 0x00); + if (ret < 0) + break; + } + if (0 != ret) + dev_err(&client->dev, "cound not find device\n"); + + return ret; +} + +static void sii902x_enable_source(struct sii902x_data *sii902x) +{ + struct i2c_client *client = sii902x_to_i2c(sii902x); + int val; + + sii902x_write(client, SII902X_GENERAL_PURPOSE_IO0, 0x01); + sii902x_write(client, SII902X_GENERAL_PURPOSE_IO1, 0x82); + val = sii902x_read(client, SII902X_GENERAL_PURPOSE_IO2); + val |= 0x1; + sii902x_write(client, SII902X_GENERAL_PURPOSE_IO2, val); +} + +static struct notifier_block nb = { + .notifier_call = sii902x_fb_event, +}; + +static int sii902x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent); + int ret, err; + struct fb_info edid_fbi; + + if (!g_enable_hdmi) + return -EPERM; + + if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE)) { + dev_err(&client->dev, "i2c_check_functionality error\n"); + return -ENODEV; + } + + sii902x = devm_kzalloc(&client->dev, sizeof(*sii902x), GFP_KERNEL); + if (!sii902x) + return -ENOMEM; + + sii902x->client = client; + i2c_set_clientdata(client, sii902x); + + err = sii902x_initialize(sii902x); + if (err) + return err; + + sii902x_chip_id(sii902x); + sii902x_power_up_tx(sii902x); + sii902x_enable_source(sii902x); + + /* try to read edid */ + if (sii902x_get_edid(&edid_fbi) < 0) + dev_warn(&client->dev, "Can not read edid\n"); + + if (client->irq) { + ret = devm_request_irq(&client->dev, client->irq, + sii902x_detect_handler, 0, + "SII902x_det", sii902x); + if (ret < 0) + dev_warn(&client->dev, + "cound not request det irq %d\n", + client->irq); + else { + INIT_DELAYED_WORK(&(sii902x->det_work), det_worker); + /*enable cable hot plug irq*/ + sii902x_write(client, SII902X_TPI_INT_ENABLE, 0x01); + } + ret = device_create_file(&client->dev, &dev_attr_fb_name); + if (ret < 0) + dev_warn(&client->dev, + "cound not create sys node for fb name\n"); + ret = device_create_file(&client->dev, &dev_attr_cable_state); + if (ret < 0) + dev_warn(&client->dev, + "cound not create sys node for cable state\n"); + } + + fb_register_client(&nb); + + return 0; +} + +static int sii902x_remove(struct i2c_client *client) +{ + fb_unregister_client(&nb); + sii902x_poweroff(); + return 0; +} + +static const struct i2c_device_id sii902x_id[] = { + { "sii902x", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sii902x_id); + +static const struct of_device_id sii902x_dt_ids[] = { + { .compatible = "fsl,sii902x", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sii902x_dt_ids); + +static struct i2c_driver sii902x_i2c_driver = { + .driver = { + .name = "sii902x", + .owner = THIS_MODULE, + .of_match_table = sii902x_dt_ids, + }, + .probe = sii902x_probe, + .remove = sii902x_remove, + .id_table = sii902x_id, +}; +module_i2c_driver(sii902x_i2c_driver); + +static int __init enable_hdmi_setup(char *str) +{ + g_enable_hdmi = true; + + return 1; +} +__setup("hdmi", enable_hdmi_setup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("SII902x DVI/HDMI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b27b75d..d201a49 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -359,6 +359,8 @@ config MAX63XX_WATCHDOG config IMX2_WDT tristate "IMX2+ Watchdog" depends on ARCH_MXC + select REGMAP_MMIO + select WATCHDOG_CORE help This is the driver for the hardware watchdog on the Freescale IMX2 and later processors. diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 693ac3f..729b09f 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -2,6 +2,7 @@ * Watchdog driver for IMX2 and later processors * * Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. <w.sang@pengutronix.de> + * Copyright (C) 2014 Freescale Semiconductor, Inc. * * some parts adapted by similar drivers from Darius Augulis and Vladimir * Zapolskiy, additional improvements by Wim Van Sebroeck. @@ -20,19 +21,18 @@ * Halt on suspend: Manual Can be automatic */ +#include <linux/clk.h> #include <linux/init.h> +#include <linux/io.h> +#include <linux/jiffies.h> #include <linux/kernel.h> -#include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/of_address.h> #include <linux/platform_device.h> -#include <linux/watchdog.h> -#include <linux/clk.h> -#include <linux/fs.h> -#include <linux/io.h> -#include <linux/uaccess.h> +#include <linux/regmap.h> #include <linux/timer.h> -#include <linux/jiffies.h> +#include <linux/watchdog.h> #define DRIVER_NAME "imx2-wdt" @@ -40,6 +40,7 @@ #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ #define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */ #define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */ +#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */ #define IMX2_WDT_WSR 0x02 /* Service Register */ #define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */ @@ -53,19 +54,12 @@ #define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8) -#define IMX2_WDT_STATUS_OPEN 0 -#define IMX2_WDT_STATUS_STARTED 1 -#define IMX2_WDT_EXPECT_CLOSE 2 - -static struct { +struct imx2_wdt_device { struct clk *clk; - void __iomem *base; - unsigned timeout; - unsigned long status; + struct regmap *regmap; struct timer_list timer; /* Pings the watchdog when closed */ -} imx2_wdt; - -static struct miscdevice imx2_wdt_miscdev; + struct watchdog_device wdog; +}; static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); @@ -83,10 +77,15 @@ static const struct watchdog_info imx2_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; -static inline void imx2_wdt_setup(void) +static inline void imx2_wdt_setup(struct watchdog_device *wdog) { - u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + u32 val; + regmap_read(wdev->regmap, IMX2_WDT_WCR, &val); + + /* Suspend timer in low power mode, write once-only */ + val |= IMX2_WDT_WCR_WDZST; /* Strip the old watchdog Time-Out value */ val &= ~IMX2_WDT_WCR_WT; /* Generate reset if WDOG times out */ @@ -94,234 +93,272 @@ static inline void imx2_wdt_setup(void) /* Keep Watchdog Disabled */ val &= ~IMX2_WDT_WCR_WDE; /* Set the watchdog's Time-Out value */ - val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout); + val |= WDOG_SEC_TO_COUNT(wdog->timeout); - __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); + regmap_write(wdev->regmap, IMX2_WDT_WCR, val); /* enable the watchdog */ val |= IMX2_WDT_WCR_WDE; - __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); + regmap_write(wdev->regmap, IMX2_WDT_WCR, val); } -static inline void imx2_wdt_ping(void) +static inline bool imx2_wdt_is_running(struct imx2_wdt_device *wdev) { - __raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR); - __raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR); -} + u32 val; -static void imx2_wdt_timer_ping(unsigned long arg) -{ - /* ping it every imx2_wdt.timeout / 2 seconds to prevent reboot */ - imx2_wdt_ping(); - mod_timer(&imx2_wdt.timer, jiffies + imx2_wdt.timeout * HZ / 2); + regmap_read(wdev->regmap, IMX2_WDT_WCR, &val); + + return val & IMX2_WDT_WCR_WDE; } -static void imx2_wdt_start(void) +static int imx2_wdt_ping(struct watchdog_device *wdog) { - if (!test_and_set_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) { - /* at our first start we enable clock and do initialisations */ - clk_prepare_enable(imx2_wdt.clk); - - imx2_wdt_setup(); - } else /* delete the timer that pings the watchdog after close */ - del_timer_sync(&imx2_wdt.timer); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); - /* Watchdog is enabled - time to reload the timeout value */ - imx2_wdt_ping(); + regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ1); + regmap_write(wdev->regmap, IMX2_WDT_WSR, IMX2_WDT_SEQ2); + return 0; } -static void imx2_wdt_stop(void) +static void imx2_wdt_timer_ping(unsigned long arg) { - /* we don't need a clk_disable, it cannot be disabled once started. - * We use a timer to ping the watchdog while /dev/watchdog is closed */ - imx2_wdt_timer_ping(0); + struct watchdog_device *wdog = (struct watchdog_device *)arg; + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + /* ping it every wdog->timeout / 2 seconds to prevent reboot */ + imx2_wdt_ping(wdog); + mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2); } -static void imx2_wdt_set_timeout(int new_timeout) +static int imx2_wdt_set_timeout(struct watchdog_device *wdog, + unsigned int new_timeout) { - u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); - /* set the new timeout value in the WSR */ - val &= ~IMX2_WDT_WCR_WT; - val |= WDOG_SEC_TO_COUNT(new_timeout); - __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR); + regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT, + WDOG_SEC_TO_COUNT(new_timeout)); + return 0; } -static int imx2_wdt_open(struct inode *inode, struct file *file) +static int imx2_wdt_start(struct watchdog_device *wdog) { - if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status)) - return -EBUSY; + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + if (imx2_wdt_is_running(wdev)) { + /* delete the timer that pings the watchdog after close */ + del_timer_sync(&wdev->timer); + imx2_wdt_set_timeout(wdog, wdog->timeout); + } else + imx2_wdt_setup(wdog); - imx2_wdt_start(); - return nonseekable_open(inode, file); + return imx2_wdt_ping(wdog); } -static int imx2_wdt_close(struct inode *inode, struct file *file) +static int imx2_wdt_stop(struct watchdog_device *wdog) { - if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout) - imx2_wdt_stop(); - else { - dev_crit(imx2_wdt_miscdev.parent, - "Unexpected close: Expect reboot!\n"); - imx2_wdt_ping(); - } - - clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status); - clear_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status); + /* + * We don't need a clk_disable, it cannot be disabled once started. + * We use a timer to ping the watchdog while /dev/watchdog is closed + */ + imx2_wdt_timer_ping((unsigned long)wdog); return 0; } -static long imx2_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog) { - void __user *argp = (void __user *)arg; - int __user *p = argp; - int new_value; - u16 val; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &imx2_wdt_info, - sizeof(struct watchdog_info)) ? -EFAULT : 0; - - case WDIOC_GETSTATUS: - return put_user(0, p); - - case WDIOC_GETBOOTSTATUS: - val = __raw_readw(imx2_wdt.base + IMX2_WDT_WRSR); - new_value = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; - return put_user(new_value, p); - - case WDIOC_KEEPALIVE: - imx2_wdt_ping(); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_value, p)) - return -EFAULT; - if ((new_value < 1) || (new_value > IMX2_WDT_MAX_TIME)) - return -EINVAL; - imx2_wdt_set_timeout(new_value); - imx2_wdt.timeout = new_value; - imx2_wdt_ping(); - - /* Fallthrough to return current value */ - case WDIOC_GETTIMEOUT: - return put_user(imx2_wdt.timeout, p); - - default: - return -ENOTTY; - } -} + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); -static ssize_t imx2_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - size_t i; - char c; - - if (len == 0) /* Can we see this even ? */ - return 0; - - clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status); - /* scan to see whether or not we got the magic character */ - for (i = 0; i != len; i++) { - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status); + if (imx2_wdt_is_running(wdev)) { + imx2_wdt_set_timeout(wdog, wdog->timeout); + imx2_wdt_timer_ping((unsigned long)wdog); } - - imx2_wdt_ping(); - return len; } -static const struct file_operations imx2_wdt_fops = { +static struct watchdog_ops imx2_wdt_ops = { .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = imx2_wdt_ioctl, - .open = imx2_wdt_open, - .release = imx2_wdt_close, - .write = imx2_wdt_write, + .start = imx2_wdt_start, + .stop = imx2_wdt_stop, + .ping = imx2_wdt_ping, + .set_timeout = imx2_wdt_set_timeout, }; -static struct miscdevice imx2_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &imx2_wdt_fops, +static struct regmap_config imx2_wdt_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x8, }; static int __init imx2_wdt_probe(struct platform_device *pdev) { - int ret; + struct device_node *np = pdev->dev.of_node; + struct imx2_wdt_device *wdev; + struct watchdog_device *wdog; struct resource *res; + void __iomem *base; + bool big_endian; + int ret; + u32 val; + + wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) + return -ENOMEM; + + big_endian = of_property_read_bool(np, "big-endian"); + if (big_endian) + imx2_wdt_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - imx2_wdt.base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(imx2_wdt.base)) - return PTR_ERR(imx2_wdt.base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + &imx2_wdt_regmap_config); + if (IS_ERR(wdev->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(wdev->regmap); + } - imx2_wdt.clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(imx2_wdt.clk)) { + wdev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdev->clk)) { dev_err(&pdev->dev, "can't get Watchdog clock\n"); - return PTR_ERR(imx2_wdt.clk); + return PTR_ERR(wdev->clk); } - imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME); - if (imx2_wdt.timeout != timeout) - dev_warn(&pdev->dev, "Initial timeout out of range! " - "Clamped from %u to %u\n", timeout, imx2_wdt.timeout); + wdog = &wdev->wdog; + wdog->info = &imx2_wdt_info; + wdog->ops = &imx2_wdt_ops; + wdog->min_timeout = 1; + wdog->max_timeout = IMX2_WDT_MAX_TIME; - setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0); + clk_prepare_enable(wdev->clk); - imx2_wdt_miscdev.parent = &pdev->dev; - ret = misc_register(&imx2_wdt_miscdev); - if (ret) - goto fail; + regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); + wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; - dev_info(&pdev->dev, - "IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n", - imx2_wdt.timeout, nowayout); - return 0; + wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME); + if (wdog->timeout != timeout) + dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n", + timeout, wdog->timeout); + + platform_set_drvdata(pdev, wdog); + watchdog_set_drvdata(wdog, wdev); + watchdog_set_nowayout(wdog, nowayout); + watchdog_init_timeout(wdog, timeout, &pdev->dev); + + setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog); -fail: - imx2_wdt_miscdev.parent = NULL; - return ret; + imx2_wdt_ping_if_active(wdog); + + ret = watchdog_register_device(wdog); + if (ret) { + dev_err(&pdev->dev, "cannot register watchdog device\n"); + return ret; + } + + dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", + wdog->timeout, nowayout); + + return 0; } static int __exit imx2_wdt_remove(struct platform_device *pdev) { - misc_deregister(&imx2_wdt_miscdev); + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); - if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) { - del_timer_sync(&imx2_wdt.timer); + watchdog_unregister_device(wdog); - dev_crit(imx2_wdt_miscdev.parent, - "Device removed: Expect reboot!\n"); + if (imx2_wdt_is_running(wdev)) { + del_timer_sync(&wdev->timer); + imx2_wdt_ping(wdog); + dev_crit(&pdev->dev, "Device removed: Expect reboot!\n"); } - - imx2_wdt_miscdev.parent = NULL; return 0; } static void imx2_wdt_shutdown(struct platform_device *pdev) { - if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) { - /* we are running, we need to delete the timer but will give - * max timeout before reboot will take place */ - del_timer_sync(&imx2_wdt.timer); - imx2_wdt_set_timeout(IMX2_WDT_MAX_TIME); - imx2_wdt_ping(); - - dev_crit(imx2_wdt_miscdev.parent, - "Device shutdown: Expect reboot!\n"); + struct watchdog_device *wdog = platform_get_drvdata(pdev); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + if (imx2_wdt_is_running(wdev)) { + /* + * We are running, we need to delete the timer but will + * give max timeout before reboot will take place + */ + del_timer_sync(&wdev->timer); + imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); + imx2_wdt_ping(wdog); + dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n"); + } +} + +#ifdef CONFIG_PM_SLEEP +/* Disable watchdog if it is active or non-active but still running */ +static int imx2_wdt_suspend(struct device *dev) +{ + struct watchdog_device *wdog = dev_get_drvdata(dev); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + /* The watchdog IP block is running */ + if (imx2_wdt_is_running(wdev)) { + imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); + imx2_wdt_ping(wdog); + + /* The watchdog is not active */ + if (!watchdog_active(wdog)) + del_timer_sync(&wdev->timer); } + + clk_disable_unprepare(wdev->clk); + + return 0; } +/* Enable watchdog and configure it if necessary */ +static int imx2_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdog = dev_get_drvdata(dev); + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + clk_prepare_enable(wdev->clk); + + if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { + /* + * If the watchdog is still active and resumes + * from deep sleep state, need to restart the + * watchdog again. + */ + imx2_wdt_setup(wdog); + imx2_wdt_set_timeout(wdog, wdog->timeout); + imx2_wdt_ping(wdog); + } else if (imx2_wdt_is_running(wdev)) { + /* Resuming from non-deep sleep state. */ + imx2_wdt_set_timeout(wdog, wdog->timeout); + imx2_wdt_ping(wdog); + /* + * But the watchdog is not active, then start + * the timer again. + */ + if (!watchdog_active(wdog)) + mod_timer(&wdev->timer, + jiffies + wdog->timeout * HZ / 2); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, + imx2_wdt_resume); + static const struct of_device_id imx2_wdt_dt_ids[] = { { .compatible = "fsl,imx21-wdt", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx2_wdt_dt_ids); static struct platform_driver imx2_wdt_driver = { .remove = __exit_p(imx2_wdt_remove), @@ -329,6 +366,7 @@ static struct platform_driver imx2_wdt_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .pm = &imx2_wdt_pm_ops, .of_match_table = imx2_wdt_dt_ids, }, }; @@ -338,5 +376,4 @@ module_platform_driver_probe(imx2_wdt_driver, imx2_wdt_probe); MODULE_AUTHOR("Wolfram Sang"); MODULE_DESCRIPTION("Watchdog driver for IMX2 and later"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/include/dt-bindings/clock/ls1021a-clock.h b/include/dt-bindings/clock/ls1021a-clock.h new file mode 100644 index 0000000..09b47d7 --- /dev/null +++ b/include/dt-bindings/clock/ls1021a-clock.h @@ -0,0 +1,55 @@ +/* + * Copyright 2014 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_LS1021A_H +#define __DT_BINDINGS_CLOCK_LS1021A_H + +#define LS1021A_CLK_DUMMY 0 +#define LS1021A_CLK_PBL_EN 1 +#define LS1021A_CLK_ESDHC_EN 2 +#define LS1021A_CLK_DMA1_EN 3 +#define LS1021A_CLK_DMA2_EN 4 +#define LS1021A_CLK_USB3_PHY_EN 5 +#define LS1021A_CLK_USB2_EN 6 +#define LS1021A_CLK_SATA_EN 7 +#define LS1021A_CLK_USB3_EN 8 +#define LS1021A_CLK_SEC_EN 9 +#define LS1021A_CLK_2D_ACE_EN 10 +#define LS1021A_CLK_QE_EN 11 +#define LS1021A_CLK_ETSEC1_EN 12 +#define LS1021A_CLK_ETSEC2_EN 13 +#define LS1021A_CLK_ETSEC3_EN 14 +#define LS1021A_CLK_PEX1_EN 15 +#define LS1021A_CLK_PEX2_EN 16 +#define LS1021A_CLK_DUART1_EN 17 +#define LS1021A_CLK_DUART2_EN 18 +#define LS1021A_CLK_QSPI_EN 19 +#define LS1021A_CLK_DDR_EN 20 +#define LS1021A_CLK_OCRAM1_EN 21 +#define LS1021A_CLK_IFC_EN 22 +#define LS1021A_CLK_GPIO_EN 23 +#define LS1021A_CLK_DBG_EN 24 +#define LS1021A_CLK_FLEXCAN1_EN 25 +#define LS1021A_CLK_FLEXCAN234_EN 26 +#define LS1021A_CLK_FLEXTIMER_EN 27 +#define LS1021A_CLK_SECMON_EN 28 +#define LS1021A_CLK_WDOG_EN 29 +#define LS1021A_CLK_WDOG12_EN 30 +#define LS1021A_CLK_I2C23_EN 31 +#define LS1021A_CLK_SAI_EN 32 +#define LS1021A_CLK_LPUART_EN 33 +#define LS1021A_CLK_DSPI12_EN 34 +#define LS1021A_CLK_ASRC_EN 35 +#define LS1021A_CLK_SPDIF_EN 36 +#define LS1021A_CLK_I2C1_EN 37 +#define LS1021A_CLK_LPUART1_EN 38 +#define LS1021A_CLK_FLEXTIMER1_EN 39 +#define LS1021A_CLK_END 40 + +#endif /* __DT_BINDINGS_CLOCK_LS1021A_H */ diff --git a/include/linux/fsl/bestcomm/sram.h b/include/linux/fsl/bestcomm/sram.h index b6d6689..8555dab 100644 --- a/include/linux/fsl/bestcomm/sram.h +++ b/include/linux/fsl/bestcomm/sram.h @@ -12,7 +12,7 @@ #ifndef __BESTCOMM_SRAM_H__ #define __BESTCOMM_SRAM_H__ -#include <asm/rheap.h> +#include <linux/fsl/rheap.h> #include <asm/mmu.h> #include <linux/spinlock.h> @@ -25,7 +25,7 @@ struct bcom_sram { phys_addr_t base_phys; void *base_virt; unsigned int size; - rh_info_t *rh; + struct _rh_info *rh; spinlock_t lock; }; diff --git a/arch/powerpc/include/asm/immap_qe.h b/include/linux/fsl/immap_qe.h index c76ef30..bd1ebd4 100644 --- a/arch/powerpc/include/asm/immap_qe.h +++ b/include/linux/fsl/immap_qe.h @@ -2,11 +2,11 @@ * QUICC Engine (QE) Internal Memory Map. * The Internal Memory Map for devices with QE on them. This * is the superset of all QE devices (8360, etc.). - * Copyright (C) 2006. Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: + * Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * 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 @@ -18,7 +18,7 @@ #ifdef __KERNEL__ #include <linux/kernel.h> -#include <asm/io.h> +#include <linux/io.h> #define QE_IMMAP_SIZE (1024 * 1024) /* 1MB from 1MB+IMMR */ @@ -29,7 +29,7 @@ struct qe_iram { u8 res0[0x04]; __be32 iready; /* I-RAM Ready Register */ u8 res1[0x70]; -} __attribute__ ((packed)); +} __packed; /* QE Interrupt Controller */ struct qe_ic_regs { @@ -52,7 +52,7 @@ struct qe_ic_regs { u8 res2[0x20]; __be32 qhivec; u8 res3[0x1C]; -} __attribute__ ((packed)); +} __packed; /* Communications Processor */ struct cp_qe { @@ -90,7 +90,7 @@ struct cp_qe { u8 res12[0x3A]; __be32 ceurnr; /* QE microcode revision number register */ u8 res13[0x244]; -} __attribute__ ((packed)); +} __packed; /* QE Multiplexer */ struct qe_mux { @@ -101,7 +101,7 @@ struct qe_mux { __be32 cmxucr[4]; /* CMX UCCx clock route registers */ __be32 cmxupcr; /* CMX UPC clock route register */ u8 res0[0x1C]; -} __attribute__ ((packed)); +} __packed; /* QE Timers */ struct qe_timers { @@ -131,13 +131,13 @@ struct qe_timers { __be16 gtevr4; /* Timer 4 event register */ __be16 gtps; /* Timer 1 prescale register */ u8 res2[0x46]; -} __attribute__ ((packed)); +} __packed; /* BRG */ struct qe_brg { __be32 brgc[16]; /* BRG configuration registers */ u8 res0[0x40]; -} __attribute__ ((packed)); +} __packed; /* SPI */ struct spi { @@ -155,7 +155,7 @@ struct spi { __be32 spitd; /* SPI transmit data register (cpu mode) */ __be32 spird; /* SPI receive data register (cpu mode) */ u8 res7[0x8]; -} __attribute__ ((packed)); +} __packed; /* SI */ struct si1 { @@ -199,14 +199,14 @@ struct si1 { __be32 siml1; /* SI1 multiframe limit register */ u8 siedm1; /* SI1 extended diagnostic mode register */ u8 res9[0xBB]; -} __attribute__ ((packed)); +} __packed; /* SI Routing Tables */ struct sir { - u8 tx[0x400]; + u8 tx[0x400]; u8 rx[0x400]; u8 res0[0x800]; -} __attribute__ ((packed)); +} __packed; /* USB Controller */ struct qe_usb_ctlr { @@ -225,7 +225,7 @@ struct qe_usb_ctlr { u8 res5[2]; __be16 usb_usfrn; u8 res6[0x22]; -} __attribute__ ((packed)); +} __packed; /* MCC */ struct qe_mcc { @@ -234,7 +234,7 @@ struct qe_mcc { __be32 mccf; /* MCC configuration register */ __be32 merl; /* MCC emergency request level register */ u8 res0[0xF0]; -} __attribute__ ((packed)); +} __packed; /* QE UCC Slow */ struct ucc_slow { @@ -253,7 +253,7 @@ struct ucc_slow { __be16 utpt; u8 res4[0x52]; u8 guemr; /* UCC general extended mode register */ -} __attribute__ ((packed)); +} __packed; /* QE UCC Fast */ struct ucc_fast { @@ -285,7 +285,7 @@ struct ucc_fast { __be32 urtry; /* UCC retry counter register */ u8 res8[0x4C]; u8 guemr; /* UCC general extended mode register */ -} __attribute__ ((packed)); +} __packed; struct ucc { union { @@ -293,7 +293,7 @@ struct ucc { struct ucc_fast fast; u8 res[0x200]; /* UCC blocks are 512 bytes each */ }; -} __attribute__ ((packed)); +} __packed; /* MultiPHY UTOPIA POS Controllers (UPC) */ struct upc { @@ -349,7 +349,7 @@ struct upc { __be32 uper3; /* Device 3 port enable register */ __be32 uper4; /* Device 4 port enable register */ u8 res2[0x150]; -} __attribute__ ((packed)); +} __packed; /* SDMA */ struct sdma { @@ -369,7 +369,7 @@ struct sdma { u8 res1[0x4]; __be32 sdebcr; /* SDMA CAM entries base register */ u8 res2[0x38]; -} __attribute__ ((packed)); +} __packed; /* Debug Space */ struct dbg { @@ -386,7 +386,7 @@ struct dbg { __be32 bprmsr; /* Breakpoint request mode serial register */ __be32 bpemr; /* Breakpoint exit mode register */ u8 res2[0x48]; -} __attribute__ ((packed)); +} __packed; /* * RISC Special Registers (Trap and Breakpoint). These are described in @@ -421,7 +421,7 @@ struct rsp { __be32 eccr; /* Exception control configuration register */ __be32 eicr; u8 res4[0x100-0xf8]; -} __attribute__ ((packed)); +} __packed; struct qe_immap { struct qe_iram iram; /* I-RAM */ @@ -461,7 +461,7 @@ struct qe_immap { Multi-user RAM */ u8 res17[0x24000]; /* 0x11C000 - 0x140000 */ u8 res18[0xC0000]; /* 0x140000 - 0x200000 */ -} __attribute__ ((packed)); +} __packed; extern struct qe_immap __iomem *qe_immr; extern phys_addr_t get_qe_base(void); diff --git a/arch/powerpc/include/asm/qe.h b/include/linux/fsl/qe.h index a7387c2..ef4422c 100644 --- a/arch/powerpc/include/asm/qe.h +++ b/include/linux/fsl/qe.h @@ -1,8 +1,8 @@ /* * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * Description: * QUICC Engine (QE) external definitions and structure. @@ -19,8 +19,9 @@ #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/err.h> -#include <asm/cpm.h> -#include <asm/immap_qe.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/fsl/immap_qe.h> #define QE_NUM_OF_SNUM 256 /* There are 256 serial number in QE */ #define QE_NUM_OF_BRGS 16 @@ -190,13 +191,48 @@ static inline int qe_alive_during_sleep(void) #endif } -/* we actually use cpm_muram implementation, define this for convenience */ -#define qe_muram_init cpm_muram_init -#define qe_muram_alloc cpm_muram_alloc -#define qe_muram_alloc_fixed cpm_muram_alloc_fixed -#define qe_muram_free cpm_muram_free -#define qe_muram_addr cpm_muram_addr -#define qe_muram_offset cpm_muram_offset +int qe_muram_init(void); + +#if defined(CONFIG_QUICC_ENGINE) +unsigned long qe_muram_alloc(unsigned long size, unsigned long align); +int qe_muram_free(unsigned long offset); +unsigned long qe_muram_alloc_fixed(unsigned long offset, unsigned long size); +void __iomem *qe_muram_addr(unsigned long offset); +unsigned long qe_muram_offset(void __iomem *addr); +dma_addr_t qe_muram_dma(void __iomem *addr); +#else +static inline unsigned long qe_muram_alloc(unsigned long size, + unsigned long align) +{ + return -ENOSYS; +} + +static inline int qe_muram_free(unsigned long offset) +{ + return -ENOSYS; +} + +static inline unsigned long qe_muram_alloc_fixed(unsigned long offset, + unsigned long size) +{ + return -ENOSYS; +} + +static inline void __iomem *qe_muram_addr(unsigned long offset) +{ + return NULL; +} + +static inline unsigned long qe_muram_offset(void __iomem *addr) +{ + return -ENOSYS; +} + +static inline dma_addr_t qe_muram_dma(void __iomem *addr) +{ + return 0; +} +#endif /* defined(CONFIG_QUICC_ENGINE) */ /* Structure that defines QE firmware binary files. * @@ -213,30 +249,30 @@ struct qe_firmware { u8 split; /* 0 = shared I-RAM, 1 = split I-RAM */ u8 count; /* Number of microcode[] structures */ struct { - __be16 model; /* The SOC model */ - u8 major; /* The SOC revision major */ - u8 minor; /* The SOC revision minor */ - } __attribute__ ((packed)) soc; + __be16 model; /* The SOC model */ + u8 major; /* The SOC revision major */ + u8 minor; /* The SOC revision minor */ + } __packed soc; u8 padding[4]; /* Reserved, for alignment */ __be64 extended_modes; /* Extended modes */ __be32 vtraps[8]; /* Virtual trap addresses */ u8 reserved[4]; /* Reserved, for future expansion */ struct qe_microcode { - u8 id[32]; /* Null-terminated identifier */ + u8 id[32]; /* Null-terminated identifier */ __be32 traps[16]; /* Trap addresses, 0 == ignore */ - __be32 eccr; /* The value for the ECCR register */ + __be32 eccr; /* The value for the ECCR register */ __be32 iram_offset; /* Offset into I-RAM for the code */ - __be32 count; /* Number of 32-bit words of the code */ + __be32 count; /* Number of 32-bit words of the code */ __be32 code_offset; /* Offset of the actual microcode */ - u8 major; /* The microcode version major */ - u8 minor; /* The microcode version minor */ + u8 major; /* The microcode version major */ + u8 minor; /* The microcode version minor */ u8 revision; /* The microcode version revision */ u8 padding; /* Reserved, for alignment */ u8 reserved[4]; /* Reserved, for future expansion */ - } __attribute__ ((packed)) microcode[1]; + } __packed microcode[1]; /* All microcode binaries should be located here */ /* CRC32 should be located here, after the microcode binaries */ -} __attribute__ ((packed)); +} __packed; struct qe_firmware_info { char id[64]; /* Firmware name */ @@ -265,11 +301,32 @@ struct qe_bd { __be16 status; __be16 length; __be32 buf; -} __attribute__ ((packed)); +} __packed; #define BD_STATUS_MASK 0xffff0000 #define BD_LENGTH_MASK 0x0000ffff +/* Buffer descriptor control/status used by serial + */ + +#define BD_SC_EMPTY (0x8000) /* Receive is empty */ +#define BD_SC_READY (0x8000) /* Transmit is ready */ +#define BD_SC_WRAP (0x2000) /* Last buffer descriptor */ +#define BD_SC_INTRPT (0x1000) /* Interrupt on change */ +#define BD_SC_LAST (0x0800) /* Last buffer in frame */ +#define BD_SC_TC (0x0400) /* Transmit CRC */ +#define BD_SC_CM (0x0200) /* Continuous mode */ +#define BD_SC_ID (0x0100) /* Rec'd too many idles */ +#define BD_SC_P (0x0100) /* xmt preamble */ +#define BD_SC_BR (0x0020) /* Break received */ +#define BD_SC_FR (0x0010) /* Framing error */ +#define BD_SC_PR (0x0008) /* Parity error */ +#define BD_SC_NAK (0x0004) /* NAK - did not respond */ +#define BD_SC_OV (0x0002) /* Overrun */ +#define BD_SC_UN (0x0002) /* Underrun */ +#define BD_SC_CD (0x0001) /* */ +#define BD_SC_CL (0x0001) /* Collision */ + /* Alignment */ #define QE_INTR_TABLE_ALIGN 16 /* ??? */ #define QE_ALIGNMENT_OF_BD 8 @@ -315,14 +372,14 @@ struct qe_timer_tables { u16 r_tmv; /* QE timer valid register */ u32 tm_cmd; /* QE timer cmd register */ u32 tm_cnt; /* QE timer internal cnt */ -} __attribute__ ((packed)); +} __packed; #define QE_FLTR_TAD_SIZE 8 /* QE extended filtering Termination Action Descriptor (TAD) */ struct qe_fltr_tad { u8 serialized[QE_FLTR_TAD_SIZE]; -} __attribute__ ((packed)); +} __packed; /* Communication Direction */ enum comm_dir { @@ -542,7 +599,7 @@ struct ucc_slow_pram { __be32 ttemp; /* Tx temp */ __be32 rcrc; /* temp receive CRC */ __be32 tcrc; /* temp transmit CRC */ -} __attribute__ ((packed)); +} __packed; /* General UCC SLOW Mode Register (GUMRH & GUMRL) */ #define UCC_SLOW_GUMR_H_SAM_QMC 0x00000000 diff --git a/arch/powerpc/include/asm/qe_ic.h b/include/linux/fsl/qe_ic.h index 25784cc..79f162c 100644 --- a/arch/powerpc/include/asm/qe_ic.h +++ b/include/linux/fsl/qe_ic.h @@ -1,8 +1,8 @@ /* * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * Description: * QE IC external definitions and structure. diff --git a/arch/powerpc/include/asm/rheap.h b/include/linux/fsl/rheap.h index 1723817..88149da 100644 --- a/arch/powerpc/include/asm/rheap.h +++ b/include/linux/fsl/rheap.h @@ -16,74 +16,78 @@ #include <linux/list.h> -typedef struct _rh_block { +struct _rh_block { struct list_head list; unsigned long start; int size; const char *owner; -} rh_block_t; +}; -typedef struct _rh_info { +struct _rh_info { unsigned int alignment; int max_blocks; int empty_slots; - rh_block_t *block; + struct _rh_block *block; struct list_head empty_list; struct list_head free_list; struct list_head taken_list; unsigned int flags; -} rh_info_t; +}; #define RHIF_STATIC_INFO 0x1 #define RHIF_STATIC_BLOCK 0x2 -typedef struct _rh_stats { +struct _rh_stats { unsigned long start; int size; const char *owner; -} rh_stats_t; +}; #define RHGS_FREE 0 #define RHGS_TAKEN 1 /* Create a remote heap dynamically */ -extern rh_info_t *rh_create(unsigned int alignment); +extern struct _rh_info *rh_create(unsigned int alignment); /* Destroy a remote heap, created by rh_create() */ -extern void rh_destroy(rh_info_t * info); +extern void rh_destroy(struct _rh_info *info); /* Initialize in place a remote info block */ -extern void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks, - rh_block_t * block); +extern void rh_init(struct _rh_info *info, unsigned int alignment, + int max_blocks, struct _rh_block *block); /* Attach a free region to manage */ -extern int rh_attach_region(rh_info_t * info, unsigned long start, int size); +extern int rh_attach_region(struct _rh_info *info, unsigned long start, + int size); /* Detach a free region */ -extern unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size); +extern unsigned long rh_detach_region(struct _rh_info *info, + unsigned long start, int size); /* Allocate the given size from the remote heap (with alignment) */ -extern unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, - const char *owner); +extern unsigned long rh_alloc_align(struct _rh_info *info, int size, + int alignment, const char *owner); /* Allocate the given size from the remote heap */ -extern unsigned long rh_alloc(rh_info_t * info, int size, const char *owner); +extern unsigned long rh_alloc(struct _rh_info *info, int size, + const char *owner); /* Allocate the given size from the given address */ -extern unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, - const char *owner); +extern unsigned long rh_alloc_fixed(struct _rh_info *info, unsigned long start, + int size, const char *owner); /* Free the allocated area */ -extern int rh_free(rh_info_t * info, unsigned long start); +extern int rh_free(struct _rh_info *info, unsigned long start); /* Get stats for debugging purposes */ -extern int rh_get_stats(rh_info_t * info, int what, int max_stats, - rh_stats_t * stats); +extern int rh_get_stats(struct _rh_info *info, int what, int max_stats, + struct _rh_stats *stats); /* Simple dump of remote heap info */ -extern void rh_dump(rh_info_t * info); +extern void rh_dump(struct _rh_info *info); /* Set owner of taken block */ -extern int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner); +extern int rh_set_owner(struct _rh_info *info, unsigned long start, + const char *owner); #endif /* __ASM_PPC_RHEAP_H__ */ diff --git a/arch/powerpc/include/asm/ucc.h b/include/linux/fsl/ucc.h index 39a0bb5..622e2fc 100644 --- a/arch/powerpc/include/asm/ucc.h +++ b/include/linux/fsl/ucc.h @@ -1,8 +1,8 @@ /* * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * Description: * Internal header file for UCC unit routines. @@ -15,8 +15,8 @@ #ifndef __UCC_H__ #define __UCC_H__ -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> #define STATISTICS diff --git a/arch/powerpc/include/asm/ucc_fast.h b/include/linux/fsl/ucc_fast.h index 561b00b..ec24f28 100644 --- a/arch/powerpc/include/asm/ucc_fast.h +++ b/include/linux/fsl/ucc_fast.h @@ -3,8 +3,8 @@ * * Copyright (C) 2006, 2012 Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * 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 @@ -16,10 +16,10 @@ #include <linux/kernel.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> -#include <asm/ucc.h> +#include <linux/fsl/ucc.h> /* Receive BD's status */ #define R_E 0x80000000 /* buffer empty */ @@ -195,14 +195,15 @@ struct ucc_fast_private { * uf_info - (In) pointer to the fast UCC info structure. * uccf_ret - (Out) pointer to the fast UCC structure. */ -int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** uccf_ret); +int ucc_fast_init(struct ucc_fast_info *uf_info, + struct ucc_fast_private **uccf_ret); /* ucc_fast_free * Frees all resources for fast UCC. * * uccf - (In) pointer to the fast UCC structure. */ -void ucc_fast_free(struct ucc_fast_private * uccf); +void ucc_fast_free(struct ucc_fast_private *uccf); /* ucc_fast_enable * Enables a fast UCC port. @@ -211,7 +212,7 @@ void ucc_fast_free(struct ucc_fast_private * uccf); * uccf - (In) pointer to the fast UCC structure. * mode - (In) TX, RX, or both. */ -void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode); +void ucc_fast_enable(struct ucc_fast_private *uccf, enum comm_dir mode); /* ucc_fast_disable * Disables a fast UCC port. @@ -220,7 +221,7 @@ void ucc_fast_enable(struct ucc_fast_private * uccf, enum comm_dir mode); * uccf - (In) pointer to the fast UCC structure. * mode - (In) TX, RX, or both. */ -void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode); +void ucc_fast_disable(struct ucc_fast_private *uccf, enum comm_dir mode); /* ucc_fast_irq * Handles interrupts on fast UCC. @@ -228,7 +229,7 @@ void ucc_fast_disable(struct ucc_fast_private * uccf, enum comm_dir mode); * * uccf - (In) pointer to the fast UCC structure. */ -void ucc_fast_irq(struct ucc_fast_private * uccf); +void ucc_fast_irq(struct ucc_fast_private *uccf); /* ucc_fast_transmit_on_demand * Immediately forces a poll of the transmitter for data to be sent. @@ -241,10 +242,10 @@ void ucc_fast_irq(struct ucc_fast_private * uccf); * * uccf - (In) pointer to the fast UCC structure. */ -void ucc_fast_transmit_on_demand(struct ucc_fast_private * uccf); +void ucc_fast_transmit_on_demand(struct ucc_fast_private *uccf); u32 ucc_fast_get_qe_cr_subblock(int uccf_num); -void ucc_fast_dump_regs(struct ucc_fast_private * uccf); +void ucc_fast_dump_regs(struct ucc_fast_private *uccf); #endif /* __UCC_FAST_H__ */ diff --git a/arch/powerpc/include/asm/ucc_slow.h b/include/linux/fsl/ucc_slow.h index c44131e..56c0318 100644 --- a/arch/powerpc/include/asm/ucc_slow.h +++ b/include/linux/fsl/ucc_slow.h @@ -1,8 +1,8 @@ /* * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. * - * Authors: Shlomi Gridish <gridish@freescale.com> - * Li Yang <leoli@freescale.com> + * Authors: Shlomi Gridish <gridish@freescale.com> + * Li Yang <leoli@freescale.com> * * Description: * Internal header file for UCC SLOW unit routines. @@ -17,10 +17,10 @@ #include <linux/kernel.h> -#include <asm/immap_qe.h> -#include <asm/qe.h> +#include <linux/fsl/immap_qe.h> +#include <linux/fsl/qe.h> -#include <asm/ucc.h> +#include <linux/fsl/ucc.h> /* transmit BD's status */ #define T_R 0x80000000 /* ready bit */ @@ -224,14 +224,15 @@ struct ucc_slow_private { * us_info - (In) pointer to the slow UCC info structure. * uccs_ret - (Out) pointer to the slow UCC structure. */ -int ucc_slow_init(struct ucc_slow_info * us_info, struct ucc_slow_private ** uccs_ret); +int ucc_slow_init(struct ucc_slow_info *us_info, + struct ucc_slow_private **uccs_ret); /* ucc_slow_free * Frees all resources for slow UCC. * * uccs - (In) pointer to the slow UCC structure. */ -void ucc_slow_free(struct ucc_slow_private * uccs); +void ucc_slow_free(struct ucc_slow_private *uccs); /* ucc_slow_enable * Enables a fast UCC port. @@ -240,7 +241,7 @@ void ucc_slow_free(struct ucc_slow_private * uccs); * uccs - (In) pointer to the slow UCC structure. * mode - (In) TX, RX, or both. */ -void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode); +void ucc_slow_enable(struct ucc_slow_private *uccs, enum comm_dir mode); /* ucc_slow_disable * Disables a fast UCC port. @@ -249,7 +250,7 @@ void ucc_slow_enable(struct ucc_slow_private * uccs, enum comm_dir mode); * uccs - (In) pointer to the slow UCC structure. * mode - (In) TX, RX, or both. */ -void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode); +void ucc_slow_disable(struct ucc_slow_private *uccs, enum comm_dir mode); /* ucc_slow_poll_transmitter_now * Immediately forces a poll of the transmitter for data to be sent. @@ -262,21 +263,21 @@ void ucc_slow_disable(struct ucc_slow_private * uccs, enum comm_dir mode); * * uccs - (In) pointer to the slow UCC structure. */ -void ucc_slow_poll_transmitter_now(struct ucc_slow_private * uccs); +void ucc_slow_poll_transmitter_now(struct ucc_slow_private *uccs); /* ucc_slow_graceful_stop_tx * Smoothly stops transmission on a specified slow UCC. * * uccs - (In) pointer to the slow UCC structure. */ -void ucc_slow_graceful_stop_tx(struct ucc_slow_private * uccs); +void ucc_slow_graceful_stop_tx(struct ucc_slow_private *uccs); /* ucc_slow_stop_tx * Stops transmission on a specified slow UCC. * * uccs - (In) pointer to the slow UCC structure. */ -void ucc_slow_stop_tx(struct ucc_slow_private * uccs); +void ucc_slow_stop_tx(struct ucc_slow_private *uccs); /* ucc_slow_restart_tx * Restarts transmitting on a specified slow UCC. diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 2c4b7d3..8c87c37 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -97,6 +97,7 @@ struct fsl_usb2_platform_data { unsigned has_fsl_erratum_a006918:1; unsigned has_fsl_erratum_a005697:1; unsigned has_fsl_erratum_a007792:1; + unsigned has_fsl_erratum_14:1; /* register save area for suspend/resume */ u32 pm_command; diff --git a/arch/powerpc/include/asm/fsl_ifc.h b/include/linux/fsl_ifc.h index e06473b..3833238 100644 --- a/arch/powerpc/include/asm/fsl_ifc.h +++ b/include/linux/fsl_ifc.h @@ -832,6 +832,7 @@ struct fsl_ifc_ctrl { u32 nand_stat; wait_queue_head_t nand_wait; + bool little_endian; #ifdef CONFIG_PM_SLEEP /* save regs when system go to deep-sleep */ struct fsl_ifc_regs *saved_regs; @@ -840,5 +841,46 @@ struct fsl_ifc_ctrl { extern struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev; +static inline u32 ifc_in32(void __iomem *addr) +{ + if (fsl_ifc_ctrl_dev->little_endian) + return ioread32(addr); + else + return ioread32be(addr); +} + +static inline u16 ifc_in16(void __iomem *addr) +{ + if (fsl_ifc_ctrl_dev->little_endian) + return ioread16(addr); + else + return ioread16be(addr); +} + +static inline u8 ifc_in8(void __iomem *addr) +{ + return ioread8(addr); +} + +static inline void ifc_out32(u32 val, void __iomem *addr) +{ + if (fsl_ifc_ctrl_dev->little_endian) + return iowrite32(val, addr); + else + return iowrite32be(val, addr); +} + +static inline void ifc_out16(u16 val, void __iomem *addr) +{ + if (fsl_ifc_ctrl_dev->little_endian) + return iowrite16(val, addr); + else + return iowrite16be(val, addr); +} + +static inline void ifc_out8(u8 val, void __iomem *addr) +{ + return iowrite8(val, addr); +} #endif /* __ASM_FSL_IFC_H */ diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index b2514f7..09354f6 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -15,9 +15,19 @@ #define _HWMON_H_ struct device; +struct attribute_group; struct device *hwmon_device_register(struct device *dev); +struct device * +hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups); +struct device * +devm_hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups); void hwmon_device_unregister(struct device *dev); +void devm_hwmon_device_unregister(struct device *dev); #endif diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h new file mode 100644 index 0000000..e1bc2c3 --- /dev/null +++ b/include/linux/mtd/spi-nor.h @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2014 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 __LINUX_MTD_SPI_NOR_H +#define __LINUX_MTD_SPI_NOR_H + +/* + * Note on opcode nomenclature: some opcodes have a format like + * SPINOR_OP_FUNCTION{4,}_x_y_z{_D}. The numbers x, y,and z stand for the number + * of I/O lines used for the opcode, address, and data (respectively). The + * FUNCTION has an optional suffix of '4', to represent an opcode which + * requires a 4-byte (32-bit) address. The suffix of 'D' stands for the + * DDR mode. + */ + +/* Flash opcodes. */ +#define SPINOR_OP_WREN 0x06 /* Write enable */ +#define SPINOR_OP_RDSR 0x05 /* Read status register */ +#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ +#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */ +#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ +#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ_1_1_4_D 0x6d /* Read data bytes (DDR Quad SPI) */ +#define SPINOR_OP_READ_1_4_4_D 0xed /* Read data bytes (DDR Quad SPI) */ +#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ +#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ +#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */ +#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */ +#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ +#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */ +#define SPINOR_OP_RDCR 0x35 /* Read configuration register */ + +/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ +#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */ +#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ +#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */ +#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ +#define SPINOR_OP_READ4_1_4_4_D 0xee /* Read data bytes (DDR Quad SPI) */ +#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ + +/* Used for SST flashes only. */ +#define SPINOR_OP_BP 0x02 /* Byte program */ +#define SPINOR_OP_WRDI 0x04 /* Write disable */ +#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ + +/* Used for Macronix and Winbond flashes. */ +#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ +#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ + +/* Used for Spansion flashes only. */ +#define SPINOR_OP_BRWR 0x17 /* Bank register write */ + +/* Status Register bits. */ +#define SR_WIP 1 /* Write in progress */ +#define SR_WEL 2 /* Write enable latch */ +/* meaning of other SR_* bits may differ between vendors */ +#define SR_BP0 4 /* Block protect 0 */ +#define SR_BP1 8 /* Block protect 1 */ +#define SR_BP2 0x10 /* Block protect 2 */ +#define SR_SRWD 0x80 /* SR write protect */ + +#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ + +/* Configuration Register bits. */ +#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ + +enum read_mode { + SPI_NOR_NORMAL = 0, + SPI_NOR_FAST, + SPI_NOR_DUAL, + SPI_NOR_QUAD, + SPI_NOR_DDR_QUAD, +}; + +/** + * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer + * @wren: command for "Write Enable", or 0x00 for not required + * @cmd: command for operation + * @cmd_pins: number of pins to send @cmd (1, 2, 4) + * @addr: address for operation + * @addr_pins: number of pins to send @addr (1, 2, 4) + * @addr_width: number of address bytes + * (3,4, or 0 for address not required) + * @mode: mode data + * @mode_pins: number of pins to send @mode (1, 2, 4) + * @mode_cycles: number of mode cycles (0 for mode not required) + * @dummy_cycles: number of dummy cycles (0 for dummy not required) + */ +struct spi_nor_xfer_cfg { + u8 wren; + u8 cmd; + u8 cmd_pins; + u32 addr; + u8 addr_pins; + u8 addr_width; + u8 mode; + u8 mode_pins; + u8 mode_cycles; + u8 dummy_cycles; +}; + +#define SPI_NOR_MAX_CMD_SIZE 8 +enum spi_nor_ops { + SPI_NOR_OPS_READ = 0, + SPI_NOR_OPS_WRITE, + SPI_NOR_OPS_ERASE, + SPI_NOR_OPS_LOCK, + SPI_NOR_OPS_UNLOCK, +}; + +/** + * struct spi_nor - Structure for defining a the SPI NOR layer + * @mtd: point to a mtd_info structure + * @lock: the lock for the read/write/erase/lock/unlock operations + * @dev: point to a spi device, or a spi nor controller device. + * @np: If exit, it points to a device_node which stands for the + * SPI NOR flash child node. + * @page_size: the page size of the SPI NOR + * @addr_width: number of address bytes + * @erase_opcode: the opcode for erasing a sector + * @read_opcode: the read opcode + * @read_dummy: the dummy needed by the read operation + * @program_opcode: the program opcode + * @flash_read: the mode of the read + * @sst_write_second: used by the SST write operation + * @cfg: used by the read_xfer/write_xfer + * @cmd_buf: used by the write_reg + * @prepare: [OPTIONAL] do some preparations for the + * read/write/erase/lock/unlock operations + * @unprepare: [OPTIONAL] do some post work after the + * read/write/erase/lock/unlock operations + * @read_xfer: [OPTIONAL] the read fundamental primitive + * @write_xfer: [OPTIONAL] the writefundamental primitive + * @read_reg: [DRIVER-SPECIFIC] read out the register + * @write_reg: [DRIVER-SPECIFIC] write data to the register + * @read_id: [REPLACEABLE] read out the ID data, and find + * the proper spi_device_id + * @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR + * at the offset @offs + * @priv: the private data + */ +struct spi_nor { + struct mtd_info *mtd; + struct mutex lock; + struct device *dev; + struct device_node *np; + u32 page_size; + u8 addr_width; + u8 erase_opcode; + u8 read_opcode; + u8 read_dummy; + u8 program_opcode; + enum read_mode flash_read; + bool sst_write_second; + struct spi_nor_xfer_cfg cfg; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; + + int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); + void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); + int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, + u8 *buf, size_t len); + int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg, + u8 *buf, size_t len); + int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); + int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len, + int write_enable); + const struct spi_device_id *(*read_id)(struct spi_nor *nor); + int (*wait_till_ready)(struct spi_nor *nor); + + int (*read)(struct spi_nor *nor, loff_t from, + size_t len, size_t *retlen, u_char *read_buf); + void (*write)(struct spi_nor *nor, loff_t to, + size_t len, size_t *retlen, const u_char *write_buf); + int (*erase)(struct spi_nor *nor, loff_t offs); + + void *priv; +}; + +/** + * spi_nor_scan() - scan the SPI NOR + * @nor: the spi_nor structure + * @id: the spi_device_id provided by the driver + * @mode: the read mode supported by the driver + * + * The drivers can use this fuction to scan the SPI NOR. + * In the scanning, it will try to get all the necessary information to + * fill the mtd_info{} and the spi_nor{}. + * + * The board may assigns a spi_device_id with @id which be used to compared with + * the spi_device_id detected by the scanning. + * + * Return: 0 for success, others for failure. + */ +int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, + enum read_mode mode); +extern const struct spi_device_id spi_nor_ids[]; + +/** + * spi_nor_match_id() - find the spi_device_id by the name + * @name: the name of the spi_device_id + * + * The drivers use this function to find the spi_device_id + * specified by the @name. + * + * Return: returns the right spi_device_id pointer on success, + * and returns NULL on failure. + */ +const struct spi_device_id *spi_nor_match_id(char *name); + +#endif diff --git a/include/linux/of.h b/include/linux/of.h index 54c2560..d27359a 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -67,7 +67,7 @@ struct device_node { #endif }; -#define MAX_PHANDLE_ARGS 8 +#define MAX_PHANDLE_ARGS 16 struct of_phandle_args { struct device_node *np; int args_count; diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index fcd63ba..9984a3e 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -47,7 +47,10 @@ static inline int of_irq_map_oldworld(struct device_node *device, int index, } #endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */ - +extern int of_irq_parse_raw(const __be32 *addr, + struct of_phandle_args *out_irq); +extern int of_irq_parse_one(struct device_node *device, int index, + struct of_phandle_args *out_irq); extern int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, u32 ointsize, const __be32 *addr, struct of_irq *out_irq); @@ -56,6 +59,7 @@ extern int of_irq_map_one(struct device_node *device, int index, extern unsigned int irq_create_of_mapping(struct device_node *controller, const u32 *intspec, unsigned int intsize); +extern unsigned int irq_create_of_mapping_new(struct of_phandle_args *irq_data); extern int of_irq_to_resource(struct device_node *dev, int index, struct resource *r); extern int of_irq_count(struct device_node *dev); diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index fd9c408..790db04 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -5,14 +5,50 @@ #include <linux/msi.h> struct pci_dev; +struct of_phandle_args; struct of_irq; int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq); struct device_node; + +#ifdef CONFIG_OF +int of_irq_parse_pci(const struct pci_dev *pdev, + struct of_phandle_args *out_irq); struct device_node *of_pci_find_child_device(struct device_node *parent, unsigned int devfn); int of_pci_get_devfn(struct device_node *np); +int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin); int of_pci_parse_bus_range(struct device_node *node, struct resource *res); +#else +static inline int of_irq_parse_pci(const struct pci_dev *pdev, + struct of_phandle_args *out_irq) +{ + return 0; +} + +static inline struct device_node * +of_pci_find_child_device(struct device_node *parent, unsigned int devfn) +{ + return NULL; +} + +static inline int of_pci_get_devfn(struct device_node *np) +{ + return -EINVAL; +} + +static inline int +of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin) +{ + return 0; +} + +static inline int +of_pci_parse_bus_range(struct device_node *node, struct resource *res) +{ + return -EINVAL; +} +#endif #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI) int of_pci_msi_chip_add(struct msi_chip *chip); diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 84b10f9..a07b670 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -418,6 +418,8 @@ struct snd_pcm_substream { #endif /* misc flags */ unsigned int hw_opened: 1; + /* data swapped flags */ + unsigned int data_swapped; }; #define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 706724e..18edabe 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -502,6 +502,43 @@ unsigned int irq_create_of_mapping(struct device_node *controller, } EXPORT_SYMBOL_GPL(irq_create_of_mapping); +unsigned int irq_create_of_mapping_new(struct of_phandle_args *irq_data) +{ + struct irq_domain *domain; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + unsigned int virq; + + domain = irq_data->np ? + irq_find_host(irq_data->np) : irq_default_domain; + if (!domain) { + pr_warn("no irq domain found for %s !\n", + of_node_full_name(irq_data->np)); + return 0; + } + + /* If domain has no translation, then we assume interrupt line */ + if (domain->ops->xlate == NULL) + hwirq = irq_data->args[0]; + else { + if (domain->ops->xlate(domain, irq_data->np, irq_data->args, + irq_data->args_count, &hwirq, &type)) + return 0; + } + + /* Create mapping */ + virq = irq_create_mapping(domain, hwirq); + if (!virq) + return virq; + + /* Set type if specified and different than the current one */ + if (type != IRQ_TYPE_NONE && + type != irq_get_trigger_type(virq)) + irq_set_irq_type(virq, type); + return virq; +} +EXPORT_SYMBOL_GPL(irq_create_of_mapping_new); + /** * irq_dispose_mapping() - Unmap an interrupt * @virq: linux irq number of the interrupt to unmap diff --git a/lib/Kconfig b/lib/Kconfig index 4490089..4ffcc86 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -365,6 +365,9 @@ config CPU_RMAP bool depends on SMP +config LIB_RHEAP + bool + config DQL bool diff --git a/lib/Makefile b/lib/Makefile index 6e23a0f..8523e9d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -194,3 +194,5 @@ quiet_cmd_build_OID_registry = GEN $@ clean-files += oid_registry_data.c obj-$(CONFIG_UCS2_STRING) += ucs2_string.o + +obj-$(CONFIG_LIB_RHEAP) += rheap.o diff --git a/arch/powerpc/lib/rheap.c b/lib/rheap.c index a1060a8..241cc0a 100644 --- a/arch/powerpc/lib/rheap.c +++ b/lib/rheap.c @@ -20,7 +20,7 @@ #include <linux/err.h> #include <linux/slab.h> -#include <asm/rheap.h> +#include <linux/fsl/rheap.h> /* * Fixup a list_head, needed when copying lists. If the pointers fall @@ -42,9 +42,9 @@ static inline void fixup(unsigned long s, unsigned long e, int d, } /* Grow the allocated blocks */ -static int grow(rh_info_t * info, int max_blocks) +static int grow(struct _rh_info *info, int max_blocks) { - rh_block_t *block, *blk; + struct _rh_block *block, *blk; int i, new_blocks; int delta; unsigned long blks, blke; @@ -54,7 +54,7 @@ static int grow(rh_info_t * info, int max_blocks) new_blocks = max_blocks - info->max_blocks; - block = kmalloc(sizeof(rh_block_t) * max_blocks, GFP_ATOMIC); + block = kmalloc(sizeof(struct _rh_block) * max_blocks, GFP_ATOMIC); if (block == NULL) return -ENOMEM; @@ -62,7 +62,7 @@ static int grow(rh_info_t * info, int max_blocks) /* copy old block area */ memcpy(block, info->block, - sizeof(rh_block_t) * info->max_blocks); + sizeof(struct _rh_block) * info->max_blocks); delta = (char *)block - (char *)info->block; @@ -100,7 +100,7 @@ static int grow(rh_info_t * info, int max_blocks) * causes a grow in the block area then all pointers kept to the block * area are invalid! */ -static int assure_empty(rh_info_t * info, int slots) +static int assure_empty(struct _rh_info *info, int slots) { int max_blocks; @@ -118,19 +118,19 @@ static int assure_empty(rh_info_t * info, int slots) return grow(info, max_blocks); } -static rh_block_t *get_slot(rh_info_t * info) +static struct _rh_block *get_slot(struct _rh_info *info) { - rh_block_t *blk; + struct _rh_block *blk; /* If no more free slots, and failure to extend. */ /* XXX: You should have called assure_empty before */ if (info->empty_slots == 0) { - printk(KERN_ERR "rh: out of slots; crash is imminent.\n"); + pr_err("rh: out of slots; crash is imminent.\n"); return NULL; } /* Get empty slot to use */ - blk = list_entry(info->empty_list.next, rh_block_t, list); + blk = list_entry(info->empty_list.next, struct _rh_block, list); list_del_init(&blk->list); info->empty_slots--; @@ -142,18 +142,18 @@ static rh_block_t *get_slot(rh_info_t * info) return blk; } -static inline void release_slot(rh_info_t * info, rh_block_t * blk) +static inline void release_slot(struct _rh_info *info, struct _rh_block *blk) { list_add(&blk->list, &info->empty_list); info->empty_slots++; } -static void attach_free_block(rh_info_t * info, rh_block_t * blkn) +static void attach_free_block(struct _rh_info *info, struct _rh_block *blkn) { - rh_block_t *blk; - rh_block_t *before; - rh_block_t *after; - rh_block_t *next; + struct _rh_block *blk; + struct _rh_block *before; + struct _rh_block *after; + struct _rh_block *next; int size; unsigned long s, e, bs, be; struct list_head *l; @@ -170,7 +170,7 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn) next = NULL; list_for_each(l, &info->free_list) { - blk = list_entry(l, rh_block_t, list); + blk = list_entry(l, struct _rh_block, list); bs = blk->start; be = bs + blk->size; @@ -229,14 +229,14 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn) release_slot(info, after); } -static void attach_taken_block(rh_info_t * info, rh_block_t * blkn) +static void attach_taken_block(struct _rh_info *info, struct _rh_block *blkn) { - rh_block_t *blk; + struct _rh_block *blk; struct list_head *l; /* Find the block immediately before the given one (if any) */ list_for_each(l, &info->taken_list) { - blk = list_entry(l, rh_block_t, list); + blk = list_entry(l, struct _rh_block, list); if (blk->start > blkn->start) { list_add_tail(&blkn->list, &blk->list); return; @@ -250,9 +250,9 @@ static void attach_taken_block(rh_info_t * info, rh_block_t * blkn) * Create a remote heap dynamically. Note that no memory for the blocks * are allocated. It will upon the first allocation */ -rh_info_t *rh_create(unsigned int alignment) +struct _rh_info *rh_create(unsigned int alignment) { - rh_info_t *info; + struct _rh_info *info; /* Alignment must be a power of two */ if ((alignment & (alignment - 1)) != 0) @@ -282,7 +282,7 @@ EXPORT_SYMBOL_GPL(rh_create); * Destroy a dynamically created remote heap. Deallocate only if the areas * are not static */ -void rh_destroy(rh_info_t * info) +void rh_destroy(struct _rh_info *info) { if ((info->flags & RHIF_STATIC_BLOCK) == 0 && info->block != NULL) kfree(info->block); @@ -297,11 +297,11 @@ EXPORT_SYMBOL_GPL(rh_destroy); * operation very early in the startup of the kernel, when it is not yet safe * to call kmalloc. */ -void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks, - rh_block_t * block) +void rh_init(struct _rh_info *info, unsigned int alignment, int max_blocks, + struct _rh_block *block) { int i; - rh_block_t *blk; + struct _rh_block *blk; /* Alignment must be a power of two */ if ((alignment & (alignment - 1)) != 0) @@ -326,9 +326,9 @@ void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks, EXPORT_SYMBOL_GPL(rh_init); /* Attach a free memory region, coalesces regions if adjuscent */ -int rh_attach_region(rh_info_t * info, unsigned long start, int size) +int rh_attach_region(struct _rh_info *info, unsigned long start, int size) { - rh_block_t *blk; + struct _rh_block *blk; unsigned long s, e, m; int r; @@ -367,10 +367,11 @@ int rh_attach_region(rh_info_t * info, unsigned long start, int size) EXPORT_SYMBOL_GPL(rh_attach_region); /* Detatch given address range, splits free block if needed. */ -unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size) +unsigned long rh_detach_region(struct _rh_info *info, unsigned long start, + int size) { struct list_head *l; - rh_block_t *blk, *newblk; + struct _rh_block *blk, *newblk; unsigned long s, e, m, bs, be; /* Validate size */ @@ -393,7 +394,7 @@ unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size) blk = NULL; list_for_each(l, &info->free_list) { - blk = list_entry(l, rh_block_t, list); + blk = list_entry(l, struct _rh_block, list); /* The range must lie entirely inside one free block */ bs = blk->start; be = blk->start + blk->size; @@ -439,11 +440,12 @@ EXPORT_SYMBOL_GPL(rh_detach_region); * is an offset into the buffer initialized by rh_init(), or a negative number * if there is an error. */ -unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const char *owner) +unsigned long rh_alloc_align(struct _rh_info *info, int size, + int alignment, const char *owner) { struct list_head *l; - rh_block_t *blk; - rh_block_t *newblk; + struct _rh_block *blk; + struct _rh_block *newblk; unsigned long start, sp_size; /* Validate size, and alignment must be power of two */ @@ -458,7 +460,7 @@ unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const ch blk = NULL; list_for_each(l, &info->free_list) { - blk = list_entry(l, rh_block_t, list); + blk = list_entry(l, struct _rh_block, list); if (size <= blk->size) { start = (blk->start + alignment - 1) & ~(alignment - 1); if (start + size <= blk->start + blk->size) @@ -480,7 +482,7 @@ unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const ch /* Create block for fragment in the beginning */ sp_size = start - blk->start; if (sp_size) { - rh_block_t *spblk; + struct _rh_block *spblk; spblk = get_slot(info); spblk->start = blk->start; @@ -514,7 +516,7 @@ EXPORT_SYMBOL_GPL(rh_alloc_align); * an offset into the buffer initialized by rh_init(), or a negative number if * there is an error. */ -unsigned long rh_alloc(rh_info_t * info, int size, const char *owner) +unsigned long rh_alloc(struct _rh_info *info, int size, const char *owner) { return rh_alloc_align(info, size, info->alignment, owner); } @@ -524,10 +526,11 @@ EXPORT_SYMBOL_GPL(rh_alloc); * alignment. The value returned is an offset into the buffer initialized by * rh_init(), or a negative number if there is an error. */ -unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, const char *owner) +unsigned long rh_alloc_fixed(struct _rh_info *info, unsigned long start, + int size, const char *owner) { struct list_head *l; - rh_block_t *blk, *newblk1, *newblk2; + struct _rh_block *blk, *newblk1, *newblk2; unsigned long s, e, m, bs = 0, be = 0; /* Validate size */ @@ -550,7 +553,7 @@ unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, co blk = NULL; list_for_each(l, &info->free_list) { - blk = list_entry(l, rh_block_t, list); + blk = list_entry(l, struct _rh_block, list); /* The range must lie entirely inside one free block */ bs = blk->start; be = blk->start + blk->size; @@ -609,16 +612,16 @@ EXPORT_SYMBOL_GPL(rh_alloc_fixed); * The return value is the size of the deallocated block, or a negative number * if there is an error. */ -int rh_free(rh_info_t * info, unsigned long start) +int rh_free(struct _rh_info *info, unsigned long start) { - rh_block_t *blk, *blk2; + struct _rh_block *blk, *blk2; struct list_head *l; int size; /* Linear search for block */ blk = NULL; list_for_each(l, &info->taken_list) { - blk2 = list_entry(l, rh_block_t, list); + blk2 = list_entry(l, struct _rh_block, list); if (start < blk2->start) break; blk = blk2; @@ -638,9 +641,10 @@ int rh_free(rh_info_t * info, unsigned long start) } EXPORT_SYMBOL_GPL(rh_free); -int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats) +int rh_get_stats(struct _rh_info *info, int what, int max_stats, + struct _rh_stats *stats) { - rh_block_t *blk; + struct _rh_block *blk; struct list_head *l; struct list_head *h; int nr; @@ -662,7 +666,7 @@ int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats) /* Linear search for block */ nr = 0; list_for_each(l, h) { - blk = list_entry(l, rh_block_t, list); + blk = list_entry(l, struct _rh_block, list); if (stats != NULL && nr < max_stats) { stats->start = blk->start; stats->size = blk->size; @@ -676,16 +680,16 @@ int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats) } EXPORT_SYMBOL_GPL(rh_get_stats); -int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner) +int rh_set_owner(struct _rh_info *info, unsigned long start, const char *owner) { - rh_block_t *blk, *blk2; + struct _rh_block *blk, *blk2; struct list_head *l; int size; /* Linear search for block */ blk = NULL; list_for_each(l, &info->taken_list) { - blk2 = list_entry(l, rh_block_t, list); + blk2 = list_entry(l, struct _rh_block, list); if (start < blk2->start) break; blk = blk2; @@ -701,46 +705,42 @@ int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner) } EXPORT_SYMBOL_GPL(rh_set_owner); -void rh_dump(rh_info_t * info) +void rh_dump(struct _rh_info *info) { - static rh_stats_t st[32]; /* XXX maximum 32 blocks */ + static struct _rh_stats st[32]; /* XXX maximum 32 blocks */ int maxnr; int i, nr; maxnr = ARRAY_SIZE(st); - printk(KERN_INFO - "info @0x%p (%d slots empty / %d max)\n", + pr_info("info @0x%p (%d slots empty / %d max)\n", info, info->empty_slots, info->max_blocks); - printk(KERN_INFO " Free:\n"); + pr_info(" Free:\n"); nr = rh_get_stats(info, RHGS_FREE, maxnr, st); if (nr > maxnr) nr = maxnr; for (i = 0; i < nr; i++) - printk(KERN_INFO - " 0x%lx-0x%lx (%u)\n", + pr_info(" 0x%lx-0x%lx (%u)\n", st[i].start, st[i].start + st[i].size, st[i].size); - printk(KERN_INFO "\n"); + pr_info("\n"); - printk(KERN_INFO " Taken:\n"); + pr_info(" Taken:\n"); nr = rh_get_stats(info, RHGS_TAKEN, maxnr, st); if (nr > maxnr) nr = maxnr; for (i = 0; i < nr; i++) - printk(KERN_INFO - " 0x%lx-0x%lx (%u) %s\n", + pr_info(" 0x%lx-0x%lx (%u) %s\n", st[i].start, st[i].start + st[i].size, st[i].size, st[i].owner != NULL ? st[i].owner : ""); - printk(KERN_INFO "\n"); + pr_info("\n"); } EXPORT_SYMBOL_GPL(rh_dump); -void rh_dump_blk(rh_info_t * info, rh_block_t * blk) +void rh_dump_blk(struct _rh_info *info, struct _rh_block *blk) { - printk(KERN_INFO - "blk @0x%p: 0x%lx-0x%lx (%u)\n", + pr_info("blk @0x%p: 0x%lx-0x%lx (%u)\n", blk, blk->start, blk->start + blk->size, blk->size); } EXPORT_SYMBOL_GPL(rh_dump_blk); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a210467..f1fe6e6 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1962,6 +1962,9 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; int err; char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); + int i; + char tmp; + if (substream->ops->copy) { if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) return err; @@ -1969,6 +1972,22 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) return -EFAULT; + + if (substream->data_swapped) { + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + for (i = 0; + i < frames_to_bytes(runtime, frames); + i = i + 2) { + tmp = *(hwbuf + i); + *(hwbuf + i) = *(hwbuf + i + 1); + *(hwbuf + i + 1) = tmp; + } + break; + default: + return -EINVAL; + } + } } return 0; } @@ -2184,11 +2203,30 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; int err; char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); + int i; + char tmp; + if (substream->ops->copy) { if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) return err; } else { char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); + if (substream->data_swapped) { + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + for (i = 0; + i < frames_to_bytes(runtime, frames); + i = i + 2) { + tmp = *(hwbuf + i); + *(hwbuf + i) = *(hwbuf + i + 1); + *(hwbuf + i + 1) = tmp; + } + break; + default: + return -EINVAL; + } + } + if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) return -EFAULT; } diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index b7ab71f..5acb70b 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,3 +1,8 @@ +config SND_SOC_FSL_SAI + tristate + select REGMAP_MMIO + select SND_SOC_GENERIC_DMAENGINE_PCM + config SND_SOC_FSL_SSI tristate @@ -213,3 +218,26 @@ config SND_SOC_IMX_MC13783 select SND_SOC_IMX_PCM_DMA endif # SND_IMX_SOC + +menuconfig SND_VF610_SOC + tristate "SoC Audio for Freescale VF610 CPUs" + select DMA_ENGINE + help + Say Y or M if you want to add support for codecs attached to + the VF610 CPUs. + + This will enable Freeacale SAI and SGTL5000 codec, and an extra + TWR-AUDIO-SGTL sub-board is needed for SGTL5000. + +if SND_VF610_SOC + +config SND_SOC_VF610_SGTL5000 + tristate "SoC Audio support for VF610 boards with SGTL5000" + depends on OF && I2C + select SND_SOC_FSL_SAI + select SND_SOC_SGTL5000 + help + Say Y if you want to add support for SoC audio on an VF610 board with + a SGTL5000 codec and a SAI. + +endif # SND_VF610_SOC diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 8db705b..96110d1 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -10,16 +10,22 @@ obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o snd-soc-p1022-rdk-objs := p1022_rdk.o obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o -# Freescale PowerPC SSI/DMA Platform Support +# Freescale SSI/DMA/SAI/SPDIF Support +snd-soc-fsl-sai-objs := fsl_sai.o snd-soc-fsl-ssi-objs := fsl_ssi.o snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o +obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o +# VF610 Platform Support +snd-soc-vf610-sgtl5000-objs := vf610-sgtl5000.o +obj-$(CONFIG_SND_SOC_VF610_SGTL5000) += snd-soc-vf610-sgtl5000.o + # MPC5200 Platform Support obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c new file mode 100644 index 0000000..ff86daf --- /dev/null +++ b/sound/soc/fsl/fsl_sai.c @@ -0,0 +1,609 @@ +/* + * Freescale ALSA SoC Digital Audio Interface (SAI) driver. + * + * 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 <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#include "fsl_sai.h" + +static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int fsl_dir) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr2, reg_cr2; + + if (fsl_dir == FSL_FMT_TRANSMITTER) + reg_cr2 = FSL_SAI_TCR2; + else + reg_cr2 = FSL_SAI_RCR2; + + regmap_read(sai->regmap, reg_cr2, &val_cr2); + + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + + switch (clk_id) { + case FSL_SAI_CLK_BUS: + val_cr2 |= FSL_SAI_CR2_MSEL_BUS; + break; + case FSL_SAI_CLK_MAST1: + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1; + break; + case FSL_SAI_CLK_MAST2: + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2; + break; + case FSL_SAI_CLK_MAST3: + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3; + break; + default: + return -EINVAL; + } + + regmap_write(sai->regmap, reg_cr2, val_cr2); + + return 0; +} + +static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + int ret; + + if (dir == SND_SOC_CLOCK_IN) + return 0; + + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, + FSL_FMT_TRANSMITTER); + if (ret) { + dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); + return ret; + } + + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, + FSL_FMT_RECEIVER); + if (ret) + dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); + + return ret; +} + +static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, + unsigned int fmt, int fsl_dir) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr2, val_cr4, reg_cr2, reg_cr4; + + if (fsl_dir == FSL_FMT_TRANSMITTER) { + reg_cr2 = FSL_SAI_TCR2; + reg_cr4 = FSL_SAI_TCR4; + } else { + reg_cr2 = FSL_SAI_RCR2; + reg_cr4 = FSL_SAI_RCR4; + } + + regmap_read(sai->regmap, reg_cr2, &val_cr2); + regmap_read(sai->regmap, reg_cr4, &val_cr4); + + if (sai->big_endian_data) + val_cr4 &= ~FSL_SAI_CR4_MF; + else + val_cr4 |= FSL_SAI_CR4_MF; + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* + * Frame low, 1clk before data, one word length for frame sync, + * frame sync starts one serial clock cycle earlier, + * that is, together with the last bit of the previous + * data word. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* + * Frame high, one word length for frame sync, + * frame sync asserts with the first bit of the frame. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); + break; + case SND_SOC_DAIFMT_DSP_A: + /* + * Frame high, 1clk before data, one bit for frame sync, + * frame sync starts one serial clock cycle earlier, + * that is, together with the last bit of the previous + * data word. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr4 |= FSL_SAI_CR4_FSE; + sai->is_dsp_mode = true; + break; + case SND_SOC_DAIFMT_DSP_B: + /* + * Frame high, one bit for frame sync, + * frame sync asserts with the first bit of the frame. + */ + val_cr2 &= ~FSL_SAI_CR2_BCP; + val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP); + sai->is_dsp_mode = true; + break; + case SND_SOC_DAIFMT_RIGHT_J: + /* To be done */ + default: + return -EINVAL; + } + + /* DAI clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + /* Invert both clocks */ + val_cr2 ^= FSL_SAI_CR2_BCP; + val_cr4 ^= FSL_SAI_CR4_FSP; + break; + case SND_SOC_DAIFMT_IB_NF: + /* Invert bit clock */ + val_cr2 ^= FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_NB_IF: + /* Invert frame clock */ + val_cr4 ^= FSL_SAI_CR4_FSP; + break; + case SND_SOC_DAIFMT_NB_NF: + /* Nothing to do for both normal cases */ + break; + default: + return -EINVAL; + } + + /* DAI clock master masks */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; + val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBS_CFM: + val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; + val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + break; + default: + return -EINVAL; + } + + regmap_write(sai->regmap, reg_cr2, val_cr2); + regmap_write(sai->regmap, reg_cr4, val_cr4); + + return 0; +} + +static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + int ret; + + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); + if (ret) { + dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); + return ret; + } + + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); + if (ret) + dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); + + return ret; +} + +static int fsl_sai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr; + unsigned int channels = params_channels(params); + u32 word_width = snd_pcm_format_width(params_format(params)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + reg_cr4 = FSL_SAI_TCR4; + reg_cr5 = FSL_SAI_TCR5; + reg_mr = FSL_SAI_TMR; + } else { + reg_cr4 = FSL_SAI_RCR4; + reg_cr5 = FSL_SAI_RCR5; + reg_mr = FSL_SAI_RMR; + } + + regmap_read(sai->regmap, reg_cr4, &val_cr4); + regmap_read(sai->regmap, reg_cr4, &val_cr5); + + val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK; + val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK; + + val_cr5 &= ~FSL_SAI_CR5_WNW_MASK; + val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + + if (!sai->is_dsp_mode) + val_cr4 |= FSL_SAI_CR4_SYWD(word_width); + + val_cr5 |= FSL_SAI_CR5_WNW(word_width); + val_cr5 |= FSL_SAI_CR5_W0W(word_width); + + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + if (sai->big_endian_data) + val_cr5 |= FSL_SAI_CR5_FBT(0); + else + val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); + + val_cr4 |= FSL_SAI_CR4_FRSZ(channels); + val_mr = ~0UL - ((1 << channels) - 1); + + regmap_write(sai->regmap, reg_cr4, val_cr4); + regmap_write(sai->regmap, reg_cr5, val_cr5); + regmap_write(sai->regmap, reg_mr, val_mr); + + return 0; +} + +static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 tcsr, rcsr; + + /* + * The transmitter bit clock and frame sync are to be + * used by both the transmitter and receiver. + */ + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, + ~FSL_SAI_CR2_SYNC); + regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC, + FSL_SAI_CR2_SYNC); + + regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr); + regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tcsr |= FSL_SAI_CSR_FRDE; + rcsr &= ~FSL_SAI_CSR_FRDE; + } else { + rcsr |= FSL_SAI_CSR_FRDE; + tcsr &= ~FSL_SAI_CSR_FRDE; + } + + /* + * It is recommended that the transmitter is the last enabled + * and the first disabled. + */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + tcsr |= FSL_SAI_CSR_TERE; + rcsr |= FSL_SAI_CSR_TERE; + + regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); + regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (!(cpu_dai->playback_active || cpu_dai->capture_active)) { + tcsr &= ~FSL_SAI_CSR_TERE; + rcsr &= ~FSL_SAI_CSR_TERE; + } + + regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr); + regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsl_sai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + + if (sai->big_endian_regs) + substream->data_swapped = 1; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = FSL_SAI_TCR3; + else + reg = FSL_SAI_RCR3; + + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, + FSL_SAI_CR3_TRCE); + + return 0; +} + +static void fsl_sai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = FSL_SAI_TCR3; + else + reg = FSL_SAI_RCR3; + + regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE, + ~FSL_SAI_CR3_TRCE); +} + +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt, + .hw_params = fsl_sai_hw_params, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, + .shutdown = fsl_sai_shutdown, +}; + +static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); + + regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0); + regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0); + regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK, + FSL_SAI_MAXBURST_TX * 2); + regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK, + FSL_SAI_MAXBURST_RX - 1); + + cpu_dai->playback_dma_data = &sai->dma_params_tx; + cpu_dai->capture_dma_data = &sai->dma_params_rx; + + snd_soc_dai_set_drvdata(cpu_dai, sai); + + return 0; +} + +static struct snd_soc_dai_driver fsl_sai_dai = { + .probe = fsl_sai_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = FSL_SAI_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_component = { + .name = "fsl-sai", +}; + +static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_TCR1: + case FSL_SAI_TCR2: + case FSL_SAI_TCR3: + case FSL_SAI_TCR4: + case FSL_SAI_TCR5: + case FSL_SAI_TFR: + case FSL_SAI_TMR: + case FSL_SAI_RCSR: + case FSL_SAI_RCR1: + case FSL_SAI_RCR2: + case FSL_SAI_RCR3: + case FSL_SAI_RCR4: + case FSL_SAI_RCR5: + case FSL_SAI_RDR: + case FSL_SAI_RFR: + case FSL_SAI_RMR: + return true; + default: + return false; + } +} + +static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TFR: + case FSL_SAI_RFR: + case FSL_SAI_TDR: + case FSL_SAI_RDR: + return true; + default: + return false; + } + +} + +static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_TCSR: + case FSL_SAI_TCR1: + case FSL_SAI_TCR2: + case FSL_SAI_TCR3: + case FSL_SAI_TCR4: + case FSL_SAI_TCR5: + case FSL_SAI_TDR: + case FSL_SAI_TMR: + case FSL_SAI_RCSR: + case FSL_SAI_RCR1: + case FSL_SAI_RCR2: + case FSL_SAI_RCR3: + case FSL_SAI_RCR4: + case FSL_SAI_RCR5: + case FSL_SAI_RMR: + return true; + default: + return false; + } +} + +static struct regmap_config fsl_sai_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = FSL_SAI_RMR, + .readable_reg = fsl_sai_readable_reg, + .volatile_reg = fsl_sai_volatile_reg, + .writeable_reg = fsl_sai_writeable_reg, +}; + +static bool fsl_sai_filter(struct dma_chan *chan, void *param) +{ + struct snd_dmaengine_dai_dma_data *dma_data = param; + + chan->private = dma_data->filter_data; + + return true; +} + +static const struct snd_pcm_hardware fsl_sai_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = FSL_SAI_DMABUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = 65535, + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, +}; + +static const struct snd_dmaengine_pcm_config fsl_sai_dmaengine_pcm_config = { + .pcm_hardware = &fsl_sai_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = fsl_sai_filter, + .prealloc_buffer_size = FSL_SAI_DMABUF_SIZE, +}; + +static int fsl_sai_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_sai *sai; + struct resource *res; + void __iomem *base; + int ret; + + sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); + if (!sai) + return -ENOMEM; + + sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); + if (sai->big_endian_regs) + fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; + + sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "sai", base, &fsl_sai_regmap_config); + if (IS_ERR(sai->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(sai->regmap); + } + + if (sai->big_endian_regs) { + sai->dma_params_rx.addr = res->start + FSL_SAI_RDR + 2; + sai->dma_params_tx.addr = res->start + FSL_SAI_TDR + 2; + } else { + sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; + sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; + } + + sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; + sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; + + platform_set_drvdata(pdev, sai); + + ret = snd_soc_register_component(&pdev->dev, &fsl_component, + &fsl_sai_dai, 1); + if (ret) + return ret; + + ret = snd_dmaengine_pcm_register(&pdev->dev, + &fsl_sai_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + if (ret) + snd_soc_unregister_component(&pdev->dev); + + return ret; +} + +static int fsl_sai_remove(struct platform_device *pdev) +{ + snd_dmaengine_pcm_unregister(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + + return 0; +} + +static const struct of_device_id fsl_sai_ids[] = { + { .compatible = "fsl,vf610-sai", }, + { /* sentinel */ } +}; + +static struct platform_driver fsl_sai_driver = { + .probe = fsl_sai_probe, + .remove = fsl_sai_remove, + .driver = { + .name = "fsl-sai", + .owner = THIS_MODULE, + .of_match_table = fsl_sai_ids, + }, +}; +module_platform_driver(fsl_sai_driver); + +MODULE_DESCRIPTION("Freescale Soc SAI Interface"); +MODULE_AUTHOR("Xiubo Li, <Li.Xiubo@freescale.com>"); +MODULE_ALIAS("platform:fsl-sai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h new file mode 100644 index 0000000..fc12d96 --- /dev/null +++ b/sound/soc/fsl/fsl_sai.h @@ -0,0 +1,114 @@ +/* + * 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 version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __FSL_SAI_H +#define __FSL_SAI_H + +#include <sound/dmaengine_pcm.h> + +#define FSL_SAI_DMABUF_SIZE (64 * 1024) + +#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +/* SAI Register Map Register */ +#define FSL_SAI_TCSR 0x00 /* SAI Transmit Control */ +#define FSL_SAI_TCR1 0x04 /* SAI Transmit Configuration 1 */ +#define FSL_SAI_TCR2 0x08 /* SAI Transmit Configuration 2 */ +#define FSL_SAI_TCR3 0x0c /* SAI Transmit Configuration 3 */ +#define FSL_SAI_TCR4 0x10 /* SAI Transmit Configuration 4 */ +#define FSL_SAI_TCR5 0x14 /* SAI Transmit Configuration 5 */ +#define FSL_SAI_TDR 0x20 /* SAI Transmit Data */ +#define FSL_SAI_TFR 0x40 /* SAI Transmit FIFO */ +#define FSL_SAI_TMR 0x60 /* SAI Transmit Mask */ +#define FSL_SAI_RCSR 0x80 /* SAI Receive Control */ +#define FSL_SAI_RCR1 0x84 /* SAI Receive Configuration 1 */ +#define FSL_SAI_RCR2 0x88 /* SAI Receive Configuration 2 */ +#define FSL_SAI_RCR3 0x8c /* SAI Receive Configuration 3 */ +#define FSL_SAI_RCR4 0x90 /* SAI Receive Configuration 4 */ +#define FSL_SAI_RCR5 0x94 /* SAI Receive Configuration 5 */ +#define FSL_SAI_RDR 0xa0 /* SAI Receive Data */ +#define FSL_SAI_RFR 0xc0 /* SAI Receive FIFO */ +#define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */ + +/* SAI Transmit/Recieve Control Register */ +#define FSL_SAI_CSR_TERE BIT(31) +#define FSL_SAI_CSR_FWF BIT(17) +#define FSL_SAI_CSR_FRIE BIT(8) +#define FSL_SAI_CSR_FRDE BIT(0) + +/* SAI Transmit and Recieve Configuration 1 Register */ +#define FSL_SAI_CR1_RFW_MASK 0x1f + +/* SAI Transmit and Recieve Configuration 2 Register */ +#define FSL_SAI_CR2_SYNC BIT(30) +#define FSL_SAI_CR2_MSEL_MASK (0xff << 26) +#define FSL_SAI_CR2_MSEL_BUS 0 +#define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) +#define FSL_SAI_CR2_MSEL_MCLK2 BIT(27) +#define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27)) +#define FSL_SAI_CR2_BCP BIT(25) +#define FSL_SAI_CR2_BCD_MSTR BIT(24) + +/* SAI Transmit and Recieve Configuration 3 Register */ +#define FSL_SAI_CR3_TRCE BIT(16) +#define FSL_SAI_CR3_WDFL(x) (x) +#define FSL_SAI_CR3_WDFL_MASK 0x1f + +/* SAI Transmit and Recieve Configuration 4 Register */ +#define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16) +#define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16) +#define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8) +#define FSL_SAI_CR4_SYWD_MASK (0x1f << 8) +#define FSL_SAI_CR4_MF BIT(4) +#define FSL_SAI_CR4_FSE BIT(3) +#define FSL_SAI_CR4_FSP BIT(1) +#define FSL_SAI_CR4_FSD_MSTR BIT(0) + +/* SAI Transmit and Recieve Configuration 5 Register */ +#define FSL_SAI_CR5_WNW(x) (((x) - 1) << 24) +#define FSL_SAI_CR5_WNW_MASK (0x1f << 24) +#define FSL_SAI_CR5_W0W(x) (((x) - 1) << 16) +#define FSL_SAI_CR5_W0W_MASK (0x1f << 16) +#define FSL_SAI_CR5_FBT(x) ((x) << 8) +#define FSL_SAI_CR5_FBT_MASK (0x1f << 8) + +/* SAI type */ +#define FSL_SAI_DMA BIT(0) +#define FSL_SAI_USE_AC97 BIT(1) +#define FSL_SAI_NET BIT(2) +#define FSL_SAI_TRA_SYN BIT(3) +#define FSL_SAI_REC_SYN BIT(4) +#define FSL_SAI_USE_I2S_SLAVE BIT(5) + +#define FSL_FMT_TRANSMITTER 0 +#define FSL_FMT_RECEIVER 1 + +/* SAI clock sources */ +#define FSL_SAI_CLK_BUS 0 +#define FSL_SAI_CLK_MAST1 1 +#define FSL_SAI_CLK_MAST2 2 +#define FSL_SAI_CLK_MAST3 3 + +/* SAI data transfer numbers per DMA request */ +#define FSL_SAI_MAXBURST_TX 6 +#define FSL_SAI_MAXBURST_RX 6 + +struct fsl_sai { + struct regmap *regmap; + + bool big_endian_regs; + bool big_endian_data; + bool is_dsp_mode; + + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; +}; + +#endif /* __FSL_SAI_H */ diff --git a/sound/soc/fsl/vf610-sgtl5000.c b/sound/soc/fsl/vf610-sgtl5000.c new file mode 100644 index 0000000..d53d554 --- /dev/null +++ b/sound/soc/fsl/vf610-sgtl5000.c @@ -0,0 +1,169 @@ +/* + * Freescale ALSA SoC Audio using SGTL5000 as codec. + * + * Copyright 2012-2014 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: + * + */ + +#include <linux/clk.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> + +#include "../codecs/sgtl5000.h" +#include "fsl_sai.h" + +static unsigned int sysclk_rate; + +static int vf610_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct device *dev = rtd->card->dev; + + ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, + sysclk_rate, SND_SOC_CLOCK_IN); + if (ret) { + dev_err(dev, "could not set codec driver clock params :%d\n", + ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, FSL_SAI_CLK_BUS, + sysclk_rate, SND_SOC_CLOCK_OUT); + if (ret) { + dev_err(dev, "could not set cpu dai driver clock params :%d\n", + ret); + return ret; + } + + return 0; +} + +static struct snd_soc_dai_link vf610_sgtl5000_dai = { + .name = "HiFi", + .stream_name = "HiFi", + .codec_dai_name = "sgtl5000", + .init = &vf610_sgtl5000_dai_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, +}; + +static const struct snd_soc_dapm_widget vf610_sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_MIC("Microphone Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker Ext", NULL), +}; + +static struct snd_soc_card vf610_sgtl5000_card = { + .owner = THIS_MODULE, + .num_links = 1, + .dai_link = &vf610_sgtl5000_dai, + .dapm_widgets = vf610_sgtl5000_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(vf610_sgtl5000_dapm_widgets), +}; + +static int vf610_sgtl5000_parse_dt(struct platform_device *pdev) +{ + int ret; + struct device_node *sai_np, *codec_np; + struct clk *codec_clk; + struct i2c_client *codec_dev; + struct device_node *np = pdev->dev.of_node; + + ret = snd_soc_of_parse_card_name(&vf610_sgtl5000_card, + "simple-audio-card,name"); + if (ret) + return ret; + + ret = snd_soc_of_parse_audio_routing(&vf610_sgtl5000_card, + "simple-audio-card,routing"); + if (ret) + return ret; + + sai_np = of_parse_phandle(np, "simple-audio-card,cpu", 0); + if (!sai_np) { + dev_err(&pdev->dev, "parsing \"saif-controller\" error\n"); + return -EINVAL; + } + vf610_sgtl5000_dai.cpu_of_node = sai_np; + vf610_sgtl5000_dai.platform_of_node = sai_np; + + codec_np = of_parse_phandle(np, "simple-audio-card,codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "parsing \"audio-codec\" error\n"); + ret = -EINVAL; + goto sai_np_fail; + } + vf610_sgtl5000_dai.codec_of_node = codec_np; + + codec_dev = of_find_i2c_device_by_node(codec_np); + if (!codec_dev) { + dev_err(&pdev->dev, "failed to find codec platform device\n"); + ret = PTR_ERR(codec_dev); + goto codec_np_fail; + } + + codec_clk = devm_clk_get(&codec_dev->dev, NULL); + if (IS_ERR(codec_clk)) { + dev_err(&pdev->dev, "failed to get codec clock\n"); + ret = PTR_ERR(codec_clk); + goto codec_np_fail; + } + + sysclk_rate = clk_get_rate(codec_clk); + +codec_np_fail: + of_node_put(codec_np); +sai_np_fail: + of_node_put(sai_np); + + return ret; +} + +static int vf610_sgtl5000_probe(struct platform_device *pdev) +{ + int ret; + + vf610_sgtl5000_card.dev = &pdev->dev; + + ret = vf610_sgtl5000_parse_dt(pdev); + if (ret) { + dev_err(&pdev->dev, "parse sgtl5000 device tree failed :%d\n", + ret); + return ret; + } + + ret = snd_soc_register_card(&vf610_sgtl5000_card); + if (ret) { + dev_err(&pdev->dev, "TWR-AUDIO-SGTL board required :%d\n", + ret); + return ret; + } + + return 0; +} + +static const struct of_device_id vf610_sgtl5000_dt_ids[] = { + { .compatible = "fsl,vf610-sgtl5000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vf610_sgtl5000_dt_ids); + +static struct platform_driver vf610_sgtl5000_driver = { + .probe = vf610_sgtl5000_probe, + .driver = { + .name = "vf610-sgtl5000", + .owner = THIS_MODULE, + .of_match_table = vf610_sgtl5000_dt_ids, + }, +}; +module_platform_driver(vf610_sgtl5000_driver); + +MODULE_DESCRIPTION("Freescale SGTL5000 ASoC driver"); +MODULE_LICENSE("GPL"); |