Commit 8b45bc89 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC driver updates from Arnd Bergmann:
 "These are changes for drivers that are intimately tied to some SoC and
  for some reason could not get merged through the respective subsystem
  maintainer tree.

  Most of the new code is for the Keystone Navigator driver, which is
  new base support that is going to be needed for their hardware
  accelerated network driver and other units.

  Most of the commits are for moving old code around from at91 and omap
  for things that are done in device drivers nowadays.

   - at91: move reset, poweroff, memory and clocksource code into
     drivers directories
   - socfpga: add edac driver (through arm-soc, as requested by Boris)
   - omap: move omap-intc code to drivers/irqchip
   - sunxi: added an RTC driver for sun6i
   - omap: mailbox driver related changes
   - keystone: support for the "Navigator" component
   - versatile: new reboot, led and soc drivers"

* tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (92 commits)
  bus: arm-ccn: Fix spurious warning message
  leds: add device tree bindings for register bit LEDs
  soc: add driver for the ARM RealView
  power: reset: driver for the Versatile syscon reboot
  leds: add a driver for syscon-based LEDs
  drivers/soc: ti: fix build break with modules
  MAINTAINERS: Add Keystone Multicore Navigator drivers entry
  soc: ti: add Keystone Navigator DMA support
  Documentation: dt: soc: add Keystone Navigator DMA bindings
  soc: ti: add Keystone Navigator QMSS driver
  Documentation: dt: soc: add Keystone Navigator QMSS bindings
  rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc
  rtc: sun6i: Add sun6i RTC driver
  irqchip: omap-intc: remove unnecessary comments
  irqchip: omap-intc: correct maximum number or MIR registers
  irqchip: omap-intc: enable TURBO idle mode
  irqchip: omap-intc: enable IP protection
  irqchip: omap-intc: remove unnecesary of_address_to_resource() call
  irqchip: omap-intc: comment style cleanup
  irqchip: omap-intc: minor improvement to omap_irq_pending()
  ...
parents eb785bef fa637bf0
...@@ -98,8 +98,8 @@ RAMC SDRAM/DDR Controller required properties: ...@@ -98,8 +98,8 @@ RAMC SDRAM/DDR Controller required properties:
- compatible: Should be "atmel,at91rm9200-sdramc", - compatible: Should be "atmel,at91rm9200-sdramc",
"atmel,at91sam9260-sdramc", "atmel,at91sam9260-sdramc",
"atmel,at91sam9g45-ddramc", "atmel,at91sam9g45-ddramc",
"atmel,sama5d3-ddramc",
- reg: Should contain registers location and length - reg: Should contain registers location and length
For at91sam9263 and at91sam9g45 you must specify 2 entries.
Examples: Examples:
...@@ -108,12 +108,6 @@ Examples: ...@@ -108,12 +108,6 @@ Examples:
reg = <0xffffe800 0x200>; reg = <0xffffe800 0x200>;
}; };
ramc0: ramc@ffffe400 {
compatible = "atmel,at91sam9g45-ddramc";
reg = <0xffffe400 0x200
0xffffe600 0x200>;
};
SHDWC Shutdown Controller SHDWC Shutdown Controller
required properties: required properties:
......
Device Tree Bindings for Register Bit LEDs
Register bit leds are used with syscon multifunctional devices
where single bits in a certain register can turn on/off a
single LED. The register bit LEDs appear as children to the
syscon device, with the proper compatible string. For the
syscon bindings see:
Documentation/devicetree/bindings/mfd/syscon.txt
Each LED is represented as a sub-node of the syscon device. Each
node's name represents the name of the corresponding LED.
LED sub-node properties:
Required properties:
- compatible : must be "register-bit-led"
- offset : register offset to the register controlling this LED
- mask : bit mask for the bit controlling this LED in the register
typically 0x01, 0x02, 0x04 ...
Optional properties:
- label : (optional)
see Documentation/devicetree/bindings/leds/common.txt
- linux,default-trigger : (optional)
see Documentation/devicetree/bindings/leds/common.txt
- default-state: (optional) The initial state of the LED. Valid
values are "on", "off", and "keep". If the LED is already on or off
and the default-state property is set the to same value, then no
glitch should be produced where the LED momentarily turns off (or
on). The "keep" setting will keep the LED at whatever its current
state is, without producing a glitch. The default is off if this
property is not present.
Example:
syscon: syscon@10000000 {
compatible = "arm,realview-pb1176-syscon", "syscon";
reg = <0x10000000 0x1000>;
led@08.0 {
compatible = "register-bit-led";
offset = <0x08>;
mask = <0x01>;
label = "versatile:0";
linux,default-trigger = "heartbeat";
default-state = "on";
};
led@08.1 {
compatible = "register-bit-led";
offset = <0x08>;
mask = <0x02>;
label = "versatile:1";
linux,default-trigger = "mmc0";
default-state = "off";
};
led@08.2 {
compatible = "register-bit-led";
offset = <0x08>;
mask = <0x04>;
label = "versatile:2";
linux,default-trigger = "cpu0";
default-state = "off";
};
led@08.3 {
compatible = "register-bit-led";
offset = <0x08>;
mask = <0x08>;
label = "versatile:3";
default-state = "off";
};
led@08.4 {
compatible = "register-bit-led";
offset = <0x08>;
mask = <0x10>;
label = "versatile:4";
default-state = "off";
};
led@08.5 {
compatible = "register-bit-led";
offset = <0x08>;
mask = <0x20>;
label = "versatile:5";
default-state = "off";
};
led@08.6 {
compatible = "register-bit-led";
offset = <0x08>;
mask = <0x40>;
label = "versatile:6";
default-state = "off";
};
led@08.7 {
compatible = "register-bit-led";
offset = <0x08>;
mask = <0x80>;
label = "versatile:7";
default-state = "off";
};
};
OMAP2+ Mailbox Driver
=====================
The OMAP mailbox hardware facilitates communication between different processors
using a queued mailbox interrupt mechanism. The IP block is external to the
various processor subsystems and is connected on an interconnect bus. The
communication is achieved through a set of registers for message storage and
interrupt configuration registers.
Each mailbox IP block has a certain number of h/w fifo queues and output
interrupt lines. An output interrupt line is routed to an interrupt controller
within a processor subsystem, and there can be more than one line going to a
specific processor's interrupt controller. The interrupt line connections are
fixed for an instance and are dictated by the IP integration into the SoC
(excluding the SoCs that have a Interrupt Crossbar IP). Each interrupt line is
programmable through a set of interrupt configuration registers, and have a rx
and tx interrupt source per h/w fifo. Communication between different processors
is achieved through the appropriate programming of the rx and tx interrupt
sources on the appropriate interrupt lines.
The number of h/w fifo queues and interrupt lines dictate the usable registers.
All the current OMAP SoCs except for the newest DRA7xx SoC has a single IP
instance. DRA7xx has multiple instances with different number of h/w fifo queues
and interrupt lines between different instances. The interrupt lines can also be
routed to different processor sub-systems on DRA7xx as they are routed through
the Crossbar, a kind of interrupt router/multiplexer.
Mailbox Device Node:
====================
A Mailbox device node is used to represent a Mailbox IP instance within a SoC.
The sub-mailboxes are represented as child nodes of this parent node.
Required properties:
--------------------
- compatible: Should be one of the following,
"ti,omap2-mailbox" for OMAP2420, OMAP2430 SoCs
"ti,omap3-mailbox" for OMAP3430, OMAP3630 SoCs
"ti,omap4-mailbox" for OMAP44xx, OMAP54xx, AM33xx,
AM43xx and DRA7xx SoCs
- reg: Contains the mailbox register address range (base
address and length)
- interrupts: Contains the interrupt information for the mailbox
device. The format is dependent on which interrupt
controller the OMAP device uses
- ti,hwmods: Name of the hwmod associated with the mailbox
- ti,mbox-num-users: Number of targets (processor devices) that the mailbox
device can interrupt
- ti,mbox-num-fifos: Number of h/w fifo queues within the mailbox IP block
Child Nodes:
============
A child node is used for representing the actual sub-mailbox device that is
used for the communication between the host processor and a remote processor.
Each child node should have a unique node name across all the different
mailbox device nodes.
Required properties:
--------------------
- ti,mbox-tx: sub-mailbox descriptor property defining a Tx fifo
- ti,mbox-rx: sub-mailbox descriptor property defining a Rx fifo
Sub-mailbox Descriptor Data
---------------------------
Each of the above ti,mbox-tx and ti,mbox-rx properties should have 3 cells of
data that represent the following:
Cell #1 (fifo_id) - mailbox fifo id used either for transmitting
(ti,mbox-tx) or for receiving (ti,mbox-rx)
Cell #2 (irq_id) - irq identifier index number to use from the parent's
interrupts data. Should be 0 for most of the cases, a
positive index value is seen only on mailboxes that have
multiple interrupt lines connected to the MPU processor.
Cell #3 (usr_id) - mailbox user id for identifying the interrupt line
associated with generating a tx/rx fifo interrupt.
Example:
--------
/* OMAP4 */
mailbox: mailbox@4a0f4000 {
compatible = "ti,omap4-mailbox";
reg = <0x4a0f4000 0x200>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "mailbox";
ti,mbox-num-users = <3>;
ti,mbox-num-fifos = <8>;
mbox_ipu: mbox_ipu {
ti,mbox-tx = <0 0 0>;
ti,mbox-rx = <1 0 0>;
};
mbox_dsp: mbox_dsp {
ti,mbox-tx = <3 0 0>;
ti,mbox-rx = <2 0 0>;
};
};
/* AM33xx */
mailbox: mailbox@480C8000 {
compatible = "ti,omap4-mailbox";
reg = <0x480C8000 0x200>;
interrupts = <77>;
ti,hwmods = "mailbox";
ti,mbox-num-users = <4>;
ti,mbox-num-fifos = <8>;
mbox_wkupm3: wkup_m3 {
ti,mbox-tx = <0 0 0>;
ti,mbox-rx = <0 0 3>;
};
};
* sun6i Real Time Clock
RTC controller for the Allwinner A31
Required properties:
- compatible : Should be "allwinner,sun6i-a31-rtc"
- reg : physical base address of the controller and length of
memory mapped region.
- interrupts : IRQ lines for the RTC alarm 0 and alarm 1, in that order.
Example:
rtc: rtc@01f00000 {
compatible = "allwinner,sun6i-a31-rtc";
reg = <0x01f00000 0x54>;
interrupts = <0 40 4>, <0 41 4>;
};
Keystone Navigator DMA Controller
This document explains the device tree bindings for the packet dma
on keystone devices. The Keystone Navigator DMA driver sets up the dma
channels and flows for the QMSS(Queue Manager SubSystem) who triggers
the actual data movements across clients using destination queues. Every
client modules like NETCP(Network Coprocessor), SRIO(Serial Rapid IO),
CRYPTO Engines etc has its own instance of dma hardware. QMSS has also
an internal packet DMA module which is used as an infrastructure DMA
with zero copy.
Navigator DMA cloud layout:
------------------
| Navigator DMAs |
------------------
|
|-> DMA instance #0
|
|-> DMA instance #1
.
.
|
|-> DMA instance #n
Navigator DMA properties:
Required properties:
- compatible: Should be "ti,keystone-navigator-dma"
- clocks: phandle to dma instances clocks. The clock handles can be as
many as the dma instances. The order should be maintained as per
the dma instances.
- ti,navigator-cloud-address: Should contain base address for the multi-core
navigator cloud and number of addresses depends on SOC integration
configuration.. Navigator cloud global address needs to be programmed
into DMA and the DMA uses it as the physical addresses to reach queue
managers. Note that these addresses though points to queue managers,
they are relevant only from DMA perspective. The QMSS may not choose to
use them since it has a different address space view to reach all
its components.
DMA instance properties:
Required properties:
- reg: Should contain register location and length of the following dma
register regions. Register regions should be specified in the following
order.
- Global control register region (global).
- Tx DMA channel configuration register region (txchan).
- Rx DMA channel configuration register region (rxchan).
- Tx DMA channel Scheduler configuration register region (txsched).
- Rx DMA flow configuration register region (rxflow).
Optional properties:
- reg-names: Names for the register regions.
- ti,enable-all: Enable all DMA channels vs clients opening specific channels
what they need. This property is useful for the userspace fast path
case where the linux drivers enables the channels used by userland
stack.
- ti,loop-back: To loopback Tx streaming I/F to Rx streaming I/F. Used for
infrastructure transfers.
- ti,rx-retry-timeout: Number of dma cycles to wait before retry on buffer
starvation.
Example:
knav_dmas: knav_dmas@0 {
compatible = "ti,keystone-navigator-dma";
clocks = <&papllclk>, <&clkxge>;
#address-cells = <1>;
#size-cells = <1>;
ranges;
ti,navigator-cloud-address = <0x23a80000 0x23a90000
0x23aa0000 0x23ab0000>;
dma_gbe: dma_gbe@0 {
reg = <0x2004000 0x100>,
<0x2004400 0x120>,
<0x2004800 0x300>,
<0x2004c00 0x120>,
<0x2005000 0x400>;
reg-names = "global", "txchan", "rxchan",
"txsched", "rxflow";
};
dma_xgbe: dma_xgbe@0 {
reg = <0x2fa1000 0x100>,
<0x2fa1400 0x200>,
<0x2fa1800 0x200>,
<0x2fa1c00 0x200>,
<0x2fa2000 0x400>;
reg-names = "global", "txchan", "rxchan",
"txsched", "rxflow";
};
};
Navigator DMA client:
Required properties:
- ti,navigator-dmas: List of one or more DMA specifiers, each consisting of
- A phandle pointing to DMA instance node
- A DMA channel number as a phandle arg.
- ti,navigator-dma-names: Contains dma channel name for each DMA specifier in
the 'ti,navigator-dmas' property.
Example:
netcp: netcp@2090000 {
..
ti,navigator-dmas = <&dma_gbe 22>,
<&dma_gbe 23>,
<&dma_gbe 8>;
ti,navigator-dma-names = "netrx0", "netrx1", "nettx";
..
};
* Texas Instruments Keystone Navigator Queue Management SubSystem driver
The QMSS (Queue Manager Sub System) found on Keystone SOCs is one of
the main hardware sub system which forms the backbone of the Keystone
multi-core Navigator. QMSS consist of queue managers, packed-data structure
processors(PDSP), linking RAM, descriptor pools and infrastructure
Packet DMA.
The Queue Manager is a hardware module that is responsible for accelerating
management of the packet queues. Packets are queued/de-queued by writing or
reading descriptor address to a particular memory mapped location. The PDSPs
perform QMSS related functions like accumulation, QoS, or event management.
Linking RAM registers are used to link the descriptors which are stored in
descriptor RAM. Descriptor RAM is configurable as internal or external memory.
The QMSS driver manages the PDSP setups, linking RAM regions,
queue pool management (allocation, push, pop and notify) and descriptor
pool management.
Required properties:
- compatible : Must be "ti,keystone-navigator-qmss";
- clocks : phandle to the reference clock for this device.
- queue-range : <start number> total range of queue numbers for the device.
- linkram0 : <address size> for internal link ram, where size is the total
link ram entries.
- linkram1 : <address size> for external link ram, where size is the total
external link ram entries. If the address is specified as "0"
driver will allocate memory.
- qmgrs : child node describing the individual queue managers on the
SoC. On keystone 1 devices there should be only one node.
On keystone 2 devices there can be more than 1 node.
-- managed-queues : the actual queues managed by each queue manager
instance, specified as <"base queue #" "# of queues">.
-- reg : Address and size of the register set for the device.
Register regions should be specified in the following
order
- Queue Peek region.
- Queue status RAM.
- Queue configuration region.
- Descriptor memory setup region.
- Queue Management/Queue Proxy region for queue Push.
- Queue Management/Queue Proxy region for queue Pop.
- queue-pools : child node classifying the queue ranges into pools.
Queue ranges are grouped into 3 type of pools:
- qpend : pool of qpend(interruptible) queues
- general-purpose : pool of general queues, primarly used
as free descriptor queues or the
transmit DMA queues.
- accumulator : pool of queues on PDSP accumulator channel
Each range can have the following properties:
-- qrange : number of queues to use per queue range, specified as
<"base queue #" "# of queues">.
-- interrupts : Optional property to specify the interrupt mapping
for interruptible queues. The driver additionaly sets
the interrupt affinity hint based on the cpu mask.
-- qalloc-by-id : Optional property to specify that the queues in this
range can only be allocated by queue id.
-- accumulator : Accumulator channel specification. Any of the PDSPs in
QMSS can be loaded with the accumulator firmware. The
accumulator firmware’s job is to poll a select number of
queues looking for descriptors that have been pushed
into them. Descriptors are popped from the queue and
placed in a buffer provided by the host. When the list
becomes full or a programmed time period expires, the
accumulator triggers an interrupt to the host to read
the buffer for descriptor information. This firmware
comes in 16, 32, and 48 channel builds. Each of these
channels can be configured to monitor 32 contiguous
queues. Accumulator channel property is specified as:
<pdsp-id, channel, entries, pacing mode, latency>
pdsp-id : QMSS PDSP running accumulator firmware
on which the channel has to be
configured
channel : Accumulator channel number
entries : Size of the accumulator descriptor list
pacing mode : Interrupt pacing mode
0 : None, i.e interrupt on list full only
1 : Time delay since last interrupt
2 : Time delay since first new packet
3 : Time delay since last new packet
latency : time to delay the interrupt, specified
in microseconds.
-- multi-queue : Optional property to specify that the channel has to
monitor upto 32 queues starting at the base queue #.
- descriptor-regions : child node describing the memory regions for keystone
navigator packet DMA descriptors. The memory for
descriptors will be allocated by the driver.
-- id : region number in QMSS.
-- region-spec : specifies the number of descriptors in the
region, specified as
<"# of descriptors" "descriptor size">.
-- link-index : start index, i.e. index of the first
descriptor in the region.
Optional properties:
- dma-coherent : Present if DMA operations are coherent.
- pdsps : child node describing the PDSP configuration.
-- firmware : firmware to be loaded on the PDSP.
-- id : the qmss pdsp that will run the firmware.
-- reg : Address and size of the register set for the PDSP.
Register regions should be specified in the following
order
- PDSP internal RAM region.
- PDSP control/status region registers.
- QMSS interrupt distributor registers.
- PDSP command interface region.
Example:
qmss: qmss@2a40000 {
compatible = "ti,keystone-qmss";
dma-coherent;
#address-cells = <1>;
#size-cells = <1>;
clocks = <&chipclk13>;
ranges;
queue-range = <0 0x4000>;
linkram0 = <0x100000 0x8000>;
linkram1 = <0x0 0x10000>;
qmgrs {
#address-cells = <1>;
#size-cells = <1>;
ranges;
qmgr0 {
managed-queues = <0 0x2000>;
reg = <0x2a40000 0x20000>,
<0x2a06000 0x400>,
<0x2a02000 0x1000>,
<0x2a03000 0x1000>,
<0x23a80000 0x20000>,
<0x2a80000 0x20000>;
};
qmgr1 {
managed-queues = <0x2000 0x2000>;
reg = <0x2a60000 0x20000>,
<0x2a06400 0x400>,
<0x2a04000 0x1000>,
<0x2a05000 0x1000>,
<0x23aa0000 0x20000>,
<0x2aa0000 0x20000>;
};
};
queue-pools {
qpend {
qpend-0 {
qrange = <658 8>;
interrupts =<0 40 0xf04 0 41 0xf04 0 42 0xf04
0 43 0xf04 0 44 0xf04 0 45 0xf04
0 46 0xf04 0 47 0xf04>;
};
qpend-1 {
qrange = <8704 16>;
interrupts = <0 48 0xf04 0 49 0xf04 0 50 0xf04
0 51 0xf04 0 52 0xf04 0 53 0xf04
0 54 0xf04 0 55 0xf04 0 56 0xf04
0 57 0xf04 0 58 0xf04 0 59 0xf04
0 60 0xf04 0 61 0xf04 0 62 0xf04
0 63 0xf04>;
qalloc-by-id;
};
qpend-2 {
qrange = <8720 16>;
interrupts = <0 64 0xf04 0 65 0xf04 0 66 0xf04
0 59 0xf04 0 68 0xf04 0 69 0xf04
0 70 0xf04 0 71 0xf04 0 72 0xf04
0 73 0xf04 0 74 0xf04 0 75 0xf04
0 76 0xf04 0 77 0xf04 0 78 0xf04
0 79 0xf04>;
};
};
general-purpose {
gp-0 {
qrange = <4000 64>;
};
netcp-tx {
qrange = <640 9>;
qalloc-by-id;
};
};
accumulator {
acc-0 {
qrange = <128 32>;
accumulator = <0 36 16 2 50>;
interrupts = <0 215 0xf01>;
multi-queue;
qalloc-by-id;
};
acc-1 {
qrange = <160 32>;
accumulator = <0 37 16 2 50>;
interrupts = <0 216 0xf01>;
multi-queue;
};
acc-2 {
qrange = <192 32>;
accumulator = <0 38 16 2 50>;
interrupts = <0 217 0xf01>;
multi-queue;
};
acc-3 {
qrange = <224 32>;
accumulator = <0 39 16 2 50>;
interrupts = <0 218 0xf01>;
multi-queue;
};
};
};
descriptor-regions {
#address-cells = <1>;
#size-cells = <1>;
ranges;
region-12 {
id = <12>;
region-spec = <8192 128>; /* num_desc desc_size */
link-index = <0x4000>;
};
};
pdsps {
#address-cells = <1>;
#size-cells = <1>;
ranges;
pdsp0@0x2a10000 {
firmware = "keystone/qmss_pdsp_acc48_k2_le_1_0_0_8.fw";
reg = <0x2a10000 0x1000>,
<0x2a0f000 0x100>,
<0x2a0c000 0x3c8>,
<0x2a20000 0x4000>;
id = <0>;
};
};
}; /* qmss */
...@@ -1404,6 +1404,11 @@ M: Dinh Nguyen <dinguyen@opensource.altera.com> ...@@ -1404,6 +1404,11 @@ M: Dinh Nguyen <dinguyen@opensource.altera.com>
S: Maintained S: Maintained
F: drivers/clk/socfpga/ F: drivers/clk/socfpga/
ARM/SOCFPGA EDAC SUPPORT
M: Thor Thayer <tthayer@opensource.altera.com>
S: Maintained
F: drivers/edac/altera_edac.
ARM/STI ARCHITECTURE ARM/STI ARCHITECTURE
M: Srinivas Kandagatla <srinivas.kandagatla@gmail.com> M: Srinivas Kandagatla <srinivas.kandagatla@gmail.com>
M: Maxime Coquelin <maxime.coquelin@st.com> M: Maxime Coquelin <maxime.coquelin@st.com>
...@@ -9194,6 +9199,15 @@ F: drivers/misc/tifm* ...@@ -9194,6 +9199,15 @@ F: drivers/misc/tifm*
F: drivers/mmc/host/tifm_sd.c F: drivers/mmc/host/tifm_sd.c
F: include/linux/tifm.h F: include/linux/tifm.h
TI KEYSTONE MULTICORE NAVIGATOR DRIVERS
M: Santosh Shilimkar <santosh.shilimkar@ti.com>
L: linux-kernel@vger.kernel.org
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: drivers/soc/ti/*
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ssantosh/linux-keystone.git
TI LM49xxx FAMILY ASoC CODEC DRIVERS TI LM49xxx FAMILY ASoC CODEC DRIVERS
M: M R Swami Reddy <mr.swami.reddy@ti.com> M: M R Swami Reddy <mr.swami.reddy@ti.com>
M: Vishwas A Deshpande <vishwas.a.deshpande@ti.com> M: Vishwas A Deshpande <vishwas.a.deshpande@ti.com>
......
...@@ -133,10 +133,9 @@ scrm_clockdomains: clockdomains { ...@@ -133,10 +133,9 @@ scrm_clockdomains: clockdomains {
}; };
intc: interrupt-controller@48200000 { intc: interrupt-controller@48200000 {
compatible = "ti,omap2-intc"; compatible = "ti,am33xx-intc";
interrupt-controller; interrupt-controller;
#interrupt-cells = <1>; #interrupt-cells = <1>;
ti,intc-size = <128>;
reg = <0x48200000 0x1000>; reg = <0x48200000 0x1000>;
}; };
......
...@@ -75,7 +75,6 @@ intc: interrupt-controller@1 { ...@@ -75,7 +75,6 @@ intc: interrupt-controller@1 {
compatible = "ti,omap2-intc"; compatible = "ti,omap2-intc";
interrupt-controller; interrupt-controller;
#interrupt-cells = <1>; #interrupt-cells = <1>;
ti,intc-size = <96>;
reg = <0x480FE000 0x1000>; reg = <0x480FE000 0x1000>;
}; };
......
...@@ -6,3 +6,10 @@ / { ...@@ -6,3 +6,10 @@ / {
model = "Nokia N810"; model = "Nokia N810";
compatible = "nokia,n810", "nokia,n8x0", "ti,omap2420", "ti,omap2"; compatible = "nokia,n810", "nokia,n8x0", "ti,omap2420", "ti,omap2";
}; };
&i2c2 {
aic3x@18 {
compatible = "tlv320aic3x";
reg = <0x18>;
};
};
...@@ -27,6 +27,12 @@ retu_mfd: retu@1 { ...@@ -27,6 +27,12 @@ retu_mfd: retu@1 {
&i2c1 { &i2c1 {
clock-frequency = <400000>; clock-frequency = <400000>;
pmic@72 {
compatible = "menelaus";
reg = <0x72>;
interrupts = <7 IRQ_TYPE_EDGE_RISING>;
};
}; };
&i2c2 { &i2c2 {
......
...@@ -141,10 +141,9 @@ counter32k: counter@48320000 { ...@@ -141,10 +141,9 @@ counter32k: counter@48320000 {
}; };
intc: interrupt-controller@48200000 { intc: interrupt-controller@48200000 {
compatible = "ti,omap2-intc"; compatible = "ti,omap3-intc";
interrupt-controller; interrupt-controller;
#interrupt-cells = <1>; #interrupt-cells = <1>;
ti,intc-size = <96>;
reg = <0x48200000 0x1000>; reg = <0x48200000 0x1000>;
}; };
......
...@@ -36,17 +36,6 @@ config OLD_IRQ_AT91 ...@@ -36,17 +36,6 @@ config OLD_IRQ_AT91
select MULTI_IRQ_HANDLER select MULTI_IRQ_HANDLER
select SPARSE_IRQ select SPARSE_IRQ
config AT91_SAM9_ALT_RESET
bool
default !ARCH_AT91X40
config AT91_SAM9G45_RESET
bool
default !ARCH_AT91X40
config AT91_SAM9_TIME
bool
config HAVE_AT91_SMD config HAVE_AT91_SMD
bool bool
...@@ -55,18 +44,20 @@ config HAVE_AT91_H32MX ...@@ -55,18 +44,20 @@ config HAVE_AT91_H32MX
config SOC_AT91SAM9 config SOC_AT91SAM9
bool bool
select AT91_SAM9_TIME
select ATMEL_AIC_IRQ if !OLD_IRQ_AT91 select ATMEL_AIC_IRQ if !OLD_IRQ_AT91
select CPU_ARM926T select CPU_ARM926T
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select MEMORY if USE_OF
select ATMEL_SDRAMC if USE_OF
config SOC_SAMA5 config SOC_SAMA5
bool bool
select AT91_SAM9_TIME
select ATMEL_AIC5_IRQ select ATMEL_AIC5_IRQ
select CPU_V7 select CPU_V7
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select USE_OF select USE_OF
select MEMORY
select ATMEL_SDRAMC
menu "Atmel AT91 System-on-Chip" menu "Atmel AT91 System-on-Chip"
......
...@@ -9,9 +9,6 @@ obj- := ...@@ -9,9 +9,6 @@ obj- :=
obj-$(CONFIG_OLD_IRQ_AT91) += irq.o obj-$(CONFIG_OLD_IRQ_AT91) += irq.o
obj-$(CONFIG_OLD_CLK_AT91) += clock.o obj-$(CONFIG_OLD_CLK_AT91) += clock.o
obj-$(CONFIG_AT91_SAM9_ALT_RESET) += at91sam9_alt_reset.o
obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o
obj-$(CONFIG_AT91_SAM9_TIME) += at91sam926x_time.o
obj-$(CONFIG_SOC_AT91SAM9) += sam9_smc.o obj-$(CONFIG_SOC_AT91SAM9) += sam9_smc.o
# CPU-specific support # CPU-specific support
......
/*
* arch/arm/mach-at91/include/mach/at91_rstc.h
*
* Copyright (C) 2007 Andrew Victor
* Copyright (C) 2007 Atmel Corporation.
*
* Reset Controller (RSTC) - System peripherals regsters.
* Based on AT91SAM9261 datasheet revision D.
*
* 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 AT91_RSTC_H
#define AT91_RSTC_H
#ifndef __ASSEMBLY__
extern void __iomem *at91_rstc_base;
#define at91_rstc_read(field) \
__raw_readl(at91_rstc_base + field)
#define at91_rstc_write(field, value) \
__raw_writel(value, at91_rstc_base + field)
#else
.extern at91_rstc_base
#endif
#define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */
#define AT91_RSTC_PROCRST (1 << 0) /* Processor Reset */
#define AT91_RSTC_PERRST (1 << 2) /* Peripheral Reset */
#define AT91_RSTC_EXTRST (1 << 3) /* External Reset */
#define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */
#define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */
#define AT91_RSTC_URSTS (1 << 0) /* User Reset Status */
#define AT91_RSTC_RSTTYP (7 << 8) /* Reset Type */
#define AT91_RSTC_RSTTYP_GENERAL (0 << 8)
#define AT91_RSTC_RSTTYP_WAKEUP (1 << 8)
#define AT91_RSTC_RSTTYP_WATCHDOG (2 << 8)
#define AT91_RSTC_RSTTYP_SOFTWARE (3 << 8)
#define AT91_RSTC_RSTTYP_USER (4 << 8)
#define AT91_RSTC_NRSTL (1 << 16) /* NRST Pin Level */
#define AT91_RSTC_SRCMP (1 << 17) /* Software Reset Command in Progress */
#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */
#define AT91_RSTC_URSTEN (1 << 0) /* User Reset Enable */
#define AT91_RSTC_URSTIEN (1 << 4) /* User Reset Interrupt Enable */
#define AT91_RSTC_ERSTL (0xf << 8) /* External Reset Length */
#endif
/*
* arch/arm/mach-at91/include/mach/at91_shdwc.h
*
* Copyright (C) 2007 Andrew Victor
* Copyright (C) 2007 Atmel Corporation.
*
* Shutdown Controller (SHDWC) - System peripherals regsters.
* Based on AT91SAM9261 datasheet revision D.
*
* 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 AT91_SHDWC_H
#define AT91_SHDWC_H
#ifndef __ASSEMBLY__
extern void __iomem *at91_shdwc_base;
#define at91_shdwc_read(field) \
__raw_readl(at91_shdwc_base + field)
#define at91_shdwc_write(field, value) \
__raw_writel(value, at91_shdwc_base + field)
#endif
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
#define AT91_SHDW_SHDW (1 << 0) /* Shut Down command */
#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
#define AT91_SHDW_MR 0x04 /* Shut Down Mode Register */
#define AT91_SHDW_WKMODE0 (3 << 0) /* Wake-up 0 Mode Selection */
#define AT91_SHDW_WKMODE0_NONE 0
#define AT91_SHDW_WKMODE0_HIGH 1
#define AT91_SHDW_WKMODE0_LOW 2
#define AT91_SHDW_WKMODE0_ANYLEVEL 3
#define AT91_SHDW_CPTWK0_MAX 0xf /* Maximum Counter On Wake Up 0 */
#define AT91_SHDW_CPTWK0 (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */
#define AT91_SHDW_CPTWK0_(x) ((x) << 4)
#define AT91_SHDW_RTTWKEN (1 << 16) /* Real Time Timer Wake-up Enable */
#define AT91_SHDW_RTCWKEN (1 << 17) /* Real Time Clock Wake-up Enable */
#define AT91_SHDW_SR 0x08 /* Shut Down Status Register */
#define AT91_SHDW_WAKEUP0 (1 << 0) /* Wake-up 0 Status */
#define AT91_SHDW_RTTWK (1 << 16) /* Real-time Timer Wake-up */
#define AT91_SHDW_RTCWK (1 << 17) /* Real-time Clock Wake-up [SAM9RL] */
#endif
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk/at91_pmc.h> #include <linux/clk/at91_pmc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
...@@ -24,7 +25,6 @@ ...@@ -24,7 +25,6 @@
#include <mach/hardware.h> #include <mach/hardware.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_rstc.h"
#include "soc.h" #include "soc.h"
#include "generic.h" #include "generic.h"
#include "sam9_smc.h" #include "sam9_smc.h"
...@@ -342,8 +342,6 @@ static void __init at91sam9260_map_io(void) ...@@ -342,8 +342,6 @@ static void __init at91sam9260_map_io(void)
static void __init at91sam9260_ioremap_registers(void) static void __init at91sam9260_ioremap_registers(void)
{ {
at91_ioremap_shdwc(AT91SAM9260_BASE_SHDWC);
at91_ioremap_rstc(AT91SAM9260_BASE_RSTC);
at91_ioremap_ramc(0, AT91SAM9260_BASE_SDRAMC, 512); at91_ioremap_ramc(0, AT91SAM9260_BASE_SDRAMC, 512);
at91sam926x_ioremap_pit(AT91SAM9260_BASE_PIT); at91sam926x_ioremap_pit(AT91SAM9260_BASE_PIT);
at91sam9_ioremap_smc(0, AT91SAM9260_BASE_SMC); at91sam9_ioremap_smc(0, AT91SAM9260_BASE_SMC);
...@@ -354,7 +352,6 @@ static void __init at91sam9260_ioremap_registers(void) ...@@ -354,7 +352,6 @@ static void __init at91sam9260_ioremap_registers(void)
static void __init at91sam9260_initialize(void) static void __init at91sam9260_initialize(void)
{ {
arm_pm_idle = at91sam9_idle; arm_pm_idle = at91sam9_idle;
arm_pm_restart = at91sam9_alt_restart;
at91_sysirq_mask_rtt(AT91SAM9260_BASE_RTT); at91_sysirq_mask_rtt(AT91SAM9260_BASE_RTT);
...@@ -362,6 +359,45 @@ static void __init at91sam9260_initialize(void) ...@@ -362,6 +359,45 @@ static void __init at91sam9260_initialize(void)
at91_gpio_init(at91sam9260_gpio, 3); at91_gpio_init(at91sam9260_gpio, 3);
} }
static struct resource rstc_resources[] = {
[0] = {
.start = AT91SAM9260_BASE_RSTC,
.end = AT91SAM9260_BASE_RSTC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AT91SAM9260_BASE_SDRAMC,
.end = AT91SAM9260_BASE_SDRAMC + SZ_512 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device rstc_device = {
.name = "at91-sam9260-reset",
.resource = rstc_resources,
.num_resources = ARRAY_SIZE(rstc_resources),
};
static struct resource shdwc_resources[] = {
[0] = {
.start = AT91SAM9260_BASE_SHDWC,
.end = AT91SAM9260_BASE_SHDWC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device shdwc_device = {
.name = "at91-poweroff",
.resource = shdwc_resources,
.num_resources = ARRAY_SIZE(shdwc_resources),
};
static void __init at91sam9260_register_devices(void)
{
platform_device_register(&rstc_device);
platform_device_register(&shdwc_device);
}
/* -------------------------------------------------------------------- /* --------------------------------------------------------------------
* Interrupt initialization * Interrupt initialization
* -------------------------------------------------------------------- */ * -------------------------------------------------------------------- */
...@@ -404,6 +440,11 @@ static unsigned int at91sam9260_default_irq_priority[NR_AIC_IRQS] __initdata = { ...@@ -404,6 +440,11 @@ static unsigned int at91sam9260_default_irq_priority[NR_AIC_IRQS] __initdata = {
0, /* Advanced Interrupt Controller */ 0, /* Advanced Interrupt Controller */
}; };
static void __init at91sam9260_init_time(void)
{
at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
}
AT91_SOC_START(at91sam9260) AT91_SOC_START(at91sam9260)
.map_io = at91sam9260_map_io, .map_io = at91sam9260_map_io,
.default_irq_priority = at91sam9260_default_irq_priority, .default_irq_priority = at91sam9260_default_irq_priority,
...@@ -411,5 +452,7 @@ AT91_SOC_START(at91sam9260) ...@@ -411,5 +452,7 @@ AT91_SOC_START(at91sam9260)
| (1 << AT91SAM9260_ID_IRQ2), | (1 << AT91SAM9260_ID_IRQ2),
.ioremap_registers = at91sam9260_ioremap_registers, .ioremap_registers = at91sam9260_ioremap_registers,
.register_clocks = at91sam9260_register_clocks, .register_clocks = at91sam9260_register_clocks,
.register_devices = at91sam9260_register_devices,
.init = at91sam9260_initialize, .init = at91sam9260_initialize,
.init_time = at91sam9260_init_time,
AT91_SOC_END AT91_SOC_END
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk/at91_pmc.h> #include <linux/clk/at91_pmc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
...@@ -23,7 +24,6 @@ ...@@ -23,7 +24,6 @@
#include <mach/hardware.h> #include <mach/hardware.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_rstc.h"
#include "soc.h" #include "soc.h"
#include "generic.h" #include "generic.h"
#include "sam9_smc.h" #include "sam9_smc.h"
...@@ -301,8 +301,6 @@ static void __init at91sam9261_map_io(void) ...@@ -301,8 +301,6 @@ static void __init at91sam9261_map_io(void)
static void __init at91sam9261_ioremap_registers(void) static void __init at91sam9261_ioremap_registers(void)
{ {
at91_ioremap_shdwc(AT91SAM9261_BASE_SHDWC);
at91_ioremap_rstc(AT91SAM9261_BASE_RSTC);
at91_ioremap_ramc(0, AT91SAM9261_BASE_SDRAMC, 512); at91_ioremap_ramc(0, AT91SAM9261_BASE_SDRAMC, 512);
at91sam926x_ioremap_pit(AT91SAM9261_BASE_PIT); at91sam926x_ioremap_pit(AT91SAM9261_BASE_PIT);
at91sam9_ioremap_smc(0, AT91SAM9261_BASE_SMC); at91sam9_ioremap_smc(0, AT91SAM9261_BASE_SMC);
...@@ -313,7 +311,6 @@ static void __init at91sam9261_ioremap_registers(void) ...@@ -313,7 +311,6 @@ static void __init at91sam9261_ioremap_registers(void)
static void __init at91sam9261_initialize(void) static void __init at91sam9261_initialize(void)
{ {
arm_pm_idle = at91sam9_idle; arm_pm_idle = at91sam9_idle;
arm_pm_restart = at91sam9_alt_restart;
at91_sysirq_mask_rtt(AT91SAM9261_BASE_RTT); at91_sysirq_mask_rtt(AT91SAM9261_BASE_RTT);
...@@ -321,6 +318,45 @@ static void __init at91sam9261_initialize(void) ...@@ -321,6 +318,45 @@ static void __init at91sam9261_initialize(void)
at91_gpio_init(at91sam9261_gpio, 3); at91_gpio_init(at91sam9261_gpio, 3);
} }
static struct resource rstc_resources[] = {
[0] = {
.start = AT91SAM9261_BASE_RSTC,
.end = AT91SAM9261_BASE_RSTC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AT91SAM9261_BASE_SDRAMC,
.end = AT91SAM9261_BASE_SDRAMC + SZ_512 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device rstc_device = {
.name = "at91-sam9260-reset",
.resource = rstc_resources,
.num_resources = ARRAY_SIZE(rstc_resources),
};
static struct resource shdwc_resources[] = {
[0] = {
.start = AT91SAM9261_BASE_SHDWC,
.end = AT91SAM9261_BASE_SHDWC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device shdwc_device = {
.name = "at91-poweroff",
.resource = shdwc_resources,
.num_resources = ARRAY_SIZE(shdwc_resources),
};
static void __init at91sam9261_register_devices(void)
{
platform_device_register(&rstc_device);
platform_device_register(&shdwc_device);
}
/* -------------------------------------------------------------------- /* --------------------------------------------------------------------
* Interrupt initialization * Interrupt initialization
* -------------------------------------------------------------------- */ * -------------------------------------------------------------------- */
...@@ -363,6 +399,11 @@ static unsigned int at91sam9261_default_irq_priority[NR_AIC_IRQS] __initdata = { ...@@ -363,6 +399,11 @@ static unsigned int at91sam9261_default_irq_priority[NR_AIC_IRQS] __initdata = {
0, /* Advanced Interrupt Controller */ 0, /* Advanced Interrupt Controller */
}; };
static void __init at91sam9261_init_time(void)
{
at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
}
AT91_SOC_START(at91sam9261) AT91_SOC_START(at91sam9261)
.map_io = at91sam9261_map_io, .map_io = at91sam9261_map_io,
.default_irq_priority = at91sam9261_default_irq_priority, .default_irq_priority = at91sam9261_default_irq_priority,
...@@ -370,5 +411,7 @@ AT91_SOC_START(at91sam9261) ...@@ -370,5 +411,7 @@ AT91_SOC_START(at91sam9261)
| (1 << AT91SAM9261_ID_IRQ2), | (1 << AT91SAM9261_ID_IRQ2),
.ioremap_registers = at91sam9261_ioremap_registers, .ioremap_registers = at91sam9261_ioremap_registers,
.register_clocks = at91sam9261_register_clocks, .register_clocks = at91sam9261_register_clocks,
.register_devices = at91sam9261_register_devices,
.init = at91sam9261_initialize, .init = at91sam9261_initialize,
.init_time = at91sam9261_init_time,
AT91_SOC_END AT91_SOC_END
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk/at91_pmc.h> #include <linux/clk/at91_pmc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
...@@ -22,7 +23,6 @@ ...@@ -22,7 +23,6 @@
#include <mach/hardware.h> #include <mach/hardware.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_rstc.h"
#include "soc.h" #include "soc.h"
#include "generic.h" #include "generic.h"
#include "sam9_smc.h" #include "sam9_smc.h"
...@@ -321,8 +321,6 @@ static void __init at91sam9263_map_io(void) ...@@ -321,8 +321,6 @@ static void __init at91sam9263_map_io(void)
static void __init at91sam9263_ioremap_registers(void) static void __init at91sam9263_ioremap_registers(void)
{ {
at91_ioremap_shdwc(AT91SAM9263_BASE_SHDWC);
at91_ioremap_rstc(AT91SAM9263_BASE_RSTC);
at91_ioremap_ramc(0, AT91SAM9263_BASE_SDRAMC0, 512); at91_ioremap_ramc(0, AT91SAM9263_BASE_SDRAMC0, 512);
at91_ioremap_ramc(1, AT91SAM9263_BASE_SDRAMC1, 512); at91_ioremap_ramc(1, AT91SAM9263_BASE_SDRAMC1, 512);
at91sam926x_ioremap_pit(AT91SAM9263_BASE_PIT); at91sam926x_ioremap_pit(AT91SAM9263_BASE_PIT);
...@@ -335,7 +333,6 @@ static void __init at91sam9263_ioremap_registers(void) ...@@ -335,7 +333,6 @@ static void __init at91sam9263_ioremap_registers(void)
static void __init at91sam9263_initialize(void) static void __init at91sam9263_initialize(void)
{ {
arm_pm_idle = at91sam9_idle; arm_pm_idle = at91sam9_idle;
arm_pm_restart = at91sam9_alt_restart;
at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT0); at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT0);
at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT1); at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT1);
...@@ -344,6 +341,45 @@ static void __init at91sam9263_initialize(void) ...@@ -344,6 +341,45 @@ static void __init at91sam9263_initialize(void)
at91_gpio_init(at91sam9263_gpio, 5); at91_gpio_init(at91sam9263_gpio, 5);
} }
static struct resource rstc_resources[] = {
[0] = {
.start = AT91SAM9263_BASE_RSTC,
.end = AT91SAM9263_BASE_RSTC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AT91SAM9263_BASE_SDRAMC0,
.end = AT91SAM9263_BASE_SDRAMC0 + SZ_512 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device rstc_device = {
.name = "at91-sam9260-reset",
.resource = rstc_resources,
.num_resources = ARRAY_SIZE(rstc_resources),
};
static struct resource shdwc_resources[] = {
[0] = {
.start = AT91SAM9263_BASE_SHDWC,
.end = AT91SAM9263_BASE_SHDWC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device shdwc_device = {
.name = "at91-poweroff",
.resource = shdwc_resources,
.num_resources = ARRAY_SIZE(shdwc_resources),
};
static void __init at91sam9263_register_devices(void)
{
platform_device_register(&rstc_device);
platform_device_register(&shdwc_device);
}
/* -------------------------------------------------------------------- /* --------------------------------------------------------------------
* Interrupt initialization * Interrupt initialization
* -------------------------------------------------------------------- */ * -------------------------------------------------------------------- */
...@@ -386,11 +422,18 @@ static unsigned int at91sam9263_default_irq_priority[NR_AIC_IRQS] __initdata = { ...@@ -386,11 +422,18 @@ static unsigned int at91sam9263_default_irq_priority[NR_AIC_IRQS] __initdata = {
0, /* Advanced Interrupt Controller (IRQ1) */ 0, /* Advanced Interrupt Controller (IRQ1) */
}; };
static void __init at91sam9263_init_time(void)
{
at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
}
AT91_SOC_START(at91sam9263) AT91_SOC_START(at91sam9263)
.map_io = at91sam9263_map_io, .map_io = at91sam9263_map_io,
.default_irq_priority = at91sam9263_default_irq_priority, .default_irq_priority = at91sam9263_default_irq_priority,
.extern_irq = (1 << AT91SAM9263_ID_IRQ0) | (1 << AT91SAM9263_ID_IRQ1), .extern_irq = (1 << AT91SAM9263_ID_IRQ0) | (1 << AT91SAM9263_ID_IRQ1),
.ioremap_registers = at91sam9263_ioremap_registers, .ioremap_registers = at91sam9263_ioremap_registers,
.register_clocks = at91sam9263_register_clocks, .register_clocks = at91sam9263_register_clocks,
.register_devices = at91sam9263_register_devices,
.init = at91sam9263_initialize, .init = at91sam9263_initialize,
.init_time = at91sam9263_init_time,
AT91_SOC_END AT91_SOC_END
/*
* reset AT91SAM9G20 as per errata
*
* (C) BitBox Ltd 2010
*
* unless the SDRAM is cleanly shutdown before we hit the
* reset register it can be left driving the data bus and
* killing the chance of a subsequent boot from NAND
*
* 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 <mach/hardware.h>
#include <mach/at91_ramc.h>
#include "at91_rstc.h"
.arm
.globl at91sam9_alt_restart
at91sam9_alt_restart: ldr r0, =at91_ramc_base @ preload constants
ldr r0, [r0]
ldr r4, =at91_rstc_base
ldr r1, [r4]
mov r2, #1
mov r3, #AT91_SDRAMC_LPCB_POWER_DOWN
ldr r4, =AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST
.balign 32 @ align to cache line
str r2, [r0, #AT91_SDRAMC_TR] @ disable SDRAM access
str r3, [r0, #AT91_SDRAMC_LPR] @ power down SDRAM
str r4, [r1, #AT91_RSTC_CR] @ reset processor
b .
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/clk/at91_pmc.h> #include <linux/clk/at91_pmc.h>
#include <linux/platform_device.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
...@@ -371,8 +372,6 @@ static void __init at91sam9g45_map_io(void) ...@@ -371,8 +372,6 @@ static void __init at91sam9g45_map_io(void)
static void __init at91sam9g45_ioremap_registers(void) static void __init at91sam9g45_ioremap_registers(void)
{ {
at91_ioremap_shdwc(AT91SAM9G45_BASE_SHDWC);
at91_ioremap_rstc(AT91SAM9G45_BASE_RSTC);
at91_ioremap_ramc(0, AT91SAM9G45_BASE_DDRSDRC1, 512); at91_ioremap_ramc(0, AT91SAM9G45_BASE_DDRSDRC1, 512);
at91_ioremap_ramc(1, AT91SAM9G45_BASE_DDRSDRC0, 512); at91_ioremap_ramc(1, AT91SAM9G45_BASE_DDRSDRC0, 512);
at91sam926x_ioremap_pit(AT91SAM9G45_BASE_PIT); at91sam926x_ioremap_pit(AT91SAM9G45_BASE_PIT);
...@@ -384,7 +383,6 @@ static void __init at91sam9g45_ioremap_registers(void) ...@@ -384,7 +383,6 @@ static void __init at91sam9g45_ioremap_registers(void)
static void __init at91sam9g45_initialize(void) static void __init at91sam9g45_initialize(void)
{ {
arm_pm_idle = at91sam9_idle; arm_pm_idle = at91sam9_idle;
arm_pm_restart = at91sam9g45_restart;
at91_sysirq_mask_rtc(AT91SAM9G45_BASE_RTC); at91_sysirq_mask_rtc(AT91SAM9G45_BASE_RTC);
at91_sysirq_mask_rtt(AT91SAM9G45_BASE_RTT); at91_sysirq_mask_rtt(AT91SAM9G45_BASE_RTT);
...@@ -393,6 +391,50 @@ static void __init at91sam9g45_initialize(void) ...@@ -393,6 +391,50 @@ static void __init at91sam9g45_initialize(void)
at91_gpio_init(at91sam9g45_gpio, 5); at91_gpio_init(at91sam9g45_gpio, 5);
} }
static struct resource rstc_resources[] = {
[0] = {
.start = AT91SAM9G45_BASE_RSTC,
.end = AT91SAM9G45_BASE_RSTC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AT91SAM9G45_BASE_DDRSDRC1,
.end = AT91SAM9G45_BASE_DDRSDRC1 + SZ_512 - 1,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = AT91SAM9G45_BASE_DDRSDRC0,
.end = AT91SAM9G45_BASE_DDRSDRC0 + SZ_512 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device rstc_device = {
.name = "at91-sam9g45-reset",
.resource = rstc_resources,
.num_resources = ARRAY_SIZE(rstc_resources),
};
static struct resource shdwc_resources[] = {
[0] = {
.start = AT91SAM9G45_BASE_SHDWC,
.end = AT91SAM9G45_BASE_SHDWC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device shdwc_device = {
.name = "at91-poweroff",
.resource = shdwc_resources,
.num_resources = ARRAY_SIZE(shdwc_resources),
};
static void __init at91sam9g45_register_devices(void)
{
platform_device_register(&rstc_device);
platform_device_register(&shdwc_device);
}
/* -------------------------------------------------------------------- /* --------------------------------------------------------------------
* Interrupt initialization * Interrupt initialization
* -------------------------------------------------------------------- */ * -------------------------------------------------------------------- */
...@@ -435,11 +477,18 @@ static unsigned int at91sam9g45_default_irq_priority[NR_AIC_IRQS] __initdata = { ...@@ -435,11 +477,18 @@ static unsigned int at91sam9g45_default_irq_priority[NR_AIC_IRQS] __initdata = {
0, /* Advanced Interrupt Controller (IRQ0) */ 0, /* Advanced Interrupt Controller (IRQ0) */
}; };
static void __init at91sam9g45_init_time(void)
{
at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
}
AT91_SOC_START(at91sam9g45) AT91_SOC_START(at91sam9g45)
.map_io = at91sam9g45_map_io, .map_io = at91sam9g45_map_io,
.default_irq_priority = at91sam9g45_default_irq_priority, .default_irq_priority = at91sam9g45_default_irq_priority,
.extern_irq = (1 << AT91SAM9G45_ID_IRQ0), .extern_irq = (1 << AT91SAM9G45_ID_IRQ0),
.ioremap_registers = at91sam9g45_ioremap_registers, .ioremap_registers = at91sam9g45_ioremap_registers,
.register_clocks = at91sam9g45_register_clocks, .register_clocks = at91sam9g45_register_clocks,
.register_devices = at91sam9g45_register_devices,
.init = at91sam9g45_initialize, .init = at91sam9g45_initialize,
.init_time = at91sam9g45_init_time,
AT91_SOC_END AT91_SOC_END
/*
* reset AT91SAM9G45 as per errata
*
* Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcosoft.com>
*
* unless the SDRAM is cleanly shutdown before we hit the
* reset register it can be left driving the data bus and
* killing the chance of a subsequent boot from NAND
*
* GPLv2 Only
*/
#include <linux/linkage.h>
#include <mach/hardware.h>
#include <mach/at91_ramc.h>
#include "at91_rstc.h"
.arm
/*
* at91_ramc_base is an array void*
* init at NULL if only one DDR controler is present in or DT
*/
.globl at91sam9g45_restart
at91sam9g45_restart:
ldr r5, =at91_ramc_base @ preload constants
ldr r0, [r5]
ldr r5, [r5, #4] @ ddr1
cmp r5, #0
ldr r4, =at91_rstc_base
ldr r1, [r4]
mov r2, #1
mov r3, #AT91_DDRSDRC_LPCB_POWER_DOWN
ldr r4, =AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST
.balign 32 @ align to cache line
strne r2, [r5, #AT91_DDRSDRC_RTR] @ disable DDR1 access
strne r3, [r5, #AT91_DDRSDRC_LPR] @ power down DDR1
str r2, [r0, #AT91_DDRSDRC_RTR] @ disable DDR0 access
str r3, [r0, #AT91_DDRSDRC_LPR] @ power down DDR0
str r4, [r1, #AT91_RSTC_CR] @ reset processor
b .
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk/at91_pmc.h> #include <linux/clk/at91_pmc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
...@@ -23,7 +24,6 @@ ...@@ -23,7 +24,6 @@
#include <mach/hardware.h> #include <mach/hardware.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_rstc.h"
#include "soc.h" #include "soc.h"
#include "generic.h" #include "generic.h"
#include "sam9_smc.h" #include "sam9_smc.h"
...@@ -311,8 +311,6 @@ static void __init at91sam9rl_map_io(void) ...@@ -311,8 +311,6 @@ static void __init at91sam9rl_map_io(void)
static void __init at91sam9rl_ioremap_registers(void) static void __init at91sam9rl_ioremap_registers(void)
{ {
at91_ioremap_shdwc(AT91SAM9RL_BASE_SHDWC);
at91_ioremap_rstc(AT91SAM9RL_BASE_RSTC);
at91_ioremap_ramc(0, AT91SAM9RL_BASE_SDRAMC, 512); at91_ioremap_ramc(0, AT91SAM9RL_BASE_SDRAMC, 512);
at91sam926x_ioremap_pit(AT91SAM9RL_BASE_PIT); at91sam926x_ioremap_pit(AT91SAM9RL_BASE_PIT);
at91sam9_ioremap_smc(0, AT91SAM9RL_BASE_SMC); at91sam9_ioremap_smc(0, AT91SAM9RL_BASE_SMC);
...@@ -323,7 +321,6 @@ static void __init at91sam9rl_ioremap_registers(void) ...@@ -323,7 +321,6 @@ static void __init at91sam9rl_ioremap_registers(void)
static void __init at91sam9rl_initialize(void) static void __init at91sam9rl_initialize(void)
{ {
arm_pm_idle = at91sam9_idle; arm_pm_idle = at91sam9_idle;
arm_pm_restart = at91sam9_alt_restart;
at91_sysirq_mask_rtc(AT91SAM9RL_BASE_RTC); at91_sysirq_mask_rtc(AT91SAM9RL_BASE_RTC);
at91_sysirq_mask_rtt(AT91SAM9RL_BASE_RTT); at91_sysirq_mask_rtt(AT91SAM9RL_BASE_RTT);
...@@ -332,6 +329,45 @@ static void __init at91sam9rl_initialize(void) ...@@ -332,6 +329,45 @@ static void __init at91sam9rl_initialize(void)
at91_gpio_init(at91sam9rl_gpio, 4); at91_gpio_init(at91sam9rl_gpio, 4);
} }
static struct resource rstc_resources[] = {
[0] = {
.start = AT91SAM9RL_BASE_RSTC,
.end = AT91SAM9RL_BASE_RSTC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AT91SAM9RL_BASE_SDRAMC,
.end = AT91SAM9RL_BASE_SDRAMC + SZ_512 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device rstc_device = {
.name = "at91-sam9260-reset",
.resource = rstc_resources,
.num_resources = ARRAY_SIZE(rstc_resources),
};
static struct resource shdwc_resources[] = {
[0] = {
.start = AT91SAM9RL_BASE_SHDWC,
.end = AT91SAM9RL_BASE_SHDWC + SZ_16 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device shdwc_device = {
.name = "at91-poweroff",
.resource = shdwc_resources,
.num_resources = ARRAY_SIZE(shdwc_resources),
};
static void __init at91sam9rl_register_devices(void)
{
platform_device_register(&rstc_device);
platform_device_register(&shdwc_device);
}
/* -------------------------------------------------------------------- /* --------------------------------------------------------------------
* Interrupt initialization * Interrupt initialization
* -------------------------------------------------------------------- */ * -------------------------------------------------------------------- */
...@@ -374,6 +410,11 @@ static unsigned int at91sam9rl_default_irq_priority[NR_AIC_IRQS] __initdata = { ...@@ -374,6 +410,11 @@ static unsigned int at91sam9rl_default_irq_priority[NR_AIC_IRQS] __initdata = {
0, /* Advanced Interrupt Controller */ 0, /* Advanced Interrupt Controller */
}; };
static void __init at91sam9rl_init_time(void)
{
at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
}
AT91_SOC_START(at91sam9rl) AT91_SOC_START(at91sam9rl)
.map_io = at91sam9rl_map_io, .map_io = at91sam9rl_map_io,
.default_irq_priority = at91sam9rl_default_irq_priority, .default_irq_priority = at91sam9rl_default_irq_priority,
...@@ -382,5 +423,7 @@ AT91_SOC_START(at91sam9rl) ...@@ -382,5 +423,7 @@ AT91_SOC_START(at91sam9rl)
#if defined(CONFIG_OLD_CLK_AT91) #if defined(CONFIG_OLD_CLK_AT91)
.register_clocks = at91sam9rl_register_clocks, .register_clocks = at91sam9rl_register_clocks,
#endif #endif
.register_devices = at91sam9rl_register_devices,
.init = at91sam9rl_initialize, .init = at91sam9rl_initialize,
.init_time = at91sam9rl_init_time,
AT91_SOC_END AT91_SOC_END
...@@ -167,6 +167,8 @@ static struct at91_cf_data afeb9260_cf_data = { ...@@ -167,6 +167,8 @@ static struct at91_cf_data afeb9260_cf_data = {
static void __init afeb9260_board_init(void) static void __init afeb9260_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */ /* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -211,7 +213,7 @@ static void __init afeb9260_board_init(void) ...@@ -211,7 +213,7 @@ static void __init afeb9260_board_init(void)
MACHINE_START(AFEB9260, "Custom afeb9260 board") MACHINE_START(AFEB9260, "Custom afeb9260 board")
/* Maintainer: Sergey Lapin <slapin@ossfans.org> */ /* Maintainer: Sergey Lapin <slapin@ossfans.org> */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = afeb9260_init_early, .init_early = afeb9260_init_early,
......
...@@ -170,6 +170,8 @@ static void __init cam60_add_device_nand(void) ...@@ -170,6 +170,8 @@ static void __init cam60_add_device_nand(void)
static void __init cam60_board_init(void) static void __init cam60_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */ /* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -188,7 +190,7 @@ static void __init cam60_board_init(void) ...@@ -188,7 +190,7 @@ static void __init cam60_board_init(void)
MACHINE_START(CAM60, "KwikByte CAM60") MACHINE_START(CAM60, "KwikByte CAM60")
/* Maintainer: KwikByte */ /* Maintainer: KwikByte */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = cam60_init_early, .init_early = cam60_init_early,
......
...@@ -322,6 +322,8 @@ static struct mci_platform_data __initdata cpu9krea_mci0_data = { ...@@ -322,6 +322,8 @@ static struct mci_platform_data __initdata cpu9krea_mci0_data = {
static void __init cpu9krea_board_init(void) static void __init cpu9krea_board_init(void)
{ {
at91_register_devices();
/* NOR */ /* NOR */
cpu9krea_add_device_nor(); cpu9krea_add_device_nor();
/* Serial */ /* Serial */
...@@ -375,7 +377,7 @@ MACHINE_START(CPUAT9260, "Eukrea CPU9260") ...@@ -375,7 +377,7 @@ MACHINE_START(CPUAT9260, "Eukrea CPU9260")
MACHINE_START(CPUAT9G20, "Eukrea CPU9G20") MACHINE_START(CPUAT9G20, "Eukrea CPU9G20")
#endif #endif
/* Maintainer: Eric Benard - EUKREA Electromatique */ /* Maintainer: Eric Benard - EUKREA Electromatique */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = cpu9krea_init_early, .init_early = cpu9krea_init_early,
......
...@@ -25,15 +25,6 @@ ...@@ -25,15 +25,6 @@
#include "board.h" #include "board.h"
#include "generic.h" #include "generic.h"
static void __init sam9_dt_timer_init(void)
{
#if defined(CONFIG_COMMON_CLK)
of_clk_init(NULL);
#endif
at91sam926x_pit_init();
}
static const char *at91_dt_board_compat[] __initdata = { static const char *at91_dt_board_compat[] __initdata = {
"atmel,at91sam9", "atmel,at91sam9",
NULL NULL
...@@ -41,7 +32,6 @@ static const char *at91_dt_board_compat[] __initdata = { ...@@ -41,7 +32,6 @@ static const char *at91_dt_board_compat[] __initdata = {
DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM (Device Tree)") DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM (Device Tree)")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = sam9_dt_timer_init,
.map_io = at91_map_io, .map_io = at91_map_io,
.init_early = at91_dt_initialize, .init_early = at91_dt_initialize,
.dt_compat = at91_dt_board_compat, .dt_compat = at91_dt_board_compat,
......
...@@ -27,14 +27,6 @@ ...@@ -27,14 +27,6 @@
#include "at91_aic.h" #include "at91_aic.h"
#include "generic.h" #include "generic.h"
static void __init sama5_dt_timer_init(void)
{
#if defined(CONFIG_COMMON_CLK)
of_clk_init(NULL);
#endif
at91sam926x_pit_init();
}
static void __init sama5_dt_device_init(void) static void __init sama5_dt_device_init(void)
{ {
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
...@@ -47,7 +39,6 @@ static const char *sama5_dt_board_compat[] __initconst = { ...@@ -47,7 +39,6 @@ static const char *sama5_dt_board_compat[] __initconst = {
DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)") DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = sama5_dt_timer_init,
.map_io = at91_map_io, .map_io = at91_map_io,
.init_early = at91_dt_initialize, .init_early = at91_dt_initialize,
.init_machine = sama5_dt_device_init, .init_machine = sama5_dt_device_init,
......
...@@ -138,6 +138,8 @@ static struct gpio_led flexibity_leds[] = { ...@@ -138,6 +138,8 @@ static struct gpio_led flexibity_leds[] = {
static void __init flexibity_board_init(void) static void __init flexibity_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */ /* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -160,7 +162,7 @@ static void __init flexibity_board_init(void) ...@@ -160,7 +162,7 @@ static void __init flexibity_board_init(void)
MACHINE_START(FLEXIBITY, "Flexibity Connect") MACHINE_START(FLEXIBITY, "Flexibity Connect")
/* Maintainer: Maxim Osipov */ /* Maintainer: Maxim Osipov */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = flexibity_init_early, .init_early = flexibity_init_early,
......
...@@ -576,7 +576,7 @@ static void __init gsia18s_board_init(void) ...@@ -576,7 +576,7 @@ static void __init gsia18s_board_init(void)
} }
MACHINE_START(GSIA18S, "GS_IA18_S") MACHINE_START(GSIA18S, "GS_IA18_S")
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = gsia18s_init_early, .init_early = gsia18s_init_early,
......
...@@ -219,7 +219,7 @@ static void __init pcontrol_g20_board_init(void) ...@@ -219,7 +219,7 @@ static void __init pcontrol_g20_board_init(void)
MACHINE_START(PCONTROL_G20, "PControl G20") MACHINE_START(PCONTROL_G20, "PControl G20")
/* Maintainer: pgsellmann@portner-elektronik.at */ /* Maintainer: pgsellmann@portner-elektronik.at */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = pcontrol_g20_init_early, .init_early = pcontrol_g20_init_early,
......
...@@ -187,6 +187,8 @@ static struct gpio_led ek_leds[] = { ...@@ -187,6 +187,8 @@ static struct gpio_led ek_leds[] = {
static void __init ek_board_init(void) static void __init ek_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */ /* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -219,7 +221,7 @@ static void __init ek_board_init(void) ...@@ -219,7 +221,7 @@ static void __init ek_board_init(void)
MACHINE_START(SAM9_L9260, "Olimex SAM9-L9260") MACHINE_START(SAM9_L9260, "Olimex SAM9-L9260")
/* Maintainer: Olimex */ /* Maintainer: Olimex */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
......
...@@ -45,7 +45,6 @@ ...@@ -45,7 +45,6 @@
#include <mach/system_rev.h> #include <mach/system_rev.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_shdwc.h"
#include "board.h" #include "board.h"
#include "sam9_smc.h" #include "sam9_smc.h"
#include "generic.h" #include "generic.h"
...@@ -307,6 +306,8 @@ static void __init ek_add_device_buttons(void) {} ...@@ -307,6 +306,8 @@ static void __init ek_add_device_buttons(void) {}
static void __init ek_board_init(void) static void __init ek_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */ /* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -344,7 +345,7 @@ static void __init ek_board_init(void) ...@@ -344,7 +345,7 @@ static void __init ek_board_init(void)
MACHINE_START(AT91SAM9260EK, "Atmel AT91SAM9260-EK") MACHINE_START(AT91SAM9260EK, "Atmel AT91SAM9260-EK")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
......
...@@ -49,7 +49,6 @@ ...@@ -49,7 +49,6 @@
#include <mach/system_rev.h> #include <mach/system_rev.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_shdwc.h"
#include "board.h" #include "board.h"
#include "sam9_smc.h" #include "sam9_smc.h"
#include "generic.h" #include "generic.h"
...@@ -561,6 +560,8 @@ static struct gpio_led ek_leds[] = { ...@@ -561,6 +560,8 @@ static struct gpio_led ek_leds[] = {
static void __init ek_board_init(void) static void __init ek_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */ /* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -603,7 +604,7 @@ static void __init ek_board_init(void) ...@@ -603,7 +604,7 @@ static void __init ek_board_init(void)
MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK") MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
...@@ -613,7 +614,7 @@ MACHINE_END ...@@ -613,7 +614,7 @@ MACHINE_END
MACHINE_START(AT91SAM9G10EK, "Atmel AT91SAM9G10-EK") MACHINE_START(AT91SAM9G10EK, "Atmel AT91SAM9G10-EK")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
......
...@@ -50,7 +50,6 @@ ...@@ -50,7 +50,6 @@
#include <mach/system_rev.h> #include <mach/system_rev.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_shdwc.h"
#include "board.h" #include "board.h"
#include "sam9_smc.h" #include "sam9_smc.h"
#include "generic.h" #include "generic.h"
...@@ -439,6 +438,8 @@ static struct platform_device *devices[] __initdata = { ...@@ -439,6 +438,8 @@ static struct platform_device *devices[] __initdata = {
static void __init ek_board_init(void) static void __init ek_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */ /* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -483,7 +484,7 @@ static void __init ek_board_init(void) ...@@ -483,7 +484,7 @@ static void __init ek_board_init(void)
MACHINE_START(AT91SAM9263EK, "Atmel AT91SAM9263-EK") MACHINE_START(AT91SAM9263EK, "Atmel AT91SAM9263-EK")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
......
...@@ -410,7 +410,7 @@ static void __init ek_board_init(void) ...@@ -410,7 +410,7 @@ static void __init ek_board_init(void)
MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK") MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
...@@ -420,7 +420,7 @@ MACHINE_END ...@@ -420,7 +420,7 @@ MACHINE_END
MACHINE_START(AT91SAM9G20EK_2MMC, "Atmel AT91SAM9G20-EK 2 MMC Slot Mod") MACHINE_START(AT91SAM9G20EK_2MMC, "Atmel AT91SAM9G20-EK 2 MMC Slot Mod")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
......
...@@ -48,7 +48,6 @@ ...@@ -48,7 +48,6 @@
#include <mach/system_rev.h> #include <mach/system_rev.h>
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_shdwc.h"
#include "board.h" #include "board.h"
#include "sam9_smc.h" #include "sam9_smc.h"
#include "generic.h" #include "generic.h"
...@@ -471,6 +470,8 @@ static struct platform_device *devices[] __initdata = { ...@@ -471,6 +470,8 @@ static struct platform_device *devices[] __initdata = {
static void __init ek_board_init(void) static void __init ek_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DGBU on ttyS0. (Rx & Tx only) */ /* DGBU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -517,7 +518,7 @@ static void __init ek_board_init(void) ...@@ -517,7 +518,7 @@ static void __init ek_board_init(void)
MACHINE_START(AT91SAM9M10G45EK, "Atmel AT91SAM9M10G45-EK") MACHINE_START(AT91SAM9M10G45EK, "Atmel AT91SAM9M10G45-EK")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
#include "at91_aic.h" #include "at91_aic.h"
#include "at91_shdwc.h"
#include "board.h" #include "board.h"
#include "sam9_smc.h" #include "sam9_smc.h"
#include "generic.h" #include "generic.h"
...@@ -292,6 +291,8 @@ static void __init ek_add_device_buttons(void) {} ...@@ -292,6 +291,8 @@ static void __init ek_add_device_buttons(void) {}
static void __init ek_board_init(void) static void __init ek_board_init(void)
{ {
at91_register_devices();
/* Serial */ /* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */ /* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0); at91_register_uart(0, 0, 0);
...@@ -323,7 +324,7 @@ static void __init ek_board_init(void) ...@@ -323,7 +324,7 @@ static void __init ek_board_init(void)
MACHINE_START(AT91SAM9RLEK, "Atmel AT91SAM9RL-EK") MACHINE_START(AT91SAM9RLEK, "Atmel AT91SAM9RL-EK")
/* Maintainer: Atmel */ /* Maintainer: Atmel */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = ek_init_early, .init_early = ek_init_early,
......
...@@ -154,6 +154,8 @@ static void __init snapper9260_add_device_nand(void) ...@@ -154,6 +154,8 @@ static void __init snapper9260_add_device_nand(void)
static void __init snapper9260_board_init(void) static void __init snapper9260_board_init(void)
{ {
at91_register_devices();
at91_add_device_i2c(snapper9260_i2c_devices, at91_add_device_i2c(snapper9260_i2c_devices,
ARRAY_SIZE(snapper9260_i2c_devices)); ARRAY_SIZE(snapper9260_i2c_devices));
...@@ -178,7 +180,7 @@ static void __init snapper9260_board_init(void) ...@@ -178,7 +180,7 @@ static void __init snapper9260_board_init(void)
} }
MACHINE_START(SNAPPER_9260, "Bluewater Systems Snapper 9260/9G20 module") MACHINE_START(SNAPPER_9260, "Bluewater Systems Snapper 9260/9G20 module")
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = snapper9260_init_early, .init_early = snapper9260_init_early,
......
...@@ -275,7 +275,7 @@ static void __init stamp9g20evb_board_init(void) ...@@ -275,7 +275,7 @@ static void __init stamp9g20evb_board_init(void)
MACHINE_START(PORTUXG20, "taskit PortuxG20") MACHINE_START(PORTUXG20, "taskit PortuxG20")
/* Maintainer: taskit GmbH */ /* Maintainer: taskit GmbH */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = stamp9g20_init_early, .init_early = stamp9g20_init_early,
...@@ -285,7 +285,7 @@ MACHINE_END ...@@ -285,7 +285,7 @@ MACHINE_END
MACHINE_START(STAMP9G20, "taskit Stamp9G20") MACHINE_START(STAMP9G20, "taskit Stamp9G20")
/* Maintainer: taskit GmbH */ /* Maintainer: taskit GmbH */
.init_time = at91sam926x_pit_init, .init_time = at91_init_time,
.map_io = at91_map_io, .map_io = at91_map_io,
.handle_irq = at91_aic_handle_irq, .handle_irq = at91_aic_handle_irq,
.init_early = stamp9g20_init_early, .init_early = stamp9g20_init_early,
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef _AT91_GENERIC_H
#define _AT91_GENERIC_H
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/reboot.h> #include <linux/reboot.h>
...@@ -38,12 +41,15 @@ extern int __init at91_aic5_of_init(struct device_node *node, ...@@ -38,12 +41,15 @@ extern int __init at91_aic5_of_init(struct device_node *node,
extern void __init at91_sysirq_mask_rtc(u32 rtc_base); extern void __init at91_sysirq_mask_rtc(u32 rtc_base);
extern void __init at91_sysirq_mask_rtt(u32 rtt_base); extern void __init at91_sysirq_mask_rtt(u32 rtt_base);
/* Devices */
extern void __init at91_register_devices(void);
/* Timer */ /* Timer */
extern void __init at91_init_time(void);
extern void at91rm9200_ioremap_st(u32 addr); extern void at91rm9200_ioremap_st(u32 addr);
extern void at91rm9200_timer_init(void); extern void at91rm9200_timer_init(void);
extern void at91sam926x_ioremap_pit(u32 addr); extern void at91sam926x_ioremap_pit(u32 addr);
extern void at91sam926x_pit_init(void); extern void at91sam926x_pit_init(int irq);
extern void at91x40_timer_init(void); extern void at91x40_timer_init(void);
/* Clocks */ /* Clocks */
...@@ -63,14 +69,6 @@ extern void at91_irq_resume(void); ...@@ -63,14 +69,6 @@ extern void at91_irq_resume(void);
/* idle */ /* idle */
extern void at91sam9_idle(void); extern void at91sam9_idle(void);
/* reset */
extern void at91_ioremap_rstc(u32 base_addr);
extern void at91sam9_alt_restart(enum reboot_mode, const char *);
extern void at91sam9g45_restart(enum reboot_mode, const char *);
/* shutdown */
extern void at91_ioremap_shdwc(u32 base_addr);
/* Matrix */ /* Matrix */
extern void at91_ioremap_matrix(u32 base_addr); extern void at91_ioremap_matrix(u32 base_addr);
...@@ -91,3 +89,5 @@ extern int __init at91_gpio_of_irq_setup(struct device_node *node, ...@@ -91,3 +89,5 @@ extern int __init at91_gpio_of_irq_setup(struct device_node *node,
struct device_node *parent); struct device_node *parent);
extern u32 at91_get_extern_irq(void); extern u32 at91_get_extern_irq(void);
#endif /* _AT91_GENERIC_H */
...@@ -34,79 +34,8 @@ ...@@ -34,79 +34,8 @@
#include "pm.h" #include "pm.h"
#include "gpio.h" #include "gpio.h"
/*
* Show the reason for the previous system reset.
*/
#include "at91_rstc.h"
#include "at91_shdwc.h"
static void (*at91_pm_standby)(void); static void (*at91_pm_standby)(void);
static void __init show_reset_status(void)
{
static char reset[] __initdata = "reset";
static char general[] __initdata = "general";
static char wakeup[] __initdata = "wakeup";
static char watchdog[] __initdata = "watchdog";
static char software[] __initdata = "software";
static char user[] __initdata = "user";
static char unknown[] __initdata = "unknown";
static char signal[] __initdata = "signal";
static char rtc[] __initdata = "rtc";
static char rtt[] __initdata = "rtt";
static char restore[] __initdata = "power-restored";
char *reason, *r2 = reset;
u32 reset_type, wake_type;
if (!at91_shdwc_base || !at91_rstc_base)
return;
reset_type = at91_rstc_read(AT91_RSTC_SR) & AT91_RSTC_RSTTYP;
wake_type = at91_shdwc_read(AT91_SHDW_SR);
switch (reset_type) {
case AT91_RSTC_RSTTYP_GENERAL:
reason = general;
break;
case AT91_RSTC_RSTTYP_WAKEUP:
/* board-specific code enabled the wakeup sources */
reason = wakeup;
/* "wakeup signal" */
if (wake_type & AT91_SHDW_WAKEUP0)
r2 = signal;
else {
r2 = reason;
if (wake_type & AT91_SHDW_RTTWK) /* rtt wakeup */
reason = rtt;
else if (wake_type & AT91_SHDW_RTCWK) /* rtc wakeup */
reason = rtc;
else if (wake_type == 0) /* power-restored wakeup */
reason = restore;
else /* unknown wakeup */
reason = unknown;
}
break;
case AT91_RSTC_RSTTYP_WATCHDOG:
reason = watchdog;
break;
case AT91_RSTC_RSTTYP_SOFTWARE:
reason = software;
break;
case AT91_RSTC_RSTTYP_USER:
reason = user;
break;
default:
reason = unknown;
break;
}
pr_info("AT91: Starting after %s %s\n", reason, r2);
}
static int at91_pm_valid_state(suspend_state_t state) static int at91_pm_valid_state(suspend_state_t state)
{ {
switch (state) { switch (state) {
...@@ -346,7 +275,6 @@ static int __init at91_pm_init(void) ...@@ -346,7 +275,6 @@ static int __init at91_pm_init(void)
suspend_set_ops(&at91_pm_ops); suspend_set_ops(&at91_pm_ops);
show_reset_status();
return 0; return 0;
} }
arch_initcall(at91_pm_init); arch_initcall(at91_pm_init);
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
* Under GPLv2 * Under GPLv2
*/ */
#define pr_fmt(fmt) "AT91: " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/mm.h> #include <linux/mm.h>
...@@ -20,7 +22,6 @@ ...@@ -20,7 +22,6 @@
#include <mach/cpu.h> #include <mach/cpu.h>
#include <mach/at91_dbgu.h> #include <mach/at91_dbgu.h>
#include "at91_shdwc.h"
#include "soc.h" #include "soc.h"
#include "generic.h" #include "generic.h"
#include "pm.h" #include "pm.h"
...@@ -37,7 +38,7 @@ void __init at91rm9200_set_type(int type) ...@@ -37,7 +38,7 @@ void __init at91rm9200_set_type(int type)
else else
at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA; at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA;
pr_info("AT91: filled in soc subtype: %s\n", pr_info("filled in soc subtype: %s\n",
at91_get_soc_subtype(&at91_soc_initdata)); at91_get_soc_subtype(&at91_soc_initdata));
} }
...@@ -67,7 +68,7 @@ void __init at91_ioremap_ramc(int id, u32 addr, u32 size) ...@@ -67,7 +68,7 @@ void __init at91_ioremap_ramc(int id, u32 addr, u32 size)
} }
at91_ramc_base[id] = ioremap(addr, size); at91_ramc_base[id] = ioremap(addr, size);
if (!at91_ramc_base[id]) if (!at91_ramc_base[id])
panic("Impossible to ioremap ramc.%d 0x%x\n", id, addr); panic(pr_fmt("Impossible to ioremap ramc.%d 0x%x\n"), id, addr);
} }
static struct map_desc sram_desc[2] __initdata; static struct map_desc sram_desc[2] __initdata;
...@@ -84,7 +85,7 @@ void __init at91_init_sram(int bank, unsigned long base, unsigned int length) ...@@ -84,7 +85,7 @@ void __init at91_init_sram(int bank, unsigned long base, unsigned int length)
desc->length = length; desc->length = length;
desc->type = MT_MEMORY_RWX_NONCACHED; desc->type = MT_MEMORY_RWX_NONCACHED;
pr_info("AT91: sram at 0x%lx of 0x%x mapped at 0x%lx\n", pr_info("sram at 0x%lx of 0x%x mapped at 0x%lx\n",
base, length, desc->virtual); base, length, desc->virtual);
iotable_init(desc, 1); iotable_init(desc, 1);
...@@ -367,45 +368,21 @@ void __init at91_map_io(void) ...@@ -367,45 +368,21 @@ void __init at91_map_io(void)
soc_detect(AT91_BASE_DBGU1); soc_detect(AT91_BASE_DBGU1);
if (!at91_soc_is_detected()) if (!at91_soc_is_detected())
panic("AT91: Impossible to detect the SOC type"); panic(pr_fmt("Impossible to detect the SOC type"));
pr_info("AT91: Detected soc type: %s\n", pr_info("Detected soc type: %s\n",
at91_get_soc_type(&at91_soc_initdata)); at91_get_soc_type(&at91_soc_initdata));
if (at91_soc_initdata.subtype != AT91_SOC_SUBTYPE_NONE) if (at91_soc_initdata.subtype != AT91_SOC_SUBTYPE_NONE)
pr_info("AT91: Detected soc subtype: %s\n", pr_info("Detected soc subtype: %s\n",
at91_get_soc_subtype(&at91_soc_initdata)); at91_get_soc_subtype(&at91_soc_initdata));
if (!at91_soc_is_enabled()) if (!at91_soc_is_enabled())
panic("AT91: Soc not enabled"); panic(pr_fmt("Soc not enabled"));
if (at91_boot_soc.map_io) if (at91_boot_soc.map_io)
at91_boot_soc.map_io(); at91_boot_soc.map_io();
} }
void __iomem *at91_shdwc_base = NULL;
static void at91sam9_poweroff(void)
{
at91_shdwc_write(AT91_SHDW_CR, AT91_SHDW_KEY | AT91_SHDW_SHDW);
}
void __init at91_ioremap_shdwc(u32 base_addr)
{
at91_shdwc_base = ioremap(base_addr, 16);
if (!at91_shdwc_base)
panic("Impossible to ioremap at91_shdwc_base\n");
pm_power_off = at91sam9_poweroff;
}
void __iomem *at91_rstc_base;
void __init at91_ioremap_rstc(u32 base_addr)
{
at91_rstc_base = ioremap(base_addr, 16);
if (!at91_rstc_base)
panic("Impossible to ioremap at91_rstc_base\n");
}
void __init at91_alt_map_io(void) void __init at91_alt_map_io(void)
{ {
/* Map peripherals */ /* Map peripherals */
...@@ -438,42 +415,15 @@ void __init at91_ioremap_matrix(u32 base_addr) ...@@ -438,42 +415,15 @@ void __init at91_ioremap_matrix(u32 base_addr)
{ {
at91_matrix_base = ioremap(base_addr, 512); at91_matrix_base = ioremap(base_addr, 512);
if (!at91_matrix_base) if (!at91_matrix_base)
panic("Impossible to ioremap at91_matrix_base\n"); panic(pr_fmt("Impossible to ioremap at91_matrix_base\n"));
} }
#if defined(CONFIG_OF) && !defined(CONFIG_ARCH_AT91X40) #if defined(CONFIG_OF) && !defined(CONFIG_ARCH_AT91X40)
static struct of_device_id rstc_ids[] = {
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9_alt_restart },
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
{ /*sentinel*/ }
};
static void at91_dt_rstc(void)
{
struct device_node *np;
const struct of_device_id *of_id;
np = of_find_matching_node(NULL, rstc_ids);
if (!np)
panic("unable to find compatible rstc node in dtb\n");
at91_rstc_base = of_iomap(np, 0);
if (!at91_rstc_base)
panic("unable to map rstc cpu registers\n");
of_id = of_match_node(rstc_ids, np);
if (!of_id)
panic("AT91: rtsc no restart function available\n");
arm_pm_restart = of_id->data;
of_node_put(np);
}
static struct of_device_id ramc_ids[] = { static struct of_device_id ramc_ids[] = {
{ .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby }, { .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby },
{ .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby }, { .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby },
{ .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby }, { .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby },
{ .compatible = "atmel,sama5d3-ddramc", .data = at91_ddr_standby },
{ /*sentinel*/ } { /*sentinel*/ }
}; };
...@@ -481,100 +431,29 @@ static void at91_dt_ramc(void) ...@@ -481,100 +431,29 @@ static void at91_dt_ramc(void)
{ {
struct device_node *np; struct device_node *np;
const struct of_device_id *of_id; const struct of_device_id *of_id;
int idx = 0;
const void *standby = NULL;
np = of_find_matching_node(NULL, ramc_ids); for_each_matching_node_and_match(np, ramc_ids, &of_id) {
if (!np) at91_ramc_base[idx] = of_iomap(np, 0);
panic("unable to find compatible ram controller node in dtb\n"); if (!at91_ramc_base[idx])
panic(pr_fmt("unable to map ramc[%d] cpu registers\n"), idx);
at91_ramc_base[0] = of_iomap(np, 0);
if (!at91_ramc_base[0])
panic("unable to map ramc[0] cpu registers\n");
/* the controller may have 2 banks */
at91_ramc_base[1] = of_iomap(np, 1);
of_id = of_match_node(ramc_ids, np);
if (!of_id)
pr_warn("AT91: ramc no standby function available\n");
else
at91_pm_set_standby(of_id->data);
of_node_put(np);
}
static struct of_device_id shdwc_ids[] = {
{ .compatible = "atmel,at91sam9260-shdwc", },
{ .compatible = "atmel,at91sam9rl-shdwc", },
{ .compatible = "atmel,at91sam9x5-shdwc", },
{ /*sentinel*/ }
};
static const char *shdwc_wakeup_modes[] = {
[AT91_SHDW_WKMODE0_NONE] = "none",
[AT91_SHDW_WKMODE0_HIGH] = "high",
[AT91_SHDW_WKMODE0_LOW] = "low",
[AT91_SHDW_WKMODE0_ANYLEVEL] = "any",
};
const int at91_dtget_shdwc_wakeup_mode(struct device_node *np) if (!standby)
{ standby = of_id->data;
const char *pm;
int err, i;
err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
if (err < 0)
return AT91_SHDW_WKMODE0_ANYLEVEL;
for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++)
if (!strcasecmp(pm, shdwc_wakeup_modes[i]))
return i;
return -ENODEV;
}
static void at91_dt_shdwc(void)
{
struct device_node *np;
int wakeup_mode;
u32 reg;
u32 mode = 0;
np = of_find_matching_node(NULL, shdwc_ids); idx++;
if (!np) {
pr_debug("AT91: unable to find compatible shutdown (shdwc) controller node in dtb\n");
return;
} }
at91_shdwc_base = of_iomap(np, 0); if (!idx)
if (!at91_shdwc_base) panic(pr_fmt("unable to find compatible ram controller node in dtb\n"));
panic("AT91: unable to map shdwc cpu registers\n");
wakeup_mode = at91_dtget_shdwc_wakeup_mode(np);
if (wakeup_mode < 0) {
pr_warn("AT91: shdwc unknown wakeup mode\n");
goto end;
}
if (!of_property_read_u32(np, "atmel,wakeup-counter", &reg)) { if (!standby) {
if (reg > AT91_SHDW_CPTWK0_MAX) { pr_warn("ramc no standby function available\n");
pr_warn("AT91: shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n", return;
reg, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX);
reg = AT91_SHDW_CPTWK0_MAX;
}
mode |= AT91_SHDW_CPTWK0_(reg);
} }
if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) at91_pm_set_standby(standby);
mode |= AT91_SHDW_RTCWKEN;
if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
mode |= AT91_SHDW_RTTWKEN;
at91_shdwc_write(AT91_SHDW_MR, wakeup_mode | mode);
end:
pm_power_off = at91sam9_poweroff;
of_node_put(np);
} }
void __init at91rm9200_dt_initialize(void) void __init at91rm9200_dt_initialize(void)
...@@ -593,9 +472,7 @@ void __init at91rm9200_dt_initialize(void) ...@@ -593,9 +472,7 @@ void __init at91rm9200_dt_initialize(void)
void __init at91_dt_initialize(void) void __init at91_dt_initialize(void)
{ {
at91_dt_rstc();
at91_dt_ramc(); at91_dt_ramc();
at91_dt_shdwc();
/* Init clock subsystem */ /* Init clock subsystem */
at91_dt_clock_init(); at91_dt_clock_init();
...@@ -623,3 +500,13 @@ void __init at91_initialize(unsigned long main_clock) ...@@ -623,3 +500,13 @@ void __init at91_initialize(unsigned long main_clock)
pinctrl_provide_dummies(); pinctrl_provide_dummies();
} }
void __init at91_register_devices(void)
{
at91_boot_soc.register_devices();
}
void __init at91_init_time(void)
{
at91_boot_soc.init_time();
}
...@@ -11,7 +11,9 @@ struct at91_init_soc { ...@@ -11,7 +11,9 @@ struct at91_init_soc {
void (*map_io)(void); void (*map_io)(void);
void (*ioremap_registers)(void); void (*ioremap_registers)(void);
void (*register_clocks)(void); void (*register_clocks)(void);
void (*register_devices)(void);
void (*init)(void); void (*init)(void);
void (*init_time)(void);
}; };
extern struct at91_init_soc at91_boot_soc; extern struct at91_init_soc at91_boot_soc;
......
...@@ -83,6 +83,7 @@ config ARCH_OMAP2PLUS ...@@ -83,6 +83,7 @@ config ARCH_OMAP2PLUS
select PINCTRL select PINCTRL
select SOC_BUS select SOC_BUS
select TI_PRIV_EDMA select TI_PRIV_EDMA
select OMAP_IRQCHIP
help help
Systems based on OMAP2, OMAP3, OMAP4 or OMAP5 Systems based on OMAP2, OMAP3, OMAP4 or OMAP5
......
...@@ -10,7 +10,6 @@ obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o gpmc.o timer.o pm.o \ ...@@ -10,7 +10,6 @@ obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o gpmc.o timer.o pm.o \
common.o gpio.o dma.o wd_timer.o display.o i2c.o hdq1w.o omap_hwmod.o \ common.o gpio.o dma.o wd_timer.o display.o i2c.o hdq1w.o omap_hwmod.o \
omap_device.o sram.o drm.o omap_device.o sram.o drm.o
omap-2-3-common = irq.o
hwmod-common = omap_hwmod.o omap_hwmod_reset.o \ hwmod-common = omap_hwmod.o omap_hwmod_reset.o \
omap_hwmod_common_data.o omap_hwmod_common_data.o
clock-common = clock.o clock_common_data.o \ clock-common = clock.o clock_common_data.o \
...@@ -20,7 +19,7 @@ secure-common = omap-smc.o omap-secure.o ...@@ -20,7 +19,7 @@ secure-common = omap-smc.o omap-secure.o
obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common) obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common)
obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(hwmod-common) $(secure-common) obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(hwmod-common) $(secure-common)
obj-$(CONFIG_ARCH_OMAP4) += $(hwmod-common) $(secure-common) obj-$(CONFIG_ARCH_OMAP4) += $(hwmod-common) $(secure-common)
obj-$(CONFIG_SOC_AM33XX) += irq.o $(hwmod-common) obj-$(CONFIG_SOC_AM33XX) += $(hwmod-common)
obj-$(CONFIG_SOC_OMAP5) += $(hwmod-common) $(secure-common) obj-$(CONFIG_SOC_OMAP5) += $(hwmod-common) $(secure-common)
obj-$(CONFIG_SOC_AM43XX) += $(hwmod-common) $(secure-common) obj-$(CONFIG_SOC_AM43XX) += $(hwmod-common) $(secure-common)
obj-$(CONFIG_SOC_DRA7XX) += $(hwmod-common) $(secure-common) obj-$(CONFIG_SOC_DRA7XX) += $(hwmod-common) $(secure-common)
......
...@@ -625,7 +625,6 @@ MACHINE_START(OMAP_3430SDP, "OMAP3430 3430SDP board") ...@@ -625,7 +625,6 @@ MACHINE_START(OMAP_3430SDP, "OMAP3430 3430SDP board")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3430_init_early, .init_early = omap3430_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap_3430sdp_init, .init_machine = omap_3430sdp_init,
.init_late = omap3430_init_late, .init_late = omap3430_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
...@@ -142,7 +142,6 @@ MACHINE_START(CRANEBOARD, "AM3517/05 CRANEBOARD") ...@@ -142,7 +142,6 @@ MACHINE_START(CRANEBOARD, "AM3517/05 CRANEBOARD")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = am35xx_init_early, .init_early = am35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = am3517_crane_init, .init_machine = am3517_crane_init,
.init_late = am35xx_init_late, .init_late = am35xx_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
...@@ -366,7 +366,6 @@ MACHINE_START(OMAP3517EVM, "OMAP3517/AM3517 EVM") ...@@ -366,7 +366,6 @@ MACHINE_START(OMAP3517EVM, "OMAP3517/AM3517 EVM")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = am35xx_init_early, .init_early = am35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = am3517_evm_init, .init_machine = am3517_evm_init,
.init_late = am35xx_init_late, .init_late = am35xx_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
...@@ -766,7 +766,6 @@ MACHINE_START(CM_T35, "Compulab CM-T35") ...@@ -766,7 +766,6 @@ MACHINE_START(CM_T35, "Compulab CM-T35")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap35xx_init_early, .init_early = omap35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = cm_t35_init, .init_machine = cm_t35_init,
.init_late = omap35xx_init_late, .init_late = omap35xx_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
...@@ -779,7 +778,6 @@ MACHINE_START(CM_T3730, "Compulab CM-T3730") ...@@ -779,7 +778,6 @@ MACHINE_START(CM_T3730, "Compulab CM-T3730")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3630_init_early, .init_early = omap3630_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = cm_t3730_init, .init_machine = cm_t3730_init,
.init_late = omap3630_init_late, .init_late = omap3630_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
...@@ -329,7 +329,6 @@ MACHINE_START(CM_T3517, "Compulab CM-T3517") ...@@ -329,7 +329,6 @@ MACHINE_START(CM_T3517, "Compulab CM-T3517")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = am35xx_init_early, .init_early = am35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = cm_t3517_init, .init_machine = cm_t3517_init,
.init_late = am35xx_init_late, .init_late = am35xx_init_late,
.init_time = omap3_gptimer_timer_init, .init_time = omap3_gptimer_timer_init,
......
...@@ -647,7 +647,6 @@ MACHINE_START(DEVKIT8000, "OMAP3 Devkit8000") ...@@ -647,7 +647,6 @@ MACHINE_START(DEVKIT8000, "OMAP3 Devkit8000")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap35xx_init_early, .init_early = omap35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = devkit8000_init, .init_machine = devkit8000_init,
.init_late = omap35xx_init_late, .init_late = omap35xx_init_late,
.init_time = omap3_secure_sync32k_timer_init, .init_time = omap3_secure_sync32k_timer_init,
......
...@@ -52,8 +52,6 @@ DT_MACHINE_START(OMAP242X_DT, "Generic OMAP2420 (Flattened Device Tree)") ...@@ -52,8 +52,6 @@ DT_MACHINE_START(OMAP242X_DT, "Generic OMAP2420 (Flattened Device Tree)")
.reserve = omap_reserve, .reserve = omap_reserve,
.map_io = omap242x_map_io, .map_io = omap242x_map_io,
.init_early = omap2420_init_early, .init_early = omap2420_init_early,
.init_irq = omap_intc_of_init,
.handle_irq = omap2_intc_handle_irq,
.init_machine = omap_generic_init, .init_machine = omap_generic_init,
.init_time = omap2_sync32k_timer_init, .init_time = omap2_sync32k_timer_init,
.dt_compat = omap242x_boards_compat, .dt_compat = omap242x_boards_compat,
...@@ -71,8 +69,6 @@ DT_MACHINE_START(OMAP243X_DT, "Generic OMAP2430 (Flattened Device Tree)") ...@@ -71,8 +69,6 @@ DT_MACHINE_START(OMAP243X_DT, "Generic OMAP2430 (Flattened Device Tree)")
.reserve = omap_reserve, .reserve = omap_reserve,
.map_io = omap243x_map_io, .map_io = omap243x_map_io,
.init_early = omap2430_init_early, .init_early = omap2430_init_early,
.init_irq = omap_intc_of_init,
.handle_irq = omap2_intc_handle_irq,
.init_machine = omap_generic_init, .init_machine = omap_generic_init,
.init_time = omap2_sync32k_timer_init, .init_time = omap2_sync32k_timer_init,
.dt_compat = omap243x_boards_compat, .dt_compat = omap243x_boards_compat,
...@@ -91,8 +87,6 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)") ...@@ -91,8 +87,6 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)")
.reserve = omap_reserve, .reserve = omap_reserve,
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3430_init_early, .init_early = omap3430_init_early,
.init_irq = omap_intc_of_init,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap_generic_init, .init_machine = omap_generic_init,
.init_late = omap3_init_late, .init_late = omap3_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
...@@ -109,8 +103,6 @@ DT_MACHINE_START(OMAP36XX_DT, "Generic OMAP36xx (Flattened Device Tree)") ...@@ -109,8 +103,6 @@ DT_MACHINE_START(OMAP36XX_DT, "Generic OMAP36xx (Flattened Device Tree)")
.reserve = omap_reserve, .reserve = omap_reserve,
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3630_init_early, .init_early = omap3630_init_early,
.init_irq = omap_intc_of_init,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap_generic_init, .init_machine = omap_generic_init,
.init_late = omap3_init_late, .init_late = omap3_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
...@@ -128,8 +120,6 @@ DT_MACHINE_START(OMAP3_GP_DT, "Generic OMAP3-GP (Flattened Device Tree)") ...@@ -128,8 +120,6 @@ DT_MACHINE_START(OMAP3_GP_DT, "Generic OMAP3-GP (Flattened Device Tree)")
.reserve = omap_reserve, .reserve = omap_reserve,
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3430_init_early, .init_early = omap3430_init_early,
.init_irq = omap_intc_of_init,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap_generic_init, .init_machine = omap_generic_init,
.init_late = omap3_init_late, .init_late = omap3_init_late,
.init_time = omap3_secure_sync32k_timer_init, .init_time = omap3_secure_sync32k_timer_init,
...@@ -146,8 +136,6 @@ DT_MACHINE_START(AM3517_DT, "Generic AM3517 (Flattened Device Tree)") ...@@ -146,8 +136,6 @@ DT_MACHINE_START(AM3517_DT, "Generic AM3517 (Flattened Device Tree)")
.reserve = omap_reserve, .reserve = omap_reserve,
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = am35xx_init_early, .init_early = am35xx_init_early,
.init_irq = omap_intc_of_init,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap_generic_init, .init_machine = omap_generic_init,
.init_late = omap3_init_late, .init_late = omap3_init_late,
.init_time = omap3_gptimer_timer_init, .init_time = omap3_gptimer_timer_init,
...@@ -166,8 +154,6 @@ DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)") ...@@ -166,8 +154,6 @@ DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
.reserve = omap_reserve, .reserve = omap_reserve,
.map_io = am33xx_map_io, .map_io = am33xx_map_io,
.init_early = am33xx_init_early, .init_early = am33xx_init_early,
.init_irq = omap_intc_of_init,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap_generic_init, .init_machine = omap_generic_init,
.init_late = am33xx_init_late, .init_late = am33xx_init_late,
.init_time = omap3_gptimer_timer_init, .init_time = omap3_gptimer_timer_init,
......
...@@ -422,7 +422,6 @@ MACHINE_START(OMAP_LDP, "OMAP LDP board") ...@@ -422,7 +422,6 @@ MACHINE_START(OMAP_LDP, "OMAP LDP board")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3430_init_early, .init_early = omap3430_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap_ldp_init, .init_machine = omap_ldp_init,
.init_late = omap3430_init_late, .init_late = omap3430_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "mmc.h" #include "mmc.h"
#include "soc.h" #include "soc.h"
#include "gpmc-onenand.h" #include "gpmc-onenand.h"
#include "common-board-devices.h"
#define TUSB6010_ASYNC_CS 1 #define TUSB6010_ASYNC_CS 1
#define TUSB6010_SYNC_CS 4 #define TUSB6010_SYNC_CS 4
...@@ -568,29 +569,14 @@ static int n8x0_menelaus_late_init(struct device *dev) ...@@ -568,29 +569,14 @@ static int n8x0_menelaus_late_init(struct device *dev)
} }
#endif #endif
static struct menelaus_platform_data n8x0_menelaus_platform_data __initdata = { struct menelaus_platform_data n8x0_menelaus_platform_data __initdata = {
.late_init = n8x0_menelaus_late_init, .late_init = n8x0_menelaus_late_init,
}; };
static struct i2c_board_info __initdata n8x0_i2c_board_info_1[] __initdata = { struct aic3x_pdata n810_aic33_data __initdata = {
{
I2C_BOARD_INFO("menelaus", 0x72),
.irq = 7 + OMAP_INTC_START,
.platform_data = &n8x0_menelaus_platform_data,
},
};
static struct aic3x_pdata n810_aic33_data __initdata = {
.gpio_reset = 118, .gpio_reset = 118,
}; };
static struct i2c_board_info n810_i2c_board_info_2[] __initdata = {
{
I2C_BOARD_INFO("tlv320aic3x", 0x18),
.platform_data = &n810_aic33_data,
},
};
static int __init n8x0_late_initcall(void) static int __init n8x0_late_initcall(void)
{ {
if (!board_caps) if (!board_caps)
...@@ -612,11 +598,5 @@ void * __init n8x0_legacy_init(void) ...@@ -612,11 +598,5 @@ void * __init n8x0_legacy_init(void)
board_check_revision(); board_check_revision();
spi_register_board_info(n800_spi_board_info, spi_register_board_info(n800_spi_board_info,
ARRAY_SIZE(n800_spi_board_info)); ARRAY_SIZE(n800_spi_board_info));
i2c_register_board_info(0, n8x0_i2c_board_info_1,
ARRAY_SIZE(n8x0_i2c_board_info_1));
if (board_is_n810())
i2c_register_board_info(1, n810_i2c_board_info_2,
ARRAY_SIZE(n810_i2c_board_info_2));
return &mmc1_data; return &mmc1_data;
} }
...@@ -588,7 +588,6 @@ MACHINE_START(OMAP3_BEAGLE, "OMAP3 Beagle Board") ...@@ -588,7 +588,6 @@ MACHINE_START(OMAP3_BEAGLE, "OMAP3 Beagle Board")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3_init_early, .init_early = omap3_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap3_beagle_init, .init_machine = omap3_beagle_init,
.init_late = omap3_init_late, .init_late = omap3_init_late,
.init_time = omap3_secure_sync32k_timer_init, .init_time = omap3_secure_sync32k_timer_init,
......
...@@ -230,7 +230,6 @@ MACHINE_START(OMAP3_TORPEDO, "Logic OMAP3 Torpedo board") ...@@ -230,7 +230,6 @@ MACHINE_START(OMAP3_TORPEDO, "Logic OMAP3 Torpedo board")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap35xx_init_early, .init_early = omap35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap3logic_init, .init_machine = omap3logic_init,
.init_late = omap35xx_init_late, .init_late = omap35xx_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
...@@ -243,7 +242,6 @@ MACHINE_START(OMAP3530_LV_SOM, "OMAP Logic 3530 LV SOM board") ...@@ -243,7 +242,6 @@ MACHINE_START(OMAP3530_LV_SOM, "OMAP Logic 3530 LV SOM board")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap35xx_init_early, .init_early = omap35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap3logic_init, .init_machine = omap3logic_init,
.init_late = omap35xx_init_late, .init_late = omap35xx_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
...@@ -624,7 +624,6 @@ MACHINE_START(OMAP3_PANDORA, "Pandora Handheld Console") ...@@ -624,7 +624,6 @@ MACHINE_START(OMAP3_PANDORA, "Pandora Handheld Console")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap35xx_init_early, .init_early = omap35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap3pandora_init, .init_machine = omap3pandora_init,
.init_late = omap35xx_init_late, .init_late = omap35xx_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
...@@ -426,7 +426,6 @@ MACHINE_START(SBC3530, "OMAP3 STALKER") ...@@ -426,7 +426,6 @@ MACHINE_START(SBC3530, "OMAP3 STALKER")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap35xx_init_early, .init_early = omap35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap3_stalker_init, .init_machine = omap3_stalker_init,
.init_late = omap35xx_init_late, .init_late = omap35xx_init_late,
.init_time = omap3_secure_sync32k_timer_init, .init_time = omap3_secure_sync32k_timer_init,
......
...@@ -388,7 +388,6 @@ MACHINE_START(TOUCHBOOK, "OMAP3 touchbook Board") ...@@ -388,7 +388,6 @@ MACHINE_START(TOUCHBOOK, "OMAP3 touchbook Board")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3430_init_early, .init_early = omap3430_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = omap3_touchbook_init, .init_machine = omap3_touchbook_init,
.init_late = omap3430_init_late, .init_late = omap3430_init_late,
.init_time = omap3_secure_sync32k_timer_init, .init_time = omap3_secure_sync32k_timer_init,
......
...@@ -564,7 +564,6 @@ MACHINE_START(OVERO, "Gumstix Overo") ...@@ -564,7 +564,6 @@ MACHINE_START(OVERO, "Gumstix Overo")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap35xx_init_early, .init_early = omap35xx_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = overo_init, .init_machine = overo_init,
.init_late = omap35xx_init_late, .init_late = omap35xx_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
...@@ -134,7 +134,6 @@ MACHINE_START(NOKIA_RX51, "Nokia RX-51 board") ...@@ -134,7 +134,6 @@ MACHINE_START(NOKIA_RX51, "Nokia RX-51 board")
.map_io = omap3_map_io, .map_io = omap3_map_io,
.init_early = omap3430_init_early, .init_early = omap3430_init_early,
.init_irq = omap3_init_irq, .init_irq = omap3_init_irq,
.handle_irq = omap3_intc_handle_irq,
.init_machine = rx51_init, .init_machine = rx51_init,
.init_late = omap3430_init_late, .init_late = omap3430_init_late,
.init_time = omap3_sync32k_timer_init, .init_time = omap3_sync32k_timer_init,
......
#ifndef __OMAP_COMMON_BOARD_DEVICES__ #ifndef __OMAP_COMMON_BOARD_DEVICES__
#define __OMAP_COMMON_BOARD_DEVICES__ #define __OMAP_COMMON_BOARD_DEVICES__
#include <sound/tlv320aic3x.h>
#include <linux/mfd/menelaus.h>
#include "twl-common.h" #include "twl-common.h"
#define NAND_BLOCK_SIZE SZ_128K #define NAND_BLOCK_SIZE SZ_128K
...@@ -12,4 +14,7 @@ void omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce, ...@@ -12,4 +14,7 @@ void omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce,
struct ads7846_platform_data *board_pdata); struct ads7846_platform_data *board_pdata);
void *n8x0_legacy_init(void); void *n8x0_legacy_init(void);
extern struct menelaus_platform_data n8x0_menelaus_platform_data;
extern struct aic3x_pdata n810_aic33_data;
#endif /* __OMAP_COMMON_BOARD_DEVICES__ */ #endif /* __OMAP_COMMON_BOARD_DEVICES__ */
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/i2c/twl.h> #include <linux/i2c/twl.h>
#include <linux/i2c-omap.h> #include <linux/i2c-omap.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/irqchip/irq-omap-intc.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
...@@ -210,18 +211,6 @@ extern struct device *omap2_get_iva_device(void); ...@@ -210,18 +211,6 @@ extern struct device *omap2_get_iva_device(void);
extern struct device *omap2_get_l3_device(void); extern struct device *omap2_get_l3_device(void);
extern struct device *omap4_get_dsp_device(void); extern struct device *omap4_get_dsp_device(void);
void omap2_init_irq(void);
void omap3_init_irq(void);
void ti81xx_init_irq(void);
extern int omap_irq_pending(void);
void omap_intc_save_context(void);
void omap_intc_restore_context(void);
void omap3_intc_suspend(void);
void omap3_intc_prepare_idle(void);
void omap3_intc_resume_idle(void);
void omap2_intc_handle_irq(struct pt_regs *regs);
void omap3_intc_handle_irq(struct pt_regs *regs);
void omap_intc_of_init(void);
void omap_gic_of_init(void); void omap_gic_of_init(void);
#ifdef CONFIG_CACHE_L2X0 #ifdef CONFIG_CACHE_L2X0
...@@ -229,16 +218,6 @@ extern void __iomem *omap4_get_l2cache_base(void); ...@@ -229,16 +218,6 @@ extern void __iomem *omap4_get_l2cache_base(void);
#endif #endif
struct device_node; struct device_node;
#ifdef CONFIG_OF
int __init intc_of_init(struct device_node *node,
struct device_node *parent);
#else
int __init intc_of_init(struct device_node *node,
struct device_node *parent)
{
return 0;
}
#endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
extern void __iomem *omap4_get_scu_base(void); extern void __iomem *omap4_get_scu_base(void);
......
...@@ -344,6 +344,8 @@ static struct pdata_init auxdata_quirks[] __initdata = { ...@@ -344,6 +344,8 @@ static struct pdata_init auxdata_quirks[] __initdata = {
struct of_dev_auxdata omap_auxdata_lookup[] __initdata = { struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
#ifdef CONFIG_MACH_NOKIA_N8X0 #ifdef CONFIG_MACH_NOKIA_N8X0
OF_DEV_AUXDATA("ti,omap2420-mmc", 0x4809c000, "mmci-omap.0", NULL), OF_DEV_AUXDATA("ti,omap2420-mmc", 0x4809c000, "mmci-omap.0", NULL),
OF_DEV_AUXDATA("menelaus", 0x72, "1-0072", &n8x0_menelaus_platform_data),
OF_DEV_AUXDATA("tlv320aic3x", 0x18, "2-0018", &n810_aic33_data),
#endif #endif
#ifdef CONFIG_ARCH_OMAP3 #ifdef CONFIG_ARCH_OMAP3
OF_DEV_AUXDATA("ti,omap3-padconf", 0x48002030, "48002030.pinmux", &pcs_pdata), OF_DEV_AUXDATA("ti,omap3-padconf", 0x48002030, "48002030.pinmux", &pcs_pdata),
......
...@@ -148,6 +148,8 @@ source "drivers/remoteproc/Kconfig" ...@@ -148,6 +148,8 @@ source "drivers/remoteproc/Kconfig"
source "drivers/rpmsg/Kconfig" source "drivers/rpmsg/Kconfig"
source "drivers/soc/Kconfig"
source "drivers/devfreq/Kconfig" source "drivers/devfreq/Kconfig"
source "drivers/extcon/Kconfig" source "drivers/extcon/Kconfig"
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
#define CCN_DT_PMCCNTRSR 0x0190 #define CCN_DT_PMCCNTRSR 0x0190
#define CCN_DT_PMOVSR 0x0198 #define CCN_DT_PMOVSR 0x0198
#define CCN_DT_PMOVSR_CLR 0x01a0 #define CCN_DT_PMOVSR_CLR 0x01a0
#define CCN_DT_PMOVSR_CLR__MASK 0x1f
#define CCN_DT_PMCR 0x01a8 #define CCN_DT_PMCR 0x01a8
#define CCN_DT_PMCR__OVFL_INTR_EN (1 << 6) #define CCN_DT_PMCR__OVFL_INTR_EN (1 << 6)
#define CCN_DT_PMCR__PMU_EN (1 << 0) #define CCN_DT_PMCR__PMU_EN (1 << 0)
...@@ -1051,7 +1052,8 @@ static irqreturn_t arm_ccn_pmu_overflow_handler(struct arm_ccn_dt *dt) ...@@ -1051,7 +1052,8 @@ static irqreturn_t arm_ccn_pmu_overflow_handler(struct arm_ccn_dt *dt)
struct perf_event *event = dt->pmu_counters[idx].event; struct perf_event *event = dt->pmu_counters[idx].event;
int overflowed = pmovsr & BIT(idx); int overflowed = pmovsr & BIT(idx);
WARN_ON_ONCE(overflowed && !event); WARN_ON_ONCE(overflowed && !event &&
idx != CCN_IDX_PMU_CYCLE_COUNTER);
if (!event || !overflowed) if (!event || !overflowed)
continue; continue;
...@@ -1087,6 +1089,7 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn) ...@@ -1087,6 +1089,7 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn)
/* Initialize DT subsystem */ /* Initialize DT subsystem */
ccn->dt.base = ccn->base + CCN_REGION_SIZE; ccn->dt.base = ccn->base + CCN_REGION_SIZE;
spin_lock_init(&ccn->dt.config_lock); spin_lock_init(&ccn->dt.config_lock);
writel(CCN_DT_PMOVSR_CLR__MASK, ccn->dt.base + CCN_DT_PMOVSR_CLR);
writel(CCN_DT_CTL__DT_EN, ccn->dt.base + CCN_DT_CTL); writel(CCN_DT_CTL__DT_EN, ccn->dt.base + CCN_DT_CTL);
writel(CCN_DT_PMCR__OVFL_INTR_EN | CCN_DT_PMCR__PMU_EN, writel(CCN_DT_PMCR__OVFL_INTR_EN | CCN_DT_PMCR__PMU_EN,
ccn->dt.base + CCN_DT_PMCR); ccn->dt.base + CCN_DT_PMCR);
......
...@@ -119,13 +119,7 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name, ...@@ -119,13 +119,7 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name,
init.ops = &system_ops; init.ops = &system_ops;
init.parent_names = &parent_name; init.parent_names = &parent_name;
init.num_parents = 1; init.num_parents = 1;
/* init.flags = CLK_SET_RATE_PARENT;
* CLK_IGNORE_UNUSED is used to avoid ddrck switch off.
* TODO : we should implement a driver supporting at91 ddr controller
* (see drivers/memory) which would request and enable the ddrck clock.
* When this is done we will be able to remove CLK_IGNORE_UNUSED flag.
*/
init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED;
sys->id = id; sys->id = id;
sys->hw.init = &init; sys->hw.init = &init;
......
...@@ -120,6 +120,10 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK ...@@ -120,6 +120,10 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
help help
Use ARM global timer clock source as sched_clock Use ARM global timer clock source as sched_clock
config ATMEL_PIT
select CLKSRC_OF if OF
def_bool SOC_AT91SAM9 || SOC_SAMA5
config CLKSRC_METAG_GENERIC config CLKSRC_METAG_GENERIC
def_bool y if METAG def_bool y if METAG
help help
......
obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o
obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o
obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
......
...@@ -9,47 +9,64 @@ ...@@ -9,47 +9,64 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#define pr_fmt(fmt) "AT91: PIT: " fmt
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/slab.h>
#include <asm/mach/time.h>
#include <mach/hardware.h>
#define AT91_PIT_MR 0x00 /* Mode Register */ #define AT91_PIT_MR 0x00 /* Mode Register */
#define AT91_PIT_PITIEN (1 << 25) /* Timer Interrupt Enable */ #define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */
#define AT91_PIT_PITEN (1 << 24) /* Timer Enabled */ #define AT91_PIT_PITEN BIT(24) /* Timer Enabled */
#define AT91_PIT_PIV (0xfffff) /* Periodic Interval Value */ #define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */
#define AT91_PIT_SR 0x04 /* Status Register */ #define AT91_PIT_SR 0x04 /* Status Register */
#define AT91_PIT_PITS (1 << 0) /* Timer Status */ #define AT91_PIT_PITS BIT(0) /* Timer Status */
#define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */ #define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */
#define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */ #define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */
#define AT91_PIT_PICNT (0xfff << 20) /* Interval Counter */ #define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */
#define AT91_PIT_CPIV (0xfffff) /* Inverval Value */ #define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */
#define PIT_CPIV(x) ((x) & AT91_PIT_CPIV) #define PIT_CPIV(x) ((x) & AT91_PIT_CPIV)
#define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20) #define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20)
static u32 pit_cycle; /* write-once */ struct pit_data {
static u32 pit_cnt; /* access only w/system irq blocked */ struct clock_event_device clkevt;
static void __iomem *pit_base_addr __read_mostly; struct clocksource clksrc;
static struct clk *mck;
void __iomem *base;
u32 cycle;
u32 cnt;
unsigned int irq;
struct clk *mck;
};
static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc)
{
return container_of(clksrc, struct pit_data, clksrc);
}
static inline unsigned int pit_read(unsigned int reg_offset) static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt)
{ {
return __raw_readl(pit_base_addr + reg_offset); return container_of(clkevt, struct pit_data, clkevt);
} }
static inline void pit_write(unsigned int reg_offset, unsigned long value) static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset)
{ {
__raw_writel(value, pit_base_addr + reg_offset); return __raw_readl(base + reg_offset);
}
static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value)
{
__raw_writel(value, base + reg_offset);
} }
/* /*
...@@ -58,40 +75,35 @@ static inline void pit_write(unsigned int reg_offset, unsigned long value) ...@@ -58,40 +75,35 @@ static inline void pit_write(unsigned int reg_offset, unsigned long value)
*/ */
static cycle_t read_pit_clk(struct clocksource *cs) static cycle_t read_pit_clk(struct clocksource *cs)
{ {
struct pit_data *data = clksrc_to_pit_data(cs);
unsigned long flags; unsigned long flags;
u32 elapsed; u32 elapsed;
u32 t; u32 t;
raw_local_irq_save(flags); raw_local_irq_save(flags);
elapsed = pit_cnt; elapsed = data->cnt;
t = pit_read(AT91_PIT_PIIR); t = pit_read(data->base, AT91_PIT_PIIR);
raw_local_irq_restore(flags); raw_local_irq_restore(flags);
elapsed += PIT_PICNT(t) * pit_cycle; elapsed += PIT_PICNT(t) * data->cycle;
elapsed += PIT_CPIV(t); elapsed += PIT_CPIV(t);
return elapsed; return elapsed;
} }
static struct clocksource pit_clk = {
.name = "pit",
.rating = 175,
.read = read_pit_clk,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
/* /*
* Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16) * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16)
*/ */
static void static void
pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
{ {
struct pit_data *data = clkevt_to_pit_data(dev);
switch (mode) { switch (mode) {
case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_PERIODIC:
/* update clocksource counter */ /* update clocksource counter */
pit_cnt += pit_cycle * PIT_PICNT(pit_read(AT91_PIT_PIVR)); data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN pit_write(data->base, AT91_PIT_MR,
| AT91_PIT_PITIEN); (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN);
break; break;
case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_ONESHOT:
BUG(); BUG();
...@@ -99,7 +111,8 @@ pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) ...@@ -99,7 +111,8 @@ pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_UNUSED:
/* disable irq, leaving the clocksource active */ /* disable irq, leaving the clocksource active */
pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN); pit_write(data->base, AT91_PIT_MR,
(data->cycle - 1) | AT91_PIT_PITEN);
break; break;
case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_RESUME:
break; break;
...@@ -108,44 +121,40 @@ pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) ...@@ -108,44 +121,40 @@ pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
static void at91sam926x_pit_suspend(struct clock_event_device *cedev) static void at91sam926x_pit_suspend(struct clock_event_device *cedev)
{ {
struct pit_data *data = clkevt_to_pit_data(cedev);
/* Disable timer */ /* Disable timer */
pit_write(AT91_PIT_MR, 0); pit_write(data->base, AT91_PIT_MR, 0);
} }
static void at91sam926x_pit_reset(void) static void at91sam926x_pit_reset(struct pit_data *data)
{ {
/* Disable timer and irqs */ /* Disable timer and irqs */
pit_write(AT91_PIT_MR, 0); pit_write(data->base, AT91_PIT_MR, 0);
/* Clear any pending interrupts, wait for PIT to stop counting */ /* Clear any pending interrupts, wait for PIT to stop counting */
while (PIT_CPIV(pit_read(AT91_PIT_PIVR)) != 0) while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0)
cpu_relax(); cpu_relax();
/* Start PIT but don't enable IRQ */ /* Start PIT but don't enable IRQ */
pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN); pit_write(data->base, AT91_PIT_MR,
(data->cycle - 1) | AT91_PIT_PITEN);
} }
static void at91sam926x_pit_resume(struct clock_event_device *cedev) static void at91sam926x_pit_resume(struct clock_event_device *cedev)
{ {
at91sam926x_pit_reset(); struct pit_data *data = clkevt_to_pit_data(cedev);
}
static struct clock_event_device pit_clkevt = {
.name = "pit",
.features = CLOCK_EVT_FEAT_PERIODIC,
.shift = 32,
.rating = 100,
.set_mode = pit_clkevt_mode,
.suspend = at91sam926x_pit_suspend,
.resume = at91sam926x_pit_resume,
};
at91sam926x_pit_reset(data);
}
/* /*
* IRQ handler for the timer. * IRQ handler for the timer.
*/ */
static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
{ {
struct pit_data *data = dev_id;
/* /*
* irqs should be disabled here, but as the irq is shared they are only * irqs should be disabled here, but as the irq is shared they are only
* guaranteed to be off if the timer irq is registered first. * guaranteed to be off if the timer irq is registered first.
...@@ -153,15 +162,15 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) ...@@ -153,15 +162,15 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
WARN_ON_ONCE(!irqs_disabled()); WARN_ON_ONCE(!irqs_disabled());
/* The PIT interrupt may be disabled, and is shared */ /* The PIT interrupt may be disabled, and is shared */
if ((pit_clkevt.mode == CLOCK_EVT_MODE_PERIODIC) if ((data->clkevt.mode == CLOCK_EVT_MODE_PERIODIC) &&
&& (pit_read(AT91_PIT_SR) & AT91_PIT_PITS)) { (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) {
unsigned nr_ticks; unsigned nr_ticks;
/* Get number of ticks performed before irq, and ack it */ /* Get number of ticks performed before irq, and ack it */
nr_ticks = PIT_PICNT(pit_read(AT91_PIT_PIVR)); nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
do { do {
pit_cnt += pit_cycle; data->cnt += data->cycle;
pit_clkevt.event_handler(&pit_clkevt); data->clkevt.event_handler(&data->clkevt);
nr_ticks--; nr_ticks--;
} while (nr_ticks); } while (nr_ticks);
...@@ -171,124 +180,117 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) ...@@ -171,124 +180,117 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
return IRQ_NONE; return IRQ_NONE;
} }
static struct irqaction at91sam926x_pit_irq = {
.name = "at91_tick",
.flags = IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = at91sam926x_pit_interrupt,
.irq = NR_IRQS_LEGACY + AT91_ID_SYS,
};
#ifdef CONFIG_OF
static struct of_device_id pit_timer_ids[] = {
{ .compatible = "atmel,at91sam9260-pit" },
{ /* sentinel */ }
};
static int __init of_at91sam926x_pit_init(void)
{
struct device_node *np;
int ret;
np = of_find_matching_node(NULL, pit_timer_ids);
if (!np)
goto err;
pit_base_addr = of_iomap(np, 0);
if (!pit_base_addr)
goto node_err;
mck = of_clk_get(np, 0);
/* Get the interrupts property */
ret = irq_of_parse_and_map(np, 0);
if (!ret) {
pr_crit("AT91: PIT: Unable to get IRQ from DT\n");
if (!IS_ERR(mck))
clk_put(mck);
goto ioremap_err;
}
at91sam926x_pit_irq.irq = ret;
of_node_put(np);
return 0;
ioremap_err:
iounmap(pit_base_addr);
node_err:
of_node_put(np);
err:
return -EINVAL;
}
#else
static int __init of_at91sam926x_pit_init(void)
{
return -EINVAL;
}
#endif
/* /*
* Set up both clocksource and clockevent support. * Set up both clocksource and clockevent support.
*/ */
void __init at91sam926x_pit_init(void) static void __init at91sam926x_pit_common_init(struct pit_data *data)
{ {
unsigned long pit_rate; unsigned long pit_rate;
unsigned bits; unsigned bits;
int ret; int ret;
mck = ERR_PTR(-ENOENT);
/* For device tree enabled device: initialize here */
of_at91sam926x_pit_init();
/* /*
* Use our actual MCK to figure out how many MCK/16 ticks per * Use our actual MCK to figure out how many MCK/16 ticks per
* 1/HZ period (instead of a compile-time constant LATCH). * 1/HZ period (instead of a compile-time constant LATCH).
*/ */
if (IS_ERR(mck)) pit_rate = clk_get_rate(data->mck) / 16;
mck = clk_get(NULL, "mck"); data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ);
WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0);
if (IS_ERR(mck))
panic("AT91: PIT: Unable to get mck clk\n");
pit_rate = clk_get_rate(mck) / 16;
pit_cycle = (pit_rate + HZ/2) / HZ;
WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0);
/* Initialize and enable the timer */ /* Initialize and enable the timer */
at91sam926x_pit_reset(); at91sam926x_pit_reset(data);
/* /*
* Register clocksource. The high order bits of PIV are unused, * Register clocksource. The high order bits of PIV are unused,
* so this isn't a 32-bit counter unless we get clockevent irqs. * so this isn't a 32-bit counter unless we get clockevent irqs.
*/ */
bits = 12 /* PICNT */ + ilog2(pit_cycle) /* PIV */; bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */;
pit_clk.mask = CLOCKSOURCE_MASK(bits); data->clksrc.mask = CLOCKSOURCE_MASK(bits);
clocksource_register_hz(&pit_clk, pit_rate); data->clksrc.name = "pit";
data->clksrc.rating = 175;
data->clksrc.read = read_pit_clk,
data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS,
clocksource_register_hz(&data->clksrc, pit_rate);
/* Set up irq handler */ /* Set up irq handler */
ret = setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); ret = request_irq(data->irq, at91sam926x_pit_interrupt,
IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
"at91_tick", data);
if (ret) if (ret)
pr_crit("AT91: PIT: Unable to setup IRQ\n"); panic(pr_fmt("Unable to setup IRQ\n"));
/* Set up and register clockevents */ /* Set up and register clockevents */
pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift); data->clkevt.name = "pit";
pit_clkevt.cpumask = cpumask_of(0); data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
clockevents_register_device(&pit_clkevt); data->clkevt.shift = 32;
data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift);
data->clkevt.rating = 100;
data->clkevt.cpumask = cpumask_of(0);
data->clkevt.set_mode = pit_clkevt_mode;
data->clkevt.resume = at91sam926x_pit_resume;
data->clkevt.suspend = at91sam926x_pit_suspend;
clockevents_register_device(&data->clkevt);
} }
void __init at91sam926x_ioremap_pit(u32 addr) static void __init at91sam926x_pit_dt_init(struct device_node *node)
{ {
#if defined(CONFIG_OF) struct pit_data *data;
struct device_node *np =
of_find_matching_node(NULL, pit_timer_ids); data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
panic(pr_fmt("Unable to allocate memory\n"));
data->base = of_iomap(node, 0);
if (!data->base)
panic(pr_fmt("Could not map PIT address\n"));
data->mck = of_clk_get(node, 0);
if (IS_ERR(data->mck))
/* Fallback on clkdev for !CCF-based boards */
data->mck = clk_get(NULL, "mck");
if (IS_ERR(data->mck))
panic(pr_fmt("Unable to get mck clk\n"));
if (np) { /* Get the interrupts property */
of_node_put(np); data->irq = irq_of_parse_and_map(node, 0);
if (!data->irq)
panic(pr_fmt("Unable to get IRQ from DT\n"));
at91sam926x_pit_common_init(data);
}
CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
at91sam926x_pit_dt_init);
static void __iomem *pit_base_addr;
void __init at91sam926x_pit_init(int irq)
{
struct pit_data *data;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
panic(pr_fmt("Unable to allocate memory\n"));
data->base = pit_base_addr;
data->mck = clk_get(NULL, "mck");
if (IS_ERR(data->mck))
panic(pr_fmt("Unable to get mck clk\n"));
data->irq = irq;
at91sam926x_pit_common_init(data);
}
void __init at91sam926x_ioremap_pit(u32 addr)
{
if (of_have_populated_dt())
return; return;
}
#endif
pit_base_addr = ioremap(addr, 16); pit_base_addr = ioremap(addr, 16);
if (!pit_base_addr) if (!pit_base_addr)
panic("Impossible to ioremap PIT\n"); panic(pr_fmt("Impossible to ioremap PIT\n"));
} }
...@@ -376,4 +376,13 @@ config EDAC_OCTEON_PCI ...@@ -376,4 +376,13 @@ config EDAC_OCTEON_PCI
Support for error detection and correction on the Support for error detection and correction on the
Cavium Octeon family of SOCs. Cavium Octeon family of SOCs.
config EDAC_ALTERA_MC
tristate "Altera SDRAM Memory Controller EDAC"
depends on EDAC_MM_EDAC && ARCH_SOCFPGA
help
Support for error detection and correction on the
Altera SDRAM memory controller. Note that the
preloader must initialize the SDRAM before loading
the kernel.
endif # EDAC endif # EDAC
...@@ -65,3 +65,5 @@ obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o ...@@ -65,3 +65,5 @@ obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o
obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o
obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o
obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o
obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o
/*
* Copyright Altera Corporation (C) 2014. All rights reserved.
* Copyright 2011-2012 Calxeda, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* Adapted from the highbank_mc_edac driver.
*/
#include <linux/ctype.h>
#include <linux/edac.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include "edac_core.h"
#include "edac_module.h"
#define EDAC_MOD_STR "altera_edac"
#define EDAC_VERSION "1"
/* SDRAM Controller CtrlCfg Register */
#define CTLCFG_OFST 0x00
/* SDRAM Controller CtrlCfg Register Bit Masks */
#define CTLCFG_ECC_EN 0x400
#define CTLCFG_ECC_CORR_EN 0x800
#define CTLCFG_GEN_SB_ERR 0x2000
#define CTLCFG_GEN_DB_ERR 0x4000
#define CTLCFG_ECC_AUTO_EN (CTLCFG_ECC_EN | \
CTLCFG_ECC_CORR_EN)
/* SDRAM Controller Address Width Register */
#define DRAMADDRW_OFST 0x2C
/* SDRAM Controller Address Widths Field Register */
#define DRAMADDRW_COLBIT_MASK 0x001F
#define DRAMADDRW_COLBIT_SHIFT 0
#define DRAMADDRW_ROWBIT_MASK 0x03E0
#define DRAMADDRW_ROWBIT_SHIFT 5
#define DRAMADDRW_BANKBIT_MASK 0x1C00
#define DRAMADDRW_BANKBIT_SHIFT 10
#define DRAMADDRW_CSBIT_MASK 0xE000
#define DRAMADDRW_CSBIT_SHIFT 13
/* SDRAM Controller Interface Data Width Register */
#define DRAMIFWIDTH_OFST 0x30
/* SDRAM Controller Interface Data Width Defines */
#define DRAMIFWIDTH_16B_ECC 24
#define DRAMIFWIDTH_32B_ECC 40
/* SDRAM Controller DRAM Status Register */
#define DRAMSTS_OFST 0x38
/* SDRAM Controller DRAM Status Register Bit Masks */
#define DRAMSTS_SBEERR 0x04
#define DRAMSTS_DBEERR 0x08
#define DRAMSTS_CORR_DROP 0x10
/* SDRAM Controller DRAM IRQ Register */
#define DRAMINTR_OFST 0x3C
/* SDRAM Controller DRAM IRQ Register Bit Masks */
#define DRAMINTR_INTREN 0x01
#define DRAMINTR_SBEMASK 0x02
#define DRAMINTR_DBEMASK 0x04
#define DRAMINTR_CORRDROPMASK 0x08
#define DRAMINTR_INTRCLR 0x10
/* SDRAM Controller Single Bit Error Count Register */
#define SBECOUNT_OFST 0x40
/* SDRAM Controller Single Bit Error Count Register Bit Masks */
#define SBECOUNT_MASK 0x0F
/* SDRAM Controller Double Bit Error Count Register */
#define DBECOUNT_OFST 0x44
/* SDRAM Controller Double Bit Error Count Register Bit Masks */
#define DBECOUNT_MASK 0x0F
/* SDRAM Controller ECC Error Address Register */
#define ERRADDR_OFST 0x48
/* SDRAM Controller ECC Error Address Register Bit Masks */
#define ERRADDR_MASK 0xFFFFFFFF
/* Altera SDRAM Memory Controller data */
struct altr_sdram_mc_data {
struct regmap *mc_vbase;
};
static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id)
{
struct mem_ctl_info *mci = dev_id;
struct altr_sdram_mc_data *drvdata = mci->pvt_info;
u32 status, err_count, err_addr;
/* Error Address is shared by both SBE & DBE */
regmap_read(drvdata->mc_vbase, ERRADDR_OFST, &err_addr);
regmap_read(drvdata->mc_vbase, DRAMSTS_OFST, &status);
if (status & DRAMSTS_DBEERR) {
regmap_read(drvdata->mc_vbase, DBECOUNT_OFST, &err_count);
panic("\nEDAC: [%d Uncorrectable errors @ 0x%08X]\n",
err_count, err_addr);
}
if (status & DRAMSTS_SBEERR) {
regmap_read(drvdata->mc_vbase, SBECOUNT_OFST, &err_count);
edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, err_count,
err_addr >> PAGE_SHIFT,
err_addr & ~PAGE_MASK, 0,
0, 0, -1, mci->ctl_name, "");
}
regmap_write(drvdata->mc_vbase, DRAMINTR_OFST,
(DRAMINTR_INTRCLR | DRAMINTR_INTREN));
return IRQ_HANDLED;
}
#ifdef CONFIG_EDAC_DEBUG
static ssize_t altr_sdr_mc_err_inject_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct mem_ctl_info *mci = file->private_data;
struct altr_sdram_mc_data *drvdata = mci->pvt_info;
u32 *ptemp;
dma_addr_t dma_handle;
u32 reg, read_reg;
ptemp = dma_alloc_coherent(mci->pdev, 16, &dma_handle, GFP_KERNEL);
if (!ptemp) {
dma_free_coherent(mci->pdev, 16, ptemp, dma_handle);
edac_printk(KERN_ERR, EDAC_MC,
"Inject: Buffer Allocation error\n");
return -ENOMEM;
}
regmap_read(drvdata->mc_vbase, CTLCFG_OFST, &read_reg);
read_reg &= ~(CTLCFG_GEN_SB_ERR | CTLCFG_GEN_DB_ERR);
/* Error are injected by writing a word while the SBE or DBE
* bit in the CTLCFG register is set. Reading the word will
* trigger the SBE or DBE error and the corresponding IRQ.
*/
if (count == 3) {
edac_printk(KERN_ALERT, EDAC_MC,
"Inject Double bit error\n");
regmap_write(drvdata->mc_vbase, CTLCFG_OFST,
(read_reg | CTLCFG_GEN_DB_ERR));
} else {
edac_printk(KERN_ALERT, EDAC_MC,
"Inject Single bit error\n");
regmap_write(drvdata->mc_vbase, CTLCFG_OFST,
(read_reg | CTLCFG_GEN_SB_ERR));
}
ptemp[0] = 0x5A5A5A5A;
ptemp[1] = 0xA5A5A5A5;
/* Clear the error injection bits */
regmap_write(drvdata->mc_vbase, CTLCFG_OFST, read_reg);
/* Ensure it has been written out */
wmb();
/*
* To trigger the error, we need to read the data back
* (the data was written with errors above).
* The ACCESS_ONCE macros and printk are used to prevent the
* the compiler optimizing these reads out.
*/
reg = ACCESS_ONCE(ptemp[0]);
read_reg = ACCESS_ONCE(ptemp[1]);
/* Force Read */
rmb();
edac_printk(KERN_ALERT, EDAC_MC, "Read Data [0x%X, 0x%X]\n",
reg, read_reg);
dma_free_coherent(mci->pdev, 16, ptemp, dma_handle);
return count;
}
static const struct file_operations altr_sdr_mc_debug_inject_fops = {
.open = simple_open,
.write = altr_sdr_mc_err_inject_write,
.llseek = generic_file_llseek,
};
static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
{
if (mci->debugfs)
debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
&altr_sdr_mc_debug_inject_fops);
}
#else
static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
{}
#endif
/* Get total memory size in bytes */
static u32 altr_sdram_get_total_mem_size(struct regmap *mc_vbase)
{
u32 size, read_reg, row, bank, col, cs, width;
if (regmap_read(mc_vbase, DRAMADDRW_OFST, &read_reg) < 0)
return 0;
if (regmap_read(mc_vbase, DRAMIFWIDTH_OFST, &width) < 0)
return 0;
col = (read_reg & DRAMADDRW_COLBIT_MASK) >>
DRAMADDRW_COLBIT_SHIFT;
row = (read_reg & DRAMADDRW_ROWBIT_MASK) >>
DRAMADDRW_ROWBIT_SHIFT;
bank = (read_reg & DRAMADDRW_BANKBIT_MASK) >>
DRAMADDRW_BANKBIT_SHIFT;
cs = (read_reg & DRAMADDRW_CSBIT_MASK) >>
DRAMADDRW_CSBIT_SHIFT;
/* Correct for ECC as its not addressible */
if (width == DRAMIFWIDTH_32B_ECC)
width = 32;
if (width == DRAMIFWIDTH_16B_ECC)
width = 16;
/* calculate the SDRAM size base on this info */
size = 1 << (row + bank + col);
size = size * cs * (width / 8);
return size;
}
static int altr_sdram_probe(struct platform_device *pdev)
{
struct edac_mc_layer layers[2];
struct mem_ctl_info *mci;
struct altr_sdram_mc_data *drvdata;
struct regmap *mc_vbase;
struct dimm_info *dimm;
u32 read_reg, mem_size;
int irq;
int res = 0;
/* Validate the SDRAM controller has ECC enabled */
/* Grab the register range from the sdr controller in device tree */
mc_vbase = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"altr,sdr-syscon");
if (IS_ERR(mc_vbase)) {
edac_printk(KERN_ERR, EDAC_MC,
"regmap for altr,sdr-syscon lookup failed.\n");
return -ENODEV;
}
if (regmap_read(mc_vbase, CTLCFG_OFST, &read_reg) ||
((read_reg & CTLCFG_ECC_AUTO_EN) != CTLCFG_ECC_AUTO_EN)) {
edac_printk(KERN_ERR, EDAC_MC,
"No ECC/ECC disabled [0x%08X]\n", read_reg);
return -ENODEV;
}
/* Grab memory size from device tree. */
mem_size = altr_sdram_get_total_mem_size(mc_vbase);
if (!mem_size) {
edac_printk(KERN_ERR, EDAC_MC,
"Unable to calculate memory size\n");
return -ENODEV;
}
/* Ensure the SDRAM Interrupt is disabled and cleared */
if (regmap_write(mc_vbase, DRAMINTR_OFST, DRAMINTR_INTRCLR)) {
edac_printk(KERN_ERR, EDAC_MC,
"Error clearing SDRAM ECC IRQ\n");
return -ENODEV;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
edac_printk(KERN_ERR, EDAC_MC,
"No irq %d in DT\n", irq);
return -ENODEV;
}
layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
layers[0].size = 1;
layers[0].is_virt_csrow = true;
layers[1].type = EDAC_MC_LAYER_CHANNEL;
layers[1].size = 1;
layers[1].is_virt_csrow = false;
mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
sizeof(struct altr_sdram_mc_data));
if (!mci)
return -ENOMEM;
mci->pdev = &pdev->dev;
drvdata = mci->pvt_info;
drvdata->mc_vbase = mc_vbase;
platform_set_drvdata(pdev, mci);
if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
res = -ENOMEM;
goto free;
}
mci->mtype_cap = MEM_FLAG_DDR3;
mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
mci->edac_cap = EDAC_FLAG_SECDED;
mci->mod_name = EDAC_MOD_STR;
mci->mod_ver = EDAC_VERSION;
mci->ctl_name = dev_name(&pdev->dev);
mci->scrub_mode = SCRUB_SW_SRC;
mci->dev_name = dev_name(&pdev->dev);
dimm = *mci->dimms;
dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1;
dimm->grain = 8;
dimm->dtype = DEV_X8;
dimm->mtype = MEM_DDR3;
dimm->edac_mode = EDAC_SECDED;
res = edac_mc_add_mc(mci);
if (res < 0)
goto err;
res = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler,
0, dev_name(&pdev->dev), mci);
if (res < 0) {
edac_mc_printk(mci, KERN_ERR,
"Unable to request irq %d\n", irq);
res = -ENODEV;
goto err2;
}
if (regmap_write(drvdata->mc_vbase, DRAMINTR_OFST,
(DRAMINTR_INTRCLR | DRAMINTR_INTREN))) {
edac_mc_printk(mci, KERN_ERR,
"Error enabling SDRAM ECC IRQ\n");
res = -ENODEV;
goto err2;
}
altr_sdr_mc_create_debugfs_nodes(mci);
devres_close_group(&pdev->dev, NULL);
return 0;
err2:
edac_mc_del_mc(&pdev->dev);
err:
devres_release_group(&pdev->dev, NULL);
free:
edac_mc_free(mci);
edac_printk(KERN_ERR, EDAC_MC,
"EDAC Probe Failed; Error %d\n", res);
return res;
}
static int altr_sdram_remove(struct platform_device *pdev)
{
struct mem_ctl_info *mci = platform_get_drvdata(pdev);
edac_mc_del_mc(&pdev->dev);
edac_mc_free(mci);
platform_set_drvdata(pdev, NULL);
return 0;
}
static const struct of_device_id altr_sdram_ctrl_of_match[] = {
{ .compatible = "altr,sdram-edac", },
{},
};
MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match);
static struct platform_driver altr_sdram_edac_driver = {
.probe = altr_sdram_probe,
.remove = altr_sdram_remove,
.driver = {
.name = "altr_sdram_edac",
.of_match_table = altr_sdram_ctrl_of_match,
},
};
module_platform_driver(altr_sdram_edac_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Thor Thayer");
MODULE_DESCRIPTION("EDAC Driver for Altera SDRAM Controller");
...@@ -75,6 +75,11 @@ config OR1K_PIC ...@@ -75,6 +75,11 @@ config OR1K_PIC
bool bool
select IRQ_DOMAIN select IRQ_DOMAIN
config OMAP_IRQCHIP
bool
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
config ORION_IRQCHIP config ORION_IRQCHIP
bool bool
select IRQ_DOMAIN select IRQ_DOMAIN
......
...@@ -13,6 +13,7 @@ obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o ...@@ -13,6 +13,7 @@ obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o
obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
......
...@@ -17,15 +17,16 @@ ...@@ -17,15 +17,16 @@
#include <linux/io.h> #include <linux/io.h>
#include <asm/exception.h> #include <asm/exception.h>
#include <asm/mach/irq.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include "soc.h" #include "irqchip.h"
#include "iomap.h"
#include "common.h" /* Define these here for now until we drop all board-files */
#define OMAP24XX_IC_BASE 0x480fe000
#define OMAP34XX_IC_BASE 0x48200000
/* selected INTC register offsets */ /* selected INTC register offsets */
...@@ -41,60 +42,106 @@ ...@@ -41,60 +42,106 @@
#define INTC_MIR_CLEAR0 0x0088 #define INTC_MIR_CLEAR0 0x0088
#define INTC_MIR_SET0 0x008c #define INTC_MIR_SET0 0x008c
#define INTC_PENDING_IRQ0 0x0098 #define INTC_PENDING_IRQ0 0x0098
/* Number of IRQ state bits in each MIR register */ #define INTC_PENDING_IRQ1 0x00b8
#define IRQ_BITS_PER_REG 32 #define INTC_PENDING_IRQ2 0x00d8
#define INTC_PENDING_IRQ3 0x00f8
#define INTC_ILR0 0x0100
#define OMAP2_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP24XX_IC_BASE)
#define OMAP3_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE)
#define INTCPS_SIR_IRQ_OFFSET 0x0040 /* omap2/3 active interrupt offset */
#define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */
#define INTCPS_NR_MIR_REGS 3 #define INTCPS_NR_ILR_REGS 128
#define INTCPS_NR_IRQS 96 #define INTCPS_NR_MIR_REGS 4
/* #define INTC_IDLE_FUNCIDLE (1 << 0)
* OMAP2 has a number of different interrupt controllers, each interrupt #define INTC_IDLE_TURBO (1 << 1)
* controller is identified as its own "bank". Register definitions are
* fairly consistent for each bank, but not all registers are implemented
* for each bank.. when in doubt, consult the TRM.
*/
static struct omap_irq_bank {
void __iomem *base_reg;
unsigned int nr_irqs;
} __attribute__ ((aligned(4))) irq_banks[] = {
{
/* MPU INTC */
.nr_irqs = 96,
},
};
static struct irq_domain *domain; #define INTC_PROTECTION_ENABLE (1 << 0)
/* Structure to save interrupt controller context */ struct omap_intc_regs {
struct omap3_intc_regs {
u32 sysconfig; u32 sysconfig;
u32 protection; u32 protection;
u32 idle; u32 idle;
u32 threshold; u32 threshold;
u32 ilr[INTCPS_NR_IRQS]; u32 ilr[INTCPS_NR_ILR_REGS];
u32 mir[INTCPS_NR_MIR_REGS]; u32 mir[INTCPS_NR_MIR_REGS];
}; };
static struct omap_intc_regs intc_context;
/* INTC bank register get/set */ static struct irq_domain *domain;
static void __iomem *omap_irq_base;
static int omap_nr_pending = 3;
static int omap_nr_irqs = 96;
static void intc_bank_write_reg(u32 val, struct omap_irq_bank *bank, u16 reg) static void intc_writel(u32 reg, u32 val)
{ {
writel_relaxed(val, bank->base_reg + reg); writel_relaxed(val, omap_irq_base + reg);
} }
static u32 intc_bank_read_reg(struct omap_irq_bank *bank, u16 reg) static u32 intc_readl(u32 reg)
{ {
return readl_relaxed(bank->base_reg + reg); return readl_relaxed(omap_irq_base + reg);
}
void omap_intc_save_context(void)
{
int i;
intc_context.sysconfig =
intc_readl(INTC_SYSCONFIG);
intc_context.protection =
intc_readl(INTC_PROTECTION);
intc_context.idle =
intc_readl(INTC_IDLE);
intc_context.threshold =
intc_readl(INTC_THRESHOLD);
for (i = 0; i < omap_nr_irqs; i++)
intc_context.ilr[i] =
intc_readl((INTC_ILR0 + 0x4 * i));
for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
intc_context.mir[i] =
intc_readl(INTC_MIR0 + (0x20 * i));
}
void omap_intc_restore_context(void)
{
int i;
intc_writel(INTC_SYSCONFIG, intc_context.sysconfig);
intc_writel(INTC_PROTECTION, intc_context.protection);
intc_writel(INTC_IDLE, intc_context.idle);
intc_writel(INTC_THRESHOLD, intc_context.threshold);
for (i = 0; i < omap_nr_irqs; i++)
intc_writel(INTC_ILR0 + 0x4 * i,
intc_context.ilr[i]);
for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
intc_writel(INTC_MIR0 + 0x20 * i,
intc_context.mir[i]);
/* MIRs are saved and restore with other PRCM registers */
}
void omap3_intc_prepare_idle(void)
{
/*
* Disable autoidle as it can stall interrupt controller,
* cf. errata ID i540 for 3430 (all revisions up to 3.1.x)
*/
intc_writel(INTC_SYSCONFIG, 0);
intc_writel(INTC_IDLE, INTC_IDLE_TURBO);
}
void omap3_intc_resume_idle(void)
{
/* Re-enable autoidle */
intc_writel(INTC_SYSCONFIG, 1);
intc_writel(INTC_IDLE, 0);
} }
/* XXX: FIQ and additional INTC support (only MPU at the moment) */ /* XXX: FIQ and additional INTC support (only MPU at the moment) */
static void omap_ack_irq(struct irq_data *d) static void omap_ack_irq(struct irq_data *d)
{ {
intc_bank_write_reg(0x1, &irq_banks[0], INTC_CONTROL); intc_writel(INTC_CONTROL, 0x1);
} }
static void omap_mask_ack_irq(struct irq_data *d) static void omap_mask_ack_irq(struct irq_data *d)
...@@ -103,43 +150,81 @@ static void omap_mask_ack_irq(struct irq_data *d) ...@@ -103,43 +150,81 @@ static void omap_mask_ack_irq(struct irq_data *d)
omap_ack_irq(d); omap_ack_irq(d);
} }
static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank) static void __init omap_irq_soft_reset(void)
{ {
unsigned long tmp; unsigned long tmp;
tmp = intc_bank_read_reg(bank, INTC_REVISION) & 0xff; tmp = intc_readl(INTC_REVISION) & 0xff;
pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n", pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n",
bank->base_reg, tmp >> 4, tmp & 0xf, bank->nr_irqs); omap_irq_base, tmp >> 4, tmp & 0xf, omap_nr_irqs);
tmp = intc_bank_read_reg(bank, INTC_SYSCONFIG); tmp = intc_readl(INTC_SYSCONFIG);
tmp |= 1 << 1; /* soft reset */ tmp |= 1 << 1; /* soft reset */
intc_bank_write_reg(tmp, bank, INTC_SYSCONFIG); intc_writel(INTC_SYSCONFIG, tmp);
while (!(intc_bank_read_reg(bank, INTC_SYSSTATUS) & 0x1)) while (!(intc_readl(INTC_SYSSTATUS) & 0x1))
/* Wait for reset to complete */; /* Wait for reset to complete */;
/* Enable autoidle */ /* Enable autoidle */
intc_bank_write_reg(1 << 0, bank, INTC_SYSCONFIG); intc_writel(INTC_SYSCONFIG, 1 << 0);
} }
int omap_irq_pending(void) int omap_irq_pending(void)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(irq_banks); i++) { for (i = 0; i < omap_nr_pending; i++)
struct omap_irq_bank *bank = irq_banks + i; if (intc_readl(INTC_PENDING_IRQ0 + (0x20 * i)))
int irq;
for (irq = 0; irq < bank->nr_irqs; irq += 32)
if (intc_bank_read_reg(bank, INTC_PENDING_IRQ0 +
((irq >> 5) << 5)))
return 1; return 1;
return 0;
}
void omap3_intc_suspend(void)
{
/* A pending interrupt would prevent OMAP from entering suspend */
omap_ack_irq(NULL);
}
static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base)
{
int ret;
int i;
ret = irq_alloc_domain_generic_chips(d, 32, 1, "INTC",
handle_level_irq, IRQ_NOREQUEST | IRQ_NOPROBE,
IRQ_LEVEL, 0);
if (ret) {
pr_warn("Failed to allocate irq chips\n");
return ret;
} }
for (i = 0; i < omap_nr_pending; i++) {
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
gc = irq_get_domain_generic_chip(d, 32 * i);
gc->reg_base = base;
ct = gc->chip_types;
ct->type = IRQ_TYPE_LEVEL_MASK;
ct->handler = handle_level_irq;
ct->chip.irq_ack = omap_mask_ack_irq;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE;
ct->regs.enable = INTC_MIR_CLEAR0 + 32 * i;
ct->regs.disable = INTC_MIR_SET0 + 32 * i;
}
return 0; return 0;
} }
static __init void static void __init omap_alloc_gc_legacy(void __iomem *base,
omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) unsigned int irq_start, unsigned int num)
{ {
struct irq_chip_generic *gc; struct irq_chip_generic *gc;
struct irq_chip_type *ct; struct irq_chip_type *ct;
...@@ -158,93 +243,94 @@ omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) ...@@ -158,93 +243,94 @@ omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)
IRQ_NOREQUEST | IRQ_NOPROBE, 0); IRQ_NOREQUEST | IRQ_NOPROBE, 0);
} }
static void __init omap_init_irq(u32 base, int nr_irqs, static int __init omap_init_irq_of(struct device_node *node)
struct device_node *node) {
int ret;
omap_irq_base = of_iomap(node, 0);
if (WARN_ON(!omap_irq_base))
return -ENOMEM;
domain = irq_domain_add_linear(node, omap_nr_irqs,
&irq_generic_chip_ops, NULL);
omap_irq_soft_reset();
ret = omap_alloc_gc_of(domain, omap_irq_base);
if (ret < 0)
irq_domain_remove(domain);
return ret;
}
static int __init omap_init_irq_legacy(u32 base)
{ {
void __iomem *omap_irq_base; int j, irq_base;
unsigned long nr_of_irqs = 0;
unsigned int nr_banks = 0;
int i, j, irq_base;
omap_irq_base = ioremap(base, SZ_4K); omap_irq_base = ioremap(base, SZ_4K);
if (WARN_ON(!omap_irq_base)) if (WARN_ON(!omap_irq_base))
return; return -ENOMEM;
irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0);
if (irq_base < 0) { if (irq_base < 0) {
pr_warn("Couldn't allocate IRQ numbers\n"); pr_warn("Couldn't allocate IRQ numbers\n");
irq_base = 0; irq_base = 0;
} }
domain = irq_domain_add_legacy(node, nr_irqs, irq_base, 0, domain = irq_domain_add_legacy(NULL, omap_nr_irqs, irq_base, 0,
&irq_domain_simple_ops, NULL); &irq_domain_simple_ops, NULL);
for (i = 0; i < ARRAY_SIZE(irq_banks); i++) { omap_irq_soft_reset();
struct omap_irq_bank *bank = irq_banks + i;
bank->nr_irqs = nr_irqs; for (j = 0; j < omap_nr_irqs; j += 32)
omap_alloc_gc_legacy(omap_irq_base + j, j + irq_base, 32);
/* Static mapping, never released */ return 0;
bank->base_reg = ioremap(base, SZ_4K);
if (!bank->base_reg) {
pr_err("Could not ioremap irq bank%i\n", i);
continue;
}
omap_irq_bank_init_one(bank);
for (j = 0; j < bank->nr_irqs; j += 32)
omap_alloc_gc(bank->base_reg + j, j + irq_base, 32);
nr_of_irqs += bank->nr_irqs;
nr_banks++;
}
pr_info("Total of %ld interrupts on %d active controller%s\n",
nr_of_irqs, nr_banks, nr_banks > 1 ? "s" : "");
} }
void __init omap2_init_irq(void) static void __init omap_irq_enable_protection(void)
{ {
omap_init_irq(OMAP24XX_IC_BASE, 96, NULL); u32 reg;
}
void __init omap3_init_irq(void) reg = intc_readl(INTC_PROTECTION);
{ reg |= INTC_PROTECTION_ENABLE;
omap_init_irq(OMAP34XX_IC_BASE, 96, NULL); intc_writel(INTC_PROTECTION, reg);
} }
void __init ti81xx_init_irq(void) static int __init omap_init_irq(u32 base, struct device_node *node)
{ {
omap_init_irq(OMAP34XX_IC_BASE, 128, NULL); int ret;
if (node)
ret = omap_init_irq_of(node);
else
ret = omap_init_irq_legacy(base);
if (ret == 0)
omap_irq_enable_protection();
return ret;
} }
static inline void omap_intc_handle_irq(void __iomem *base_addr, struct pt_regs *regs) static asmlinkage void __exception_irq_entry
omap_intc_handle_irq(struct pt_regs *regs)
{ {
u32 irqnr; u32 irqnr = 0;
int handled_irq = 0; int handled_irq = 0;
int i;
do { do {
irqnr = readl_relaxed(base_addr + 0x98); for (i = 0; i < omap_nr_pending; i++) {
if (irqnr) irqnr = intc_readl(INTC_PENDING_IRQ0 + (0x20 * i));
goto out;
irqnr = readl_relaxed(base_addr + 0xb8);
if (irqnr) if (irqnr)
goto out; goto out;
}
irqnr = readl_relaxed(base_addr + 0xd8);
#if IS_ENABLED(CONFIG_SOC_TI81XX) || IS_ENABLED(CONFIG_SOC_AM33XX)
if (irqnr)
goto out;
irqnr = readl_relaxed(base_addr + 0xf8);
#endif
out: out:
if (!irqnr) if (!irqnr)
break; break;
irqnr = readl_relaxed(base_addr + INTCPS_SIR_IRQ_OFFSET); irqnr = intc_readl(INTC_SIR);
irqnr &= ACTIVEIRQ_MASK; irqnr &= ACTIVEIRQ_MASK;
if (irqnr) { if (irqnr) {
...@@ -254,127 +340,64 @@ static inline void omap_intc_handle_irq(void __iomem *base_addr, struct pt_regs ...@@ -254,127 +340,64 @@ static inline void omap_intc_handle_irq(void __iomem *base_addr, struct pt_regs
} }
} while (irqnr); } while (irqnr);
/* If an irq is masked or deasserted while active, we will /*
* If an irq is masked or deasserted while active, we will
* keep ending up here with no irq handled. So remove it from * keep ending up here with no irq handled. So remove it from
* the INTC with an ack.*/ * the INTC with an ack.
*/
if (!handled_irq) if (!handled_irq)
omap_ack_irq(NULL); omap_ack_irq(NULL);
} }
asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs) void __init omap2_init_irq(void)
{ {
void __iomem *base_addr = OMAP2_IRQ_BASE; omap_nr_irqs = 96;
omap_intc_handle_irq(base_addr, regs); omap_nr_pending = 3;
omap_init_irq(OMAP24XX_IC_BASE, NULL);
set_handle_irq(omap_intc_handle_irq);
} }
int __init intc_of_init(struct device_node *node, void __init omap3_init_irq(void)
struct device_node *parent)
{ {
struct resource res; omap_nr_irqs = 96;
u32 nr_irq = 96; omap_nr_pending = 3;
omap_init_irq(OMAP34XX_IC_BASE, NULL);
if (WARN_ON(!node)) set_handle_irq(omap_intc_handle_irq);
return -ENODEV;
if (of_address_to_resource(node, 0, &res)) {
WARN(1, "unable to get intc registers\n");
return -EINVAL;
}
if (of_property_read_u32(node, "ti,intc-size", &nr_irq))
pr_warn("unable to get intc-size, default to %d\n", nr_irq);
omap_init_irq(res.start, nr_irq, of_node_get(node));
return 0;
} }
static const struct of_device_id irq_match[] __initconst = { void __init ti81xx_init_irq(void)
{ .compatible = "ti,omap2-intc", .data = intc_of_init, },
{ }
};
void __init omap_intc_of_init(void)
{ {
of_irq_init(irq_match); omap_nr_irqs = 96;
omap_nr_pending = 4;
omap_init_irq(OMAP34XX_IC_BASE, NULL);
set_handle_irq(omap_intc_handle_irq);
} }
#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX) static int __init intc_of_init(struct device_node *node,
static struct omap3_intc_regs intc_context[ARRAY_SIZE(irq_banks)]; struct device_node *parent)
void omap_intc_save_context(void)
{ {
int ind = 0, i = 0; int ret;
for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) {
struct omap_irq_bank *bank = irq_banks + ind;
intc_context[ind].sysconfig =
intc_bank_read_reg(bank, INTC_SYSCONFIG);
intc_context[ind].protection =
intc_bank_read_reg(bank, INTC_PROTECTION);
intc_context[ind].idle =
intc_bank_read_reg(bank, INTC_IDLE);
intc_context[ind].threshold =
intc_bank_read_reg(bank, INTC_THRESHOLD);
for (i = 0; i < INTCPS_NR_IRQS; i++)
intc_context[ind].ilr[i] =
intc_bank_read_reg(bank, (0x100 + 0x4*i));
for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
intc_context[ind].mir[i] =
intc_bank_read_reg(&irq_banks[0], INTC_MIR0 +
(0x20 * i));
}
}
void omap_intc_restore_context(void) omap_nr_pending = 3;
{ omap_nr_irqs = 96;
int ind = 0, i = 0;
if (WARN_ON(!node))
for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) { return -ENODEV;
struct omap_irq_bank *bank = irq_banks + ind;
intc_bank_write_reg(intc_context[ind].sysconfig, if (of_device_is_compatible(node, "ti,am33xx-intc")) {
bank, INTC_SYSCONFIG); omap_nr_irqs = 128;
intc_bank_write_reg(intc_context[ind].sysconfig, omap_nr_pending = 4;
bank, INTC_SYSCONFIG);
intc_bank_write_reg(intc_context[ind].protection,
bank, INTC_PROTECTION);
intc_bank_write_reg(intc_context[ind].idle,
bank, INTC_IDLE);
intc_bank_write_reg(intc_context[ind].threshold,
bank, INTC_THRESHOLD);
for (i = 0; i < INTCPS_NR_IRQS; i++)
intc_bank_write_reg(intc_context[ind].ilr[i],
bank, (0x100 + 0x4*i));
for (i = 0; i < INTCPS_NR_MIR_REGS; i++)
intc_bank_write_reg(intc_context[ind].mir[i],
&irq_banks[0], INTC_MIR0 + (0x20 * i));
} }
/* MIRs are saved and restore with other PRCM registers */
}
void omap3_intc_suspend(void) ret = omap_init_irq(-1, of_node_get(node));
{ if (ret < 0)
/* A pending interrupt would prevent OMAP from entering suspend */ return ret;
omap_ack_irq(NULL);
}
void omap3_intc_prepare_idle(void) set_handle_irq(omap_intc_handle_irq);
{
/*
* Disable autoidle as it can stall interrupt controller,
* cf. errata ID i540 for 3430 (all revisions up to 3.1.x)
*/
intc_bank_write_reg(0, &irq_banks[0], INTC_SYSCONFIG);
}
void omap3_intc_resume_idle(void) return 0;
{
/* Re-enable autoidle */
intc_bank_write_reg(1, &irq_banks[0], INTC_SYSCONFIG);
} }
asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs) IRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init);
{ IRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init);
void __iomem *base_addr = OMAP3_IRQ_BASE; IRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init);
omap_intc_handle_irq(base_addr, regs);
}
#endif /* CONFIG_ARCH_OMAP3 */
...@@ -478,6 +478,16 @@ config LEDS_BLINKM ...@@ -478,6 +478,16 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED. through I2C. Say Y to enable support for the BlinkM LED.
config LEDS_SYSCON
bool "LED support for LEDs on system controllers"
depends on LEDS_CLASS=y
depends on MFD_SYSCON
depends on OF
help
This option enabled support for the LEDs on syscon type
devices. This will only work with device tree enabled
devices.
config LEDS_VERSATILE config LEDS_VERSATILE
tristate "LED support for the ARM Versatile and RealView" tristate "LED support for the ARM Versatile and RealView"
depends on ARCH_REALVIEW || ARCH_VERSATILE depends on ARCH_REALVIEW || ARCH_VERSATILE
......
...@@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o ...@@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
# LED SPI Drivers # LED SPI Drivers
......
/*
* Generic Syscon LEDs Driver
*
* Copyright (c) 2014, Linaro Limited
* Author: Linus Walleij <linus.walleij@linaro.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* This driver provides system reboot functionality for APM X-Gene SoC.
* For system shutdown, this is board specify. If a board designer
* implements GPIO shutdown, use the gpio-poweroff.c driver.
*/
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/leds.h>
/**
* struct syscon_led - state container for syscon based LEDs
* @cdev: LED class device for this LED
* @map: regmap to access the syscon device backing this LED
* @offset: the offset into the syscon regmap for the LED register
* @mask: the bit in the register corresponding to the LED
* @state: current state of the LED
*/
struct syscon_led {
struct led_classdev cdev;
struct regmap *map;
u32 offset;
u32 mask;
bool state;
};
static void syscon_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct syscon_led *sled =
container_of(led_cdev, struct syscon_led, cdev);
u32 val;
int ret;
if (value == LED_OFF) {
val = 0;
sled->state = false;
} else {
val = sled->mask;
sled->state = true;
}
ret = regmap_update_bits(sled->map, sled->offset, sled->mask, val);
if (ret < 0)
dev_err(sled->cdev.dev, "error updating LED status\n");
}
static const struct of_device_id syscon_match[] = {
{ .compatible = "syscon", },
{},
};
static int __init syscon_leds_init(void)
{
const struct of_device_id *devid;
struct device_node *np;
struct device_node *child;
struct regmap *map;
struct platform_device *pdev;
struct device *dev;
int ret;
np = of_find_matching_node_and_match(NULL, syscon_match,
&devid);
if (!np)
return -ENODEV;
map = syscon_node_to_regmap(np);
if (IS_ERR(map))
return PTR_ERR(map);
/*
* If the map is there, the device should be there, we allocate
* memory on the syscon device's behalf here.
*/
pdev = of_find_device_by_node(np);
if (!pdev)
return -ENODEV;
dev = &pdev->dev;
for_each_available_child_of_node(np, child) {
struct syscon_led *sled;
const char *state;
/* Only check for register-bit-leds */
if (of_property_match_string(child, "compatible",
"register-bit-led") < 0)
continue;
sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL);
if (!sled)
return -ENOMEM;
sled->map = map;
if (of_property_read_u32(child, "offset", &sled->offset))
return -EINVAL;
if (of_property_read_u32(child, "mask", &sled->mask))
return -EINVAL;
sled->cdev.name =
of_get_property(child, "label", NULL) ? : child->name;
sled->cdev.default_trigger =
of_get_property(child, "linux,default-trigger", NULL);
state = of_get_property(child, "default-state", NULL);
if (state) {
if (!strcmp(state, "keep")) {
u32 val;
ret = regmap_read(map, sled->offset, &val);
if (ret < 0)
return ret;
sled->state = !!(val & sled->mask);
} else if (!strcmp(state, "on")) {
sled->state = true;
ret = regmap_update_bits(map, sled->offset,
sled->mask,
sled->mask);
if (ret < 0)
return ret;
} else {
sled->state = false;
ret = regmap_update_bits(map, sled->offset,
sled->mask, 0);
if (ret < 0)
return ret;
}
}
sled->cdev.brightness_set = syscon_led_set;
ret = led_classdev_register(dev, &sled->cdev);
if (ret < 0)
return ret;
dev_info(dev, "registered LED %s\n", sled->cdev.name);
}
return 0;
}
device_initcall(syscon_leds_init);
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/platform_data/mailbox-omap.h> #include <linux/platform_data/mailbox-omap.h>
...@@ -94,6 +95,18 @@ struct omap_mbox_device { ...@@ -94,6 +95,18 @@ struct omap_mbox_device {
struct list_head elem; struct list_head elem;
}; };
struct omap_mbox_fifo_info {
int tx_id;
int tx_usr;
int tx_irq;
int rx_id;
int rx_usr;
int rx_irq;
const char *name;
};
struct omap_mbox { struct omap_mbox {
const char *name; const char *name;
int irq; int irq;
...@@ -587,24 +600,118 @@ static int omap_mbox_unregister(struct omap_mbox_device *mdev) ...@@ -587,24 +600,118 @@ static int omap_mbox_unregister(struct omap_mbox_device *mdev)
return 0; return 0;
} }
static const struct of_device_id omap_mailbox_of_match[] = {
{
.compatible = "ti,omap2-mailbox",
.data = (void *)MBOX_INTR_CFG_TYPE1,
},
{
.compatible = "ti,omap3-mailbox",
.data = (void *)MBOX_INTR_CFG_TYPE1,
},
{
.compatible = "ti,omap4-mailbox",
.data = (void *)MBOX_INTR_CFG_TYPE2,
},
{
/* end */
},
};
MODULE_DEVICE_TABLE(of, omap_mailbox_of_match);
static int omap_mbox_probe(struct platform_device *pdev) static int omap_mbox_probe(struct platform_device *pdev)
{ {
struct resource *mem; struct resource *mem;
int ret; int ret;
struct omap_mbox **list, *mbox, *mboxblk; struct omap_mbox **list, *mbox, *mboxblk;
struct omap_mbox_pdata *pdata = pdev->dev.platform_data; struct omap_mbox_pdata *pdata = pdev->dev.platform_data;
struct omap_mbox_dev_info *info; struct omap_mbox_dev_info *info = NULL;
struct omap_mbox_fifo_info *finfo, *finfoblk;
struct omap_mbox_device *mdev; struct omap_mbox_device *mdev;
struct omap_mbox_fifo *fifo; struct omap_mbox_fifo *fifo;
u32 intr_type; struct device_node *node = pdev->dev.of_node;
struct device_node *child;
const struct of_device_id *match;
u32 intr_type, info_count;
u32 num_users, num_fifos;
u32 tmp[3];
u32 l; u32 l;
int i; int i;
if (!pdata || !pdata->info_cnt || !pdata->info) { if (!node && (!pdata || !pdata->info_cnt || !pdata->info)) {
pr_err("%s: platform not supported\n", __func__); pr_err("%s: platform not supported\n", __func__);
return -ENODEV; return -ENODEV;
} }
if (node) {
match = of_match_device(omap_mailbox_of_match, &pdev->dev);
if (!match)
return -ENODEV;
intr_type = (u32)match->data;
if (of_property_read_u32(node, "ti,mbox-num-users",
&num_users))
return -ENODEV;
if (of_property_read_u32(node, "ti,mbox-num-fifos",
&num_fifos))
return -ENODEV;
info_count = of_get_available_child_count(node);
if (!info_count) {
dev_err(&pdev->dev, "no available mbox devices found\n");
return -ENODEV;
}
} else { /* non-DT device creation */
info_count = pdata->info_cnt;
info = pdata->info;
intr_type = pdata->intr_type;
num_users = pdata->num_users;
num_fifos = pdata->num_fifos;
}
finfoblk = devm_kzalloc(&pdev->dev, info_count * sizeof(*finfoblk),
GFP_KERNEL);
if (!finfoblk)
return -ENOMEM;
finfo = finfoblk;
child = NULL;
for (i = 0; i < info_count; i++, finfo++) {
if (node) {
child = of_get_next_available_child(node, child);
ret = of_property_read_u32_array(child, "ti,mbox-tx",
tmp, ARRAY_SIZE(tmp));
if (ret)
return ret;
finfo->tx_id = tmp[0];
finfo->tx_irq = tmp[1];
finfo->tx_usr = tmp[2];
ret = of_property_read_u32_array(child, "ti,mbox-rx",
tmp, ARRAY_SIZE(tmp));
if (ret)
return ret;
finfo->rx_id = tmp[0];
finfo->rx_irq = tmp[1];
finfo->rx_usr = tmp[2];
finfo->name = child->name;
} else {
finfo->tx_id = info->tx_id;
finfo->rx_id = info->rx_id;
finfo->tx_usr = info->usr_id;
finfo->tx_irq = info->irq_id;
finfo->rx_usr = info->usr_id;
finfo->rx_irq = info->irq_id;
finfo->name = info->name;
info++;
}
if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos ||
finfo->tx_usr >= num_users || finfo->rx_usr >= num_users)
return -EINVAL;
}
mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL);
if (!mdev) if (!mdev)
return -ENOMEM; return -ENOMEM;
...@@ -615,41 +722,40 @@ static int omap_mbox_probe(struct platform_device *pdev) ...@@ -615,41 +722,40 @@ static int omap_mbox_probe(struct platform_device *pdev)
return PTR_ERR(mdev->mbox_base); return PTR_ERR(mdev->mbox_base);
/* allocate one extra for marking end of list */ /* allocate one extra for marking end of list */
list = devm_kzalloc(&pdev->dev, (pdata->info_cnt + 1) * sizeof(*list), list = devm_kzalloc(&pdev->dev, (info_count + 1) * sizeof(*list),
GFP_KERNEL); GFP_KERNEL);
if (!list) if (!list)
return -ENOMEM; return -ENOMEM;
mboxblk = devm_kzalloc(&pdev->dev, pdata->info_cnt * sizeof(*mbox), mboxblk = devm_kzalloc(&pdev->dev, info_count * sizeof(*mbox),
GFP_KERNEL); GFP_KERNEL);
if (!mboxblk) if (!mboxblk)
return -ENOMEM; return -ENOMEM;
info = pdata->info;
intr_type = pdata->intr_type;
mbox = mboxblk; mbox = mboxblk;
for (i = 0; i < pdata->info_cnt; i++, info++) { finfo = finfoblk;
for (i = 0; i < info_count; i++, finfo++) {
fifo = &mbox->tx_fifo; fifo = &mbox->tx_fifo;
fifo->msg = MAILBOX_MESSAGE(info->tx_id); fifo->msg = MAILBOX_MESSAGE(finfo->tx_id);
fifo->fifo_stat = MAILBOX_FIFOSTATUS(info->tx_id); fifo->fifo_stat = MAILBOX_FIFOSTATUS(finfo->tx_id);
fifo->intr_bit = MAILBOX_IRQ_NOTFULL(info->tx_id); fifo->intr_bit = MAILBOX_IRQ_NOTFULL(finfo->tx_id);
fifo->irqenable = MAILBOX_IRQENABLE(intr_type, info->usr_id); fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->tx_usr);
fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, info->usr_id); fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->tx_usr);
fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, info->usr_id); fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->tx_usr);
fifo = &mbox->rx_fifo; fifo = &mbox->rx_fifo;
fifo->msg = MAILBOX_MESSAGE(info->rx_id); fifo->msg = MAILBOX_MESSAGE(finfo->rx_id);
fifo->msg_stat = MAILBOX_MSGSTATUS(info->rx_id); fifo->msg_stat = MAILBOX_MSGSTATUS(finfo->rx_id);
fifo->intr_bit = MAILBOX_IRQ_NEWMSG(info->rx_id); fifo->intr_bit = MAILBOX_IRQ_NEWMSG(finfo->rx_id);
fifo->irqenable = MAILBOX_IRQENABLE(intr_type, info->usr_id); fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->rx_usr);
fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, info->usr_id); fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr);
fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, info->usr_id); fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr);
mbox->intr_type = intr_type; mbox->intr_type = intr_type;
mbox->parent = mdev; mbox->parent = mdev;
mbox->name = info->name; mbox->name = finfo->name;
mbox->irq = platform_get_irq(pdev, info->irq_id); mbox->irq = platform_get_irq(pdev, finfo->tx_irq);
if (mbox->irq < 0) if (mbox->irq < 0)
return mbox->irq; return mbox->irq;
list[i] = mbox++; list[i] = mbox++;
...@@ -657,8 +763,8 @@ static int omap_mbox_probe(struct platform_device *pdev) ...@@ -657,8 +763,8 @@ static int omap_mbox_probe(struct platform_device *pdev)
mutex_init(&mdev->cfg_lock); mutex_init(&mdev->cfg_lock);
mdev->dev = &pdev->dev; mdev->dev = &pdev->dev;
mdev->num_users = pdata->num_users; mdev->num_users = num_users;
mdev->num_fifos = pdata->num_fifos; mdev->num_fifos = num_fifos;
mdev->mboxes = list; mdev->mboxes = list;
ret = omap_mbox_register(mdev); ret = omap_mbox_register(mdev);
if (ret) if (ret)
...@@ -684,6 +790,7 @@ static int omap_mbox_probe(struct platform_device *pdev) ...@@ -684,6 +790,7 @@ static int omap_mbox_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
goto unregister; goto unregister;
devm_kfree(&pdev->dev, finfoblk);
return 0; return 0;
unregister: unregister:
...@@ -708,6 +815,7 @@ static struct platform_driver omap_mbox_driver = { ...@@ -708,6 +815,7 @@ static struct platform_driver omap_mbox_driver = {
.driver = { .driver = {
.name = "omap-mailbox", .name = "omap-mailbox",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(omap_mailbox_of_match),
}, },
}; };
......
...@@ -7,6 +7,16 @@ menuconfig MEMORY ...@@ -7,6 +7,16 @@ menuconfig MEMORY
if MEMORY if MEMORY
config ATMEL_SDRAMC
bool "Atmel (Multi-port DDR-)SDRAM Controller"
default y
depends on ARCH_AT91 && OF
help
This driver is for Atmel SDRAM Controller or Atmel Multi-port
DDR-SDRAM Controller available on Atmel AT91SAM9 and SAMA5 SoCs.
Starting with the at91sam9g45, this controller supports SDR, DDR and
LP-DDR memories.
config TI_AEMIF config TI_AEMIF
tristate "Texas Instruments AEMIF driver" tristate "Texas Instruments AEMIF driver"
depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
ifeq ($(CONFIG_DDR),y) ifeq ($(CONFIG_DDR),y)
obj-$(CONFIG_OF) += of_memory.o obj-$(CONFIG_OF) += of_memory.o
endif endif
obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o
......
/*
* Atmel (Multi-port DDR-)SDRAM Controller driver
*
* Copyright (C) 2014 Atmel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
struct at91_ramc_caps {
bool has_ddrck;
bool has_mpddr_clk;
};
static const struct at91_ramc_caps at91rm9200_caps = { };
static const struct at91_ramc_caps at91sam9g45_caps = {
.has_ddrck = 1,
.has_mpddr_clk = 0,
};
static const struct at91_ramc_caps sama5d3_caps = {
.has_ddrck = 1,
.has_mpddr_clk = 1,
};
static const struct of_device_id atmel_ramc_of_match[] = {
{ .compatible = "atmel,at91rm9200-sdramc", .data = &at91rm9200_caps, },
{ .compatible = "atmel,at91sam9260-sdramc", .data = &at91rm9200_caps, },
{ .compatible = "atmel,at91sam9g45-ddramc", .data = &at91sam9g45_caps, },
{ .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, },
{},
};
MODULE_DEVICE_TABLE(of, atmel_ramc_of_match);
static int atmel_ramc_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct at91_ramc_caps *caps;
struct clk *clk;
match = of_match_device(atmel_ramc_of_match, &pdev->dev);
caps = match->data;
if (caps->has_ddrck) {
clk = devm_clk_get(&pdev->dev, "ddrck");
if (IS_ERR(clk))
return PTR_ERR(clk);
clk_prepare_enable(clk);
}
if (caps->has_mpddr_clk) {
clk = devm_clk_get(&pdev->dev, "mpddr");
if (IS_ERR(clk)) {
pr_err("AT91 RAMC: couldn't get mpddr clock\n");
return PTR_ERR(clk);
}
clk_prepare_enable(clk);
}
return 0;
}
static struct platform_driver atmel_ramc_driver = {
.probe = atmel_ramc_probe,
.driver = {
.name = "atmel-ramc",
.owner = THIS_MODULE,
.of_match_table = atmel_ramc_of_match,
},
};
static int __init atmel_ramc_init(void)
{
return platform_driver_register(&atmel_ramc_driver);
}
module_init(atmel_ramc_init);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller");
...@@ -6,15 +6,33 @@ menuconfig POWER_RESET ...@@ -6,15 +6,33 @@ menuconfig POWER_RESET
Say Y here to enable board reset and power off Say Y here to enable board reset and power off
if POWER_RESET
config POWER_RESET_AS3722 config POWER_RESET_AS3722
bool "ams AS3722 power-off driver" bool "ams AS3722 power-off driver"
depends on MFD_AS3722 && POWER_RESET depends on MFD_AS3722
help help
This driver supports turning off board via a ams AS3722 power-off. This driver supports turning off board via a ams AS3722 power-off.
config POWER_RESET_AT91_POWEROFF
bool "Atmel AT91 poweroff driver"
depends on ARCH_AT91
default SOC_AT91SAM9 || SOC_SAMA5
help
This driver supports poweroff for Atmel AT91SAM9 and SAMA5
SoCs
config POWER_RESET_AT91_RESET
bool "Atmel AT91 reset driver"
depends on ARCH_AT91
default SOC_AT91SAM9 || SOC_SAMA5
help
This driver supports restart for Atmel AT91SAM9 and SAMA5
SoCs
config POWER_RESET_AXXIA config POWER_RESET_AXXIA
bool "LSI Axxia reset driver" bool "LSI Axxia reset driver"
depends on POWER_RESET && ARCH_AXXIA depends on ARCH_AXXIA
help help
This driver supports restart for Axxia SoC. This driver supports restart for Axxia SoC.
...@@ -33,7 +51,7 @@ config POWER_RESET_BRCMSTB ...@@ -33,7 +51,7 @@ config POWER_RESET_BRCMSTB
config POWER_RESET_GPIO config POWER_RESET_GPIO
bool "GPIO power-off driver" bool "GPIO power-off driver"
depends on OF_GPIO && POWER_RESET depends on OF_GPIO
help help
This driver supports turning off your board via a GPIO line. This driver supports turning off your board via a GPIO line.
If your board needs a GPIO high/low to power down, say Y and If your board needs a GPIO high/low to power down, say Y and
...@@ -47,13 +65,13 @@ config POWER_RESET_HISI ...@@ -47,13 +65,13 @@ config POWER_RESET_HISI
config POWER_RESET_MSM config POWER_RESET_MSM
bool "Qualcomm MSM power-off driver" bool "Qualcomm MSM power-off driver"
depends on POWER_RESET && ARCH_QCOM depends on ARCH_QCOM
help help
Power off and restart support for Qualcomm boards. Power off and restart support for Qualcomm boards.
config POWER_RESET_QNAP config POWER_RESET_QNAP
bool "QNAP power-off driver" bool "QNAP power-off driver"
depends on OF_GPIO && POWER_RESET && PLAT_ORION depends on OF_GPIO && PLAT_ORION
help help
This driver supports turning off QNAP NAS devices by sending This driver supports turning off QNAP NAS devices by sending
commands to the microcontroller which controls the main power. commands to the microcontroller which controls the main power.
...@@ -71,14 +89,22 @@ config POWER_RESET_RESTART ...@@ -71,14 +89,22 @@ config POWER_RESET_RESTART
config POWER_RESET_SUN6I config POWER_RESET_SUN6I
bool "Allwinner A31 SoC reset driver" bool "Allwinner A31 SoC reset driver"
depends on ARCH_SUNXI depends on ARCH_SUNXI
depends on POWER_RESET
help help
Reboot support for the Allwinner A31 SoCs. Reboot support for the Allwinner A31 SoCs.
config POWER_RESET_VERSATILE
bool "ARM Versatile family reboot driver"
depends on ARM
depends on MFD_SYSCON
depends on OF
help
Power off and restart support for ARM Versatile family of
reference boards.
config POWER_RESET_VEXPRESS config POWER_RESET_VEXPRESS
bool "ARM Versatile Express power-off and reset driver" bool "ARM Versatile Express power-off and reset driver"
depends on ARM || ARM64 depends on ARM || ARM64
depends on POWER_RESET && VEXPRESS_CONFIG depends on VEXPRESS_CONFIG
help help
Power off and reset support for the ARM Ltd. Versatile Power off and reset support for the ARM Ltd. Versatile
Express boards. Express boards.
...@@ -86,7 +112,6 @@ config POWER_RESET_VEXPRESS ...@@ -86,7 +112,6 @@ config POWER_RESET_VEXPRESS
config POWER_RESET_XGENE config POWER_RESET_XGENE
bool "APM SoC X-Gene reset driver" bool "APM SoC X-Gene reset driver"
depends on ARM64 depends on ARM64
depends on POWER_RESET
help help
Reboot support for the APM SoC X-Gene Eval boards. Reboot support for the APM SoC X-Gene Eval boards.
...@@ -97,3 +122,4 @@ config POWER_RESET_KEYSTONE ...@@ -97,3 +122,4 @@ config POWER_RESET_KEYSTONE
help help
Reboot support for the KEYSTONE SoCs. Reboot support for the KEYSTONE SoCs.
endif
obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o
obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o
obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o
obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
...@@ -7,6 +9,7 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o ...@@ -7,6 +9,7 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
/*
* Copyright (C) 2014 Linaro Ltd.
*
* Author: Linus Walleij <linus.walleij@linaro.org>
*
* 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/init.h>
#include <linux/mfd/syscon.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <asm/system_misc.h>
#define REALVIEW_SYS_LOCK_OFFSET 0x20
#define REALVIEW_SYS_LOCK_VAL 0xA05F
#define REALVIEW_SYS_RESETCTL_OFFSET 0x40
/*
* We detect the different syscon types from the compatible strings.
*/
enum versatile_reboot {
REALVIEW_REBOOT_EB,
REALVIEW_REBOOT_PB1176,
REALVIEW_REBOOT_PB11MP,
REALVIEW_REBOOT_PBA8,
REALVIEW_REBOOT_PBX,
};
/* Pointer to the system controller */
static struct regmap *syscon_regmap;
static enum versatile_reboot versatile_reboot_type;
static const struct of_device_id versatile_reboot_of_match[] = {
{
.compatible = "arm,realview-eb-syscon",
.data = (void *)REALVIEW_REBOOT_EB,
},
{
.compatible = "arm,realview-pb1176-syscon",
.data = (void *)REALVIEW_REBOOT_PB1176,
},
{
.compatible = "arm,realview-pb11mp-syscon",
.data = (void *)REALVIEW_REBOOT_PB11MP,
},
{
.compatible = "arm,realview-pba8-syscon",
.data = (void *)REALVIEW_REBOOT_PBA8,
},
{
.compatible = "arm,realview-pbx-syscon",
.data = (void *)REALVIEW_REBOOT_PBX,
},
};
static void versatile_reboot(enum reboot_mode mode, const char *cmd)
{
/* Unlock the reset register */
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
REALVIEW_SYS_LOCK_VAL);
/* Then hit reset on the different machines */
switch (versatile_reboot_type) {
case REALVIEW_REBOOT_EB:
regmap_write(syscon_regmap,
REALVIEW_SYS_RESETCTL_OFFSET, 0x0008);
break;
case REALVIEW_REBOOT_PB1176:
regmap_write(syscon_regmap,
REALVIEW_SYS_RESETCTL_OFFSET, 0x0100);
break;
case REALVIEW_REBOOT_PB11MP:
case REALVIEW_REBOOT_PBA8:
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x0000);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x0004);
break;
case REALVIEW_REBOOT_PBX:
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x00f0);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x00f4);
break;
}
dsb();
}
static int __init versatile_reboot_probe(void)
{
const struct of_device_id *reboot_id;
struct device_node *np;
np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match,
&reboot_id);
if (!np)
return -ENODEV;
versatile_reboot_type = (enum versatile_reboot)reboot_id->data;
syscon_regmap = syscon_node_to_regmap(np);
if (IS_ERR(syscon_regmap))
return PTR_ERR(syscon_regmap);
arm_pm_restart = versatile_reboot;
pr_info("versatile reboot driver registered\n");
return 0;
}
device_initcall(versatile_reboot_probe);
/*
* Atmel AT91 SAM9 SoCs reset code
*
* Copyright (C) 2007 Atmel Corporation.
* Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
* Copyright (C) 2014 Free Electrons
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
#define AT91_SHDW_MR 0x04 /* Shut Down Mode Register */
#define AT91_SHDW_WKMODE0 GENMASK(2, 0) /* Wake-up 0 Mode Selection */
#define AT91_SHDW_CPTWK0_MAX 0xf /* Maximum Counter On Wake Up 0 */
#define AT91_SHDW_CPTWK0 (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */
#define AT91_SHDW_CPTWK0_(x) ((x) << 4)
#define AT91_SHDW_RTTWKEN BIT(16) /* Real Time Timer Wake-up Enable */
#define AT91_SHDW_RTCWKEN BIT(17) /* Real Time Clock Wake-up Enable */
#define AT91_SHDW_SR 0x08 /* Shut Down Status Register */
#define AT91_SHDW_WAKEUP0 BIT(0) /* Wake-up 0 Status */
#define AT91_SHDW_RTTWK BIT(16) /* Real-time Timer Wake-up */
#define AT91_SHDW_RTCWK BIT(17) /* Real-time Clock Wake-up [SAM9RL] */
enum wakeup_type {
AT91_SHDW_WKMODE0_NONE = 0,
AT91_SHDW_WKMODE0_HIGH = 1,
AT91_SHDW_WKMODE0_LOW = 2,
AT91_SHDW_WKMODE0_ANYLEVEL = 3,
};
static const char *shdwc_wakeup_modes[] = {
[AT91_SHDW_WKMODE0_NONE] = "none",
[AT91_SHDW_WKMODE0_HIGH] = "high",
[AT91_SHDW_WKMODE0_LOW] = "low",
[AT91_SHDW_WKMODE0_ANYLEVEL] = "any",
};
static void __iomem *at91_shdwc_base;
static void __init at91_wakeup_status(void)
{
u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR);
char *reason = "unknown";
/* Simple power-on, just bail out */
if (!reg)
return;
if (reg & AT91_SHDW_RTTWK)
reason = "RTT";
else if (reg & AT91_SHDW_RTCWK)
reason = "RTC";
pr_info("AT91: Wake-Up source: %s\n", reason);
}
static void at91_poweroff(void)
{
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
}
const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
{
const char *pm;
int err, i;
err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
if (err < 0)
return AT91_SHDW_WKMODE0_ANYLEVEL;
for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++)
if (!strcasecmp(pm, shdwc_wakeup_modes[i]))
return i;
return -ENODEV;
}
static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
enum wakeup_type wakeup_mode;
u32 mode = 0, tmp;
wakeup_mode = at91_poweroff_get_wakeup_mode(np);
if (wakeup_mode < 0) {
dev_warn(&pdev->dev, "shdwc unknown wakeup mode\n");
return;
}
if (!of_property_read_u32(np, "atmel,wakeup-counter", &tmp)) {
if (tmp > AT91_SHDW_CPTWK0_MAX) {
dev_warn(&pdev->dev,
"shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n",
tmp, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX);
tmp = AT91_SHDW_CPTWK0_MAX;
}
mode |= AT91_SHDW_CPTWK0_(tmp);
}
if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
mode |= AT91_SHDW_RTCWKEN;
if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
mode |= AT91_SHDW_RTTWKEN;
writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR);
}
static int at91_poweroff_probe(struct platform_device *pdev)
{
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(at91_shdwc_base)) {
dev_err(&pdev->dev, "Could not map reset controller address\n");
return PTR_ERR(at91_shdwc_base);
}
at91_wakeup_status();
if (pdev->dev.of_node)
at91_poweroff_dt_set_wakeup_mode(pdev);
pm_power_off = at91_poweroff;
return 0;
}
static struct of_device_id at91_poweroff_of_match[] = {
{ .compatible = "atmel,at91sam9260-shdwc", },
{ .compatible = "atmel,at91sam9rl-shdwc", },
{ .compatible = "atmel,at91sam9x5-shdwc", },
{ /*sentinel*/ }
};
static struct platform_driver at91_poweroff_driver = {
.probe = at91_poweroff_probe,
.driver = {
.name = "at91-poweroff",
.of_match_table = at91_poweroff_of_match,
},
};
module_platform_driver(at91_poweroff_driver);
/*
* Atmel AT91 SAM9 SoCs reset code
*
* Copyright (C) 2007 Atmel Corporation.
* Copyright (C) BitBox Ltd 2010
* Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcosoft.com>
* Copyright (C) 2014 Free Electrons
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <asm/system_misc.h>
#include <mach/at91sam9_ddrsdr.h>
#include <mach/at91sam9_sdramc.h>
#define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */
#define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */
#define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */
#define AT91_RSTC_EXTRST BIT(3) /* External Reset */
#define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */
#define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */
#define AT91_RSTC_URSTS BIT(0) /* User Reset Status */
#define AT91_RSTC_RSTTYP GENMASK(10, 8) /* Reset Type */
#define AT91_RSTC_NRSTL BIT(16) /* NRST Pin Level */
#define AT91_RSTC_SRCMP BIT(17) /* Software Reset Command in Progress */
#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */
#define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */
#define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */
#define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */
enum reset_type {
RESET_TYPE_GENERAL = 0,
RESET_TYPE_WAKEUP = 1,
RESET_TYPE_WATCHDOG = 2,
RESET_TYPE_SOFTWARE = 3,
RESET_TYPE_USER = 4,
};
static void __iomem *at91_ramc_base[2], *at91_rstc_base;
/*
* unless the SDRAM is cleanly shutdown before we hit the
* reset register it can be left driving the data bus and
* killing the chance of a subsequent boot from NAND
*/
static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
{
asm volatile(
/* Align to cache lines */
".balign 32\n\t"
/* Disable SDRAM accesses */
"str %2, [%0, #" __stringify(AT91_SDRAMC_TR) "]\n\t"
/* Power down SDRAM */
"str %3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]\n\t"
/* Reset CPU */
"str %4, [%1, #" __stringify(AT91_RSTC_CR) "]\n\t"
"b .\n\t"
:
: "r" (at91_ramc_base[0]),
"r" (at91_rstc_base),
"r" (1),
"r" (AT91_SDRAMC_LPCB_POWER_DOWN),
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
}
static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
{
asm volatile(
/*
* Test wether we have a second RAM controller to care
* about.
*
* First, test that we can dereference the virtual address.
*/
"cmp %1, #0\n\t"
"beq 1f\n\t"
/* Then, test that the RAM controller is enabled */
"ldr r0, [%1]\n\t"
"cmp r0, #0\n\t"
/* Align to cache lines */
".balign 32\n\t"
/* Disable SDRAM0 accesses */
"1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
/* Power down SDRAM0 */
" str %4, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
/* Disable SDRAM1 accesses */
" strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
/* Power down SDRAM1 */
" strne %4, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
/* Reset CPU */
" str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t"
" b .\n\t"
:
: "r" (at91_ramc_base[0]),
"r" (at91_ramc_base[1]),
"r" (at91_rstc_base),
"r" (1),
"r" (AT91_DDRSDRC_LPCB_POWER_DOWN),
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)
: "r0");
}
static void __init at91_reset_status(struct platform_device *pdev)
{
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
char *reason;
switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
case RESET_TYPE_GENERAL:
reason = "general reset";
break;
case RESET_TYPE_WAKEUP:
reason = "wakeup";
break;
case RESET_TYPE_WATCHDOG:
reason = "watchdog reset";
break;
case RESET_TYPE_SOFTWARE:
reason = "software reset";
break;
case RESET_TYPE_USER:
reason = "user reset";
break;
default:
reason = "unknown reset";
break;
}
pr_info("AT91: Starting after %s\n", reason);
}
static struct of_device_id at91_ramc_of_match[] = {
{ .compatible = "atmel,at91sam9260-sdramc", },
{ .compatible = "atmel,at91sam9g45-ddramc", },
{ .compatible = "atmel,sama5d3-ddramc", },
{ /* sentinel */ }
};
static struct of_device_id at91_reset_of_match[] = {
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
{ /* sentinel */ }
};
static int at91_reset_of_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device_node *np;
int idx = 0;
at91_rstc_base = of_iomap(pdev->dev.of_node, 0);
if (!at91_rstc_base) {
dev_err(&pdev->dev, "Could not map reset controller address\n");
return -ENODEV;
}
for_each_matching_node(np, at91_ramc_of_match) {
at91_ramc_base[idx] = of_iomap(np, 0);
if (!at91_ramc_base[idx]) {
dev_err(&pdev->dev, "Could not map ram controller address\n");
return -ENODEV;
}
idx++;
}
match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
arm_pm_restart = match->data;
return 0;
}
static int at91_reset_platform_probe(struct platform_device *pdev)
{
const struct platform_device_id *match;
struct resource *res;
int idx = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_rstc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(at91_rstc_base)) {
dev_err(&pdev->dev, "Could not map reset controller address\n");
return PTR_ERR(at91_rstc_base);
}
for (idx = 0; idx < 2; idx++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 );
at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (IS_ERR(at91_ramc_base[idx])) {
dev_err(&pdev->dev, "Could not map ram controller address\n");
return PTR_ERR(at91_ramc_base[idx]);
}
}
match = platform_get_device_id(pdev);
arm_pm_restart = (void (*)(enum reboot_mode, const char*))
match->driver_data;
return 0;
}
static int at91_reset_probe(struct platform_device *pdev)
{
int ret;
if (pdev->dev.of_node)
ret = at91_reset_of_probe(pdev);
else
ret = at91_reset_platform_probe(pdev);
if (ret)
return ret;
at91_reset_status(pdev);
return 0;
}
static struct platform_device_id at91_reset_plat_match[] = {
{ "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
{ "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
{ /* sentinel */ }
};
static struct platform_driver at91_reset_driver = {
.probe = at91_reset_probe,
.driver = {
.name = "at91-reset",
.of_match_table = at91_reset_of_match,
},
.id_table = at91_reset_plat_match,
};
module_platform_driver(at91_reset_driver);
...@@ -1175,9 +1175,16 @@ config RTC_DRV_SUN4V ...@@ -1175,9 +1175,16 @@ config RTC_DRV_SUN4V
If you say Y here you will get support for the Hypervisor If you say Y here you will get support for the Hypervisor
based RTC on SUN4V systems. based RTC on SUN4V systems.
config RTC_DRV_SUN6I
tristate "Allwinner A31 RTC"
depends on MACH_SUN6I || MACH_SUN8I
help
If you say Y here you will get support for the RTC found on
Allwinner A31.
config RTC_DRV_SUNXI config RTC_DRV_SUNXI
tristate "Allwinner sun4i/sun7i RTC" tristate "Allwinner sun4i/sun7i RTC"
depends on ARCH_SUNXI depends on MACH_SUN4I || MACH_SUN7I
help help
If you say Y here you will get support for the RTC found on If you say Y here you will get support for the RTC found on
Allwinner A10/A20. Allwinner A10/A20.
......
...@@ -128,6 +128,7 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o ...@@ -128,6 +128,7 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o
obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o
obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
......
/*
* An RTC driver for Allwinner A31/A23
*
* Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org>
*
* based on rtc-sunxi.c
*
* An RTC driver for Allwinner A10/A20
*
* Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.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 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/delay.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/types.h>
/* Control register */
#define SUN6I_LOSC_CTRL 0x0000
#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
/* RTC */
#define SUN6I_RTC_YMD 0x0010
#define SUN6I_RTC_HMS 0x0014
/* Alarm 0 (counter) */
#define SUN6I_ALRM_COUNTER 0x0020
#define SUN6I_ALRM_CUR_VAL 0x0024
#define SUN6I_ALRM_EN 0x0028
#define SUN6I_ALRM_EN_CNT_EN BIT(0)
#define SUN6I_ALRM_IRQ_EN 0x002c
#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0)
#define SUN6I_ALRM_IRQ_STA 0x0030
#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0)
/* Alarm 1 (wall clock) */
#define SUN6I_ALRM1_EN 0x0044
#define SUN6I_ALRM1_IRQ_EN 0x0048
#define SUN6I_ALRM1_IRQ_STA 0x004c
#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0)
/* Alarm config */
#define SUN6I_ALARM_CONFIG 0x0050
#define SUN6I_ALARM_CONFIG_WAKEUP BIT(0)
/*
* Get date values
*/
#define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f)
#define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8)
#define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16)
#define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22)
/*
* Get time values
*/
#define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f)
#define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8)
#define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16)
/*
* Set date values
*/
#define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f)
#define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00)
#define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000)
#define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000)
/*
* Set time values
*/
#define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f)
#define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00)
#define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000)
/*
* The year parameter passed to the driver is usually an offset relative to
* the year 1900. This macro is used to convert this offset to another one
* relative to the minimum year allowed by the hardware.
*
* The year range is 1970 - 2033. This range is selected to match Allwinner's
* driver, even though it is somewhat limited.
*/
#define SUN6I_YEAR_MIN 1970
#define SUN6I_YEAR_MAX 2033
#define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900)
struct sun6i_rtc_dev {
struct rtc_device *rtc;
struct device *dev;
void __iomem *base;
int irq;
unsigned long alarm;
};
static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
{
struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
u32 val;
val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND;
writel(val, chip->base + SUN6I_ALRM_IRQ_STA);
rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
{
u32 alrm_val = 0;
u32 alrm_irq_val = 0;
u32 alrm_wake_val = 0;
if (to) {
alrm_val = SUN6I_ALRM_EN_CNT_EN;
alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN;
alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP;
} else {
writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
chip->base + SUN6I_ALRM_IRQ_STA);
}
writel(alrm_val, chip->base + SUN6I_ALRM_EN);
writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
}
static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
u32 date, time;
/*
* read again in case it changes
*/
do {
date = readl(chip->base + SUN6I_RTC_YMD);
time = readl(chip->base + SUN6I_RTC_HMS);
} while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
(time != readl(chip->base + SUN6I_RTC_HMS)));
rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time);
rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time);
rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date);
rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
rtc_tm->tm_mon -= 1;
/*
* switch from (data_year->min)-relative offset to
* a (1900)-relative one
*/
rtc_tm->tm_year += SUN6I_YEAR_OFF;
return rtc_valid_tm(rtc_tm);
}
static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
u32 alrm_st;
u32 alrm_en;
alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
rtc_time_to_tm(chip->alarm, &wkalrm->time);
return 0;
}
static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
struct rtc_time *alrm_tm = &wkalrm->time;
struct rtc_time tm_now;
unsigned long time_now = 0;
unsigned long time_set = 0;
unsigned long time_gap = 0;
int ret = 0;
ret = sun6i_rtc_gettime(dev, &tm_now);
if (ret < 0) {
dev_err(dev, "Error in getting time\n");
return -EINVAL;
}
rtc_tm_to_time(alrm_tm, &time_set);
rtc_tm_to_time(&tm_now, &time_now);
if (time_set <= time_now) {
dev_err(dev, "Date to set in the past\n");
return -EINVAL;
}
time_gap = time_set - time_now;
if (time_gap > U32_MAX) {
dev_err(dev, "Date too far in the future\n");
return -EINVAL;
}
sun6i_rtc_setaie(0, chip);
writel(0, chip->base + SUN6I_ALRM_COUNTER);
usleep_range(100, 300);
writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
chip->alarm = time_set;
sun6i_rtc_setaie(wkalrm->enabled, chip);
return 0;
}
static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset,
unsigned int mask, unsigned int ms_timeout)
{
const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
u32 reg;
do {
reg = readl(chip->base + offset);
reg &= mask;
if (!reg)
return 0;
} while (time_before(jiffies, timeout));
return -ETIMEDOUT;
}
static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
{
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
u32 date = 0;
u32 time = 0;
int year;
year = rtc_tm->tm_year + 1900;
if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) {
dev_err(dev, "rtc only supports year in range %d - %d\n",
SUN6I_YEAR_MIN, SUN6I_YEAR_MAX);
return -EINVAL;
}
rtc_tm->tm_year -= SUN6I_YEAR_OFF;
rtc_tm->tm_mon += 1;
date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) |
SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
if (is_leap_year(year))
date |= SUN6I_LEAP_SET_VALUE(1);
time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) |
SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) |
SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
/* Check whether registers are writable */
if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
dev_err(dev, "rtc is still busy.\n");
return -EBUSY;
}
writel(time, chip->base + SUN6I_RTC_HMS);
/*
* After writing the RTC HH-MM-SS register, the
* SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not
* be cleared until the real writing operation is finished
*/
if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) {
dev_err(dev, "Failed to set rtc time.\n");
return -ETIMEDOUT;
}
writel(date, chip->base + SUN6I_RTC_YMD);
/*
* After writing the RTC YY-MM-DD register, the
* SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not
* be cleared until the real writing operation is finished
*/
if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) {
dev_err(dev, "Failed to set rtc time.\n");
return -ETIMEDOUT;
}
return 0;
}
static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
if (!enabled)
sun6i_rtc_setaie(enabled, chip);
return 0;
}
static const struct rtc_class_ops sun6i_rtc_ops = {
.read_time = sun6i_rtc_gettime,
.set_time = sun6i_rtc_settime,
.read_alarm = sun6i_rtc_getalarm,
.set_alarm = sun6i_rtc_setalarm,
.alarm_irq_enable = sun6i_rtc_alarm_irq_enable
};
static int sun6i_rtc_probe(struct platform_device *pdev)
{
struct sun6i_rtc_dev *chip;
struct resource *res;
int ret;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
platform_set_drvdata(pdev, chip);
chip->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
chip->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
chip->irq = platform_get_irq(pdev, 0);
if (chip->irq < 0) {
dev_err(&pdev->dev, "No IRQ resource\n");
return chip->irq;
}
ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq,
0, dev_name(&pdev->dev), chip);
if (ret) {
dev_err(&pdev->dev, "Could not request IRQ\n");
return ret;
}
/* clear the alarm counter value */
writel(0, chip->base + SUN6I_ALRM_COUNTER);
/* disable counter alarm */
writel(0, chip->base + SUN6I_ALRM_EN);
/* disable counter alarm interrupt */
writel(0, chip->base + SUN6I_ALRM_IRQ_EN);
/* disable week alarm */
writel(0, chip->base + SUN6I_ALRM1_EN);
/* disable week alarm interrupt */
writel(0, chip->base + SUN6I_ALRM1_IRQ_EN);
/* clear counter alarm pending interrupts */
writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
chip->base + SUN6I_ALRM_IRQ_STA);
/* clear week alarm pending interrupts */
writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND,
chip->base + SUN6I_ALRM1_IRQ_STA);
/* disable alarm wakeup */
writel(0, chip->base + SUN6I_ALARM_CONFIG);
chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
&sun6i_rtc_ops, THIS_MODULE);
if (IS_ERR(chip->rtc)) {
dev_err(&pdev->dev, "unable to register device\n");
return PTR_ERR(chip->rtc);
}
dev_info(&pdev->dev, "RTC enabled\n");
return 0;
}
static int sun6i_rtc_remove(struct platform_device *pdev)
{
struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev);
rtc_device_unregister(chip->rtc);
return 0;
}
static const struct of_device_id sun6i_rtc_dt_ids[] = {
{ .compatible = "allwinner,sun6i-a31-rtc" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
static struct platform_driver sun6i_rtc_driver = {
.probe = sun6i_rtc_probe,
.remove = sun6i_rtc_remove,
.driver = {
.name = "sun6i-rtc",
.of_match_table = sun6i_rtc_dt_ids,
},
};
module_platform_driver(sun6i_rtc_driver);
MODULE_DESCRIPTION("sun6i RTC driver");
MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
MODULE_LICENSE("GPL");
menu "SOC (System On Chip) specific Drivers" menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/qcom/Kconfig" source "drivers/soc/qcom/Kconfig"
source "drivers/soc/ti/Kconfig"
source "drivers/soc/versatile/Kconfig"
endmenu endmenu
...@@ -4,3 +4,5 @@ ...@@ -4,3 +4,5 @@
obj-$(CONFIG_ARCH_QCOM) += qcom/ obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_SOC_TI) += ti/
obj-$(CONFIG_PLAT_VERSATILE) += versatile/
#
# TI SOC drivers
#
menuconfig SOC_TI
bool "TI SOC drivers support"
if SOC_TI
config KEYSTONE_NAVIGATOR_QMSS
tristate "Keystone Queue Manager Sub System"
depends on ARCH_KEYSTONE
help
Say y here to support the Keystone multicore Navigator Queue
Manager support. The Queue Manager is a hardware module that
is responsible for accelerating management of the packet queues.
Packets are queued/de-queued by writing/reading descriptor address
to a particular memory mapped location in the Queue Manager module.
If unsure, say N.
config KEYSTONE_NAVIGATOR_DMA
tristate "TI Keystone Navigator Packet DMA support"
depends on ARCH_KEYSTONE
help
Say y tp enable support for the Keystone Navigator Packet DMA on
on Keystone family of devices. It sets up the dma channels for the
Queue Manager Sub System.
If unsure, say N.
endif # SOC_TI
#
# TI Keystone SOC drivers
#
obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss_queue.o knav_qmss_acc.o
obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o
/*
* Copyright (C) 2014 Texas Instruments Incorporated
* Authors: Santosh Shilimkar <santosh.shilimkar@ti.com>
* Sandeep Nair <sandeep_n@ti.com>
* Cyril Chemparathy <cyril@ti.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 Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/dma-direction.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/of_dma.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/soc/ti/knav_dma.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#define REG_MASK 0xffffffff
#define DMA_LOOPBACK BIT(31)
#define DMA_ENABLE BIT(31)
#define DMA_TEARDOWN BIT(30)
#define DMA_TX_FILT_PSWORDS BIT(29)
#define DMA_TX_FILT_EINFO BIT(30)
#define DMA_TX_PRIO_SHIFT 0
#define DMA_RX_PRIO_SHIFT 16
#define DMA_PRIO_MASK GENMASK(3, 0)
#define DMA_PRIO_DEFAULT 0
#define DMA_RX_TIMEOUT_DEFAULT 17500 /* cycles */
#define DMA_RX_TIMEOUT_MASK GENMASK(16, 0)
#define DMA_RX_TIMEOUT_SHIFT 0
#define CHAN_HAS_EPIB BIT(30)
#define CHAN_HAS_PSINFO BIT(29)
#define CHAN_ERR_RETRY BIT(28)
#define CHAN_PSINFO_AT_SOP BIT(25)
#define CHAN_SOP_OFF_SHIFT 16
#define CHAN_SOP_OFF_MASK GENMASK(9, 0)
#define DESC_TYPE_SHIFT 26
#define DESC_TYPE_MASK GENMASK(2, 0)
/*
* QMGR & QNUM together make up 14 bits with QMGR as the 2 MSb's in the logical
* navigator cloud mapping scheme.
* using the 14bit physical queue numbers directly maps into this scheme.
*/
#define CHAN_QNUM_MASK GENMASK(14, 0)
#define DMA_MAX_QMS 4
#define DMA_TIMEOUT 1 /* msecs */
#define DMA_INVALID_ID 0xffff
struct reg_global {
u32 revision;
u32 perf_control;
u32 emulation_control;
u32 priority_control;
u32 qm_base_address[DMA_MAX_QMS];
};
struct reg_chan {
u32 control;
u32 mode;
u32 __rsvd[6];
};
struct reg_tx_sched {
u32 prio;
};
struct reg_rx_flow {
u32 control;
u32 tags;
u32 tag_sel;
u32 fdq_sel[2];
u32 thresh[3];
};
struct knav_dma_pool_device {
struct device *dev;
struct list_head list;
};
struct knav_dma_device {
bool loopback, enable_all;
unsigned tx_priority, rx_priority, rx_timeout;
unsigned logical_queue_managers;
unsigned qm_base_address[DMA_MAX_QMS];
struct reg_global __iomem *reg_global;
struct reg_chan __iomem *reg_tx_chan;
struct reg_rx_flow __iomem *reg_rx_flow;
struct reg_chan __iomem *reg_rx_chan;
struct reg_tx_sched __iomem *reg_tx_sched;
unsigned max_rx_chan, max_tx_chan;
unsigned max_rx_flow;
char name[32];
atomic_t ref_count;
struct list_head list;
struct list_head chan_list;
spinlock_t lock;
};
struct knav_dma_chan {
enum dma_transfer_direction direction;
struct knav_dma_device *dma;
atomic_t ref_count;
/* registers */
struct reg_chan __iomem *reg_chan;
struct reg_tx_sched __iomem *reg_tx_sched;
struct reg_rx_flow __iomem *reg_rx_flow;
/* configuration stuff */
unsigned channel, flow;
struct knav_dma_cfg cfg;
struct list_head list;
spinlock_t lock;
};
#define chan_number(ch) ((ch->direction == DMA_MEM_TO_DEV) ? \
ch->channel : ch->flow)
static struct knav_dma_pool_device *kdev;
static bool check_config(struct knav_dma_chan *chan, struct knav_dma_cfg *cfg)
{
if (!memcmp(&chan->cfg, cfg, sizeof(*cfg)))
return true;
else
return false;
}
static int chan_start(struct knav_dma_chan *chan,
struct knav_dma_cfg *cfg)
{
u32 v = 0;
spin_lock(&chan->lock);
if ((chan->direction == DMA_MEM_TO_DEV) && chan->reg_chan) {
if (cfg->u.tx.filt_pswords)
v |= DMA_TX_FILT_PSWORDS;
if (cfg->u.tx.filt_einfo)
v |= DMA_TX_FILT_EINFO;
writel_relaxed(v, &chan->reg_chan->mode);
writel_relaxed(DMA_ENABLE, &chan->reg_chan->control);
}
if (chan->reg_tx_sched)
writel_relaxed(cfg->u.tx.priority, &chan->reg_tx_sched->prio);
if (chan->reg_rx_flow) {
v = 0;
if (cfg->u.rx.einfo_present)
v |= CHAN_HAS_EPIB;
if (cfg->u.rx.psinfo_present)
v |= CHAN_HAS_PSINFO;
if (cfg->u.rx.err_mode == DMA_RETRY)
v |= CHAN_ERR_RETRY;
v |= (cfg->u.rx.desc_type & DESC_TYPE_MASK) << DESC_TYPE_SHIFT;
if (cfg->u.rx.psinfo_at_sop)
v |= CHAN_PSINFO_AT_SOP;
v |= (cfg->u.rx.sop_offset & CHAN_SOP_OFF_MASK)
<< CHAN_SOP_OFF_SHIFT;
v |= cfg->u.rx.dst_q & CHAN_QNUM_MASK;
writel_relaxed(v, &chan->reg_rx_flow->control);
writel_relaxed(0, &chan->reg_rx_flow->tags);
writel_relaxed(0, &chan->reg_rx_flow->tag_sel);
v = cfg->u.rx.fdq[0] << 16;
v |= cfg->u.rx.fdq[1] & CHAN_QNUM_MASK;
writel_relaxed(v, &chan->reg_rx_flow->fdq_sel[0]);
v = cfg->u.rx.fdq[2] << 16;
v |= cfg->u.rx.fdq[3] & CHAN_QNUM_MASK;
writel_relaxed(v, &chan->reg_rx_flow->fdq_sel[1]);
writel_relaxed(0, &chan->reg_rx_flow->thresh[0]);
writel_relaxed(0, &chan->reg_rx_flow->thresh[1]);
writel_relaxed(0, &chan->reg_rx_flow->thresh[2]);
}
/* Keep a copy of the cfg */
memcpy(&chan->cfg, cfg, sizeof(*cfg));
spin_unlock(&chan->lock);
return 0;
}
static int chan_teardown(struct knav_dma_chan *chan)
{
unsigned long end, value;
if (!chan->reg_chan)
return 0;
/* indicate teardown */
writel_relaxed(DMA_TEARDOWN, &chan->reg_chan->control);
/* wait for the dma to shut itself down */
end = jiffies + msecs_to_jiffies(DMA_TIMEOUT);
do {
value = readl_relaxed(&chan->reg_chan->control);
if ((value & DMA_ENABLE) == 0)
break;
} while (time_after(end, jiffies));
if (readl_relaxed(&chan->reg_chan->control) & DMA_ENABLE) {
dev_err(kdev->dev, "timeout waiting for teardown\n");
return -ETIMEDOUT;
}
return 0;
}
static void chan_stop(struct knav_dma_chan *chan)
{
spin_lock(&chan->lock);
if (chan->reg_rx_flow) {
/* first detach fdqs, starve out the flow */
writel_relaxed(0, &chan->reg_rx_flow->fdq_sel[0]);
writel_relaxed(0, &chan->reg_rx_flow->fdq_sel[1]);
writel_relaxed(0, &chan->reg_rx_flow->thresh[0]);
writel_relaxed(0, &chan->reg_rx_flow->thresh[1]);
writel_relaxed(0, &chan->reg_rx_flow->thresh[2]);
}
/* teardown the dma channel */
chan_teardown(chan);
/* then disconnect the completion side */
if (chan->reg_rx_flow) {
writel_relaxed(0, &chan->reg_rx_flow->control);
writel_relaxed(0, &chan->reg_rx_flow->tags);
writel_relaxed(0, &chan->reg_rx_flow->tag_sel);
}
memset(&chan->cfg, 0, sizeof(struct knav_dma_cfg));
spin_unlock(&chan->lock);
dev_dbg(kdev->dev, "channel stopped\n");
}
static void dma_hw_enable_all(struct knav_dma_device *dma)
{
int i;
for (i = 0; i < dma->max_tx_chan; i++) {
writel_relaxed(0, &dma->reg_tx_chan[i].mode);
writel_relaxed(DMA_ENABLE, &dma->reg_tx_chan[i].control);
}
}
static void knav_dma_hw_init(struct knav_dma_device *dma)
{
unsigned v;
int i;
spin_lock(&dma->lock);
v = dma->loopback ? DMA_LOOPBACK : 0;
writel_relaxed(v, &dma->reg_global->emulation_control);
v = readl_relaxed(&dma->reg_global->perf_control);
v |= ((dma->rx_timeout & DMA_RX_TIMEOUT_MASK) << DMA_RX_TIMEOUT_SHIFT);
writel_relaxed(v, &dma->reg_global->perf_control);
v = ((dma->tx_priority << DMA_TX_PRIO_SHIFT) |
(dma->rx_priority << DMA_RX_PRIO_SHIFT));
writel_relaxed(v, &dma->reg_global->priority_control);
/* Always enable all Rx channels. Rx paths are managed using flows */
for (i = 0; i < dma->max_rx_chan; i++)
writel_relaxed(DMA_ENABLE, &dma->reg_rx_chan[i].control);
for (i = 0; i < dma->logical_queue_managers; i++)
writel_relaxed(dma->qm_base_address[i],
&dma->reg_global->qm_base_address[i]);
spin_unlock(&dma->lock);
}
static void knav_dma_hw_destroy(struct knav_dma_device *dma)
{
int i;
unsigned v;
spin_lock(&dma->lock);
v = ~DMA_ENABLE & REG_MASK;
for (i = 0; i < dma->max_rx_chan; i++)
writel_relaxed(v, &dma->reg_rx_chan[i].control);
for (i = 0; i < dma->max_tx_chan; i++)
writel_relaxed(v, &dma->reg_tx_chan[i].control);
spin_unlock(&dma->lock);
}
static void dma_debug_show_channels(struct seq_file *s,
struct knav_dma_chan *chan)
{
int i;
seq_printf(s, "\t%s %d:\t",
((chan->direction == DMA_MEM_TO_DEV) ? "tx chan" : "rx flow"),
chan_number(chan));
if (chan->direction == DMA_MEM_TO_DEV) {
seq_printf(s, "einfo - %d, pswords - %d, priority - %d\n",
chan->cfg.u.tx.filt_einfo,
chan->cfg.u.tx.filt_pswords,
chan->cfg.u.tx.priority);
} else {
seq_printf(s, "einfo - %d, psinfo - %d, desc_type - %d\n",
chan->cfg.u.rx.einfo_present,
chan->cfg.u.rx.psinfo_present,
chan->cfg.u.rx.desc_type);
seq_printf(s, "\t\t\tdst_q: [%d], thresh: %d fdq: ",
chan->cfg.u.rx.dst_q,
chan->cfg.u.rx.thresh);
for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN; i++)
seq_printf(s, "[%d]", chan->cfg.u.rx.fdq[i]);
seq_printf(s, "\n");
}
}
static void dma_debug_show_devices(struct seq_file *s,
struct knav_dma_device *dma)
{
struct knav_dma_chan *chan;
list_for_each_entry(chan, &dma->chan_list, list) {
if (atomic_read(&chan->ref_count))
dma_debug_show_channels(s, chan);
}
}
static int dma_debug_show(struct seq_file *s, void *v)
{
struct knav_dma_device *dma;
list_for_each_entry(dma, &kdev->list, list) {
if (atomic_read(&dma->ref_count)) {
seq_printf(s, "%s : max_tx_chan: (%d), max_rx_flows: (%d)\n",
dma->name, dma->max_tx_chan, dma->max_rx_flow);
dma_debug_show_devices(s, dma);
}
}
return 0;
}
static int knav_dma_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, dma_debug_show, NULL);
}
static const struct file_operations knav_dma_debug_ops = {
.open = knav_dma_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int of_channel_match_helper(struct device_node *np, const char *name,
const char **dma_instance)
{
struct of_phandle_args args;
struct device_node *dma_node;
int index;
dma_node = of_parse_phandle(np, "ti,navigator-dmas", 0);
if (!dma_node)
return -ENODEV;
*dma_instance = dma_node->name;
index = of_property_match_string(np, "ti,navigator-dma-names", name);
if (index < 0) {
dev_err(kdev->dev, "No 'ti,navigator-dma-names' propery\n");
return -ENODEV;
}
if (of_parse_phandle_with_fixed_args(np, "ti,navigator-dmas",
1, index, &args)) {
dev_err(kdev->dev, "Missing the pahndle args name %s\n", name);
return -ENODEV;
}
if (args.args[0] < 0) {
dev_err(kdev->dev, "Missing args for %s\n", name);
return -ENODEV;
}
return args.args[0];
}
/**
* knav_dma_open_channel() - try to setup an exclusive slave channel
* @dev: pointer to client device structure
* @name: slave channel name
* @config: dma configuration parameters
*
* Returns pointer to appropriate DMA channel on success or NULL.
*/
void *knav_dma_open_channel(struct device *dev, const char *name,
struct knav_dma_cfg *config)
{
struct knav_dma_chan *chan;
struct knav_dma_device *dma;
bool found = false;
int chan_num = -1;
const char *instance;
if (!kdev) {
pr_err("keystone-navigator-dma driver not registered\n");
return (void *)-EINVAL;
}
chan_num = of_channel_match_helper(dev->of_node, name, &instance);
if (chan_num < 0) {
dev_err(kdev->dev, "No DMA instace with name %s\n", name);
return (void *)-EINVAL;
}
dev_dbg(kdev->dev, "initializing %s channel %d from DMA %s\n",
config->direction == DMA_MEM_TO_DEV ? "transmit" :
config->direction == DMA_DEV_TO_MEM ? "receive" :
"unknown", chan_num, instance);
if (config->direction != DMA_MEM_TO_DEV &&
config->direction != DMA_DEV_TO_MEM) {
dev_err(kdev->dev, "bad direction\n");
return (void *)-EINVAL;
}
/* Look for correct dma instance */
list_for_each_entry(dma, &kdev->list, list) {
if (!strcmp(dma->name, instance)) {
found = true;
break;
}
}
if (!found) {
dev_err(kdev->dev, "No DMA instace with name %s\n", instance);
return (void *)-EINVAL;
}
/* Look for correct dma channel from dma instance */
found = false;
list_for_each_entry(chan, &dma->chan_list, list) {
if (config->direction == DMA_MEM_TO_DEV) {
if (chan->channel == chan_num) {
found = true;
break;
}
} else {
if (chan->flow == chan_num) {
found = true;
break;
}
}
}
if (!found) {
dev_err(kdev->dev, "channel %d is not in DMA %s\n",
chan_num, instance);
return (void *)-EINVAL;
}
if (atomic_read(&chan->ref_count) >= 1) {
if (!check_config(chan, config)) {
dev_err(kdev->dev, "channel %d config miss-match\n",
chan_num);
return (void *)-EINVAL;
}
}
if (atomic_inc_return(&chan->dma->ref_count) <= 1)
knav_dma_hw_init(chan->dma);
if (atomic_inc_return(&chan->ref_count) <= 1)
chan_start(chan, config);
dev_dbg(kdev->dev, "channel %d opened from DMA %s\n",
chan_num, instance);
return chan;
}
EXPORT_SYMBOL_GPL(knav_dma_open_channel);
/**
* knav_dma_close_channel() - Destroy a dma channel
*
* channel: dma channel handle
*
*/
void knav_dma_close_channel(void *channel)
{
struct knav_dma_chan *chan = channel;
if (!kdev) {
pr_err("keystone-navigator-dma driver not registered\n");
return;
}
if (atomic_dec_return(&chan->ref_count) <= 0)
chan_stop(chan);
if (atomic_dec_return(&chan->dma->ref_count) <= 0)
knav_dma_hw_destroy(chan->dma);
dev_dbg(kdev->dev, "channel %d or flow %d closed from DMA %s\n",
chan->channel, chan->flow, chan->dma->name);
}
EXPORT_SYMBOL_GPL(knav_dma_close_channel);
static void __iomem *pktdma_get_regs(struct knav_dma_device *dma,
struct device_node *node,
unsigned index, resource_size_t *_size)
{
struct device *dev = kdev->dev;
struct resource res;
void __iomem *regs;
int ret;
ret = of_address_to_resource(node, index, &res);
if (ret) {
dev_err(dev, "Can't translate of node(%s) address for index(%d)\n",
node->name, index);
return ERR_PTR(ret);
}
regs = devm_ioremap_resource(kdev->dev, &res);
if (IS_ERR(regs))
dev_err(dev, "Failed to map register base for index(%d) node(%s)\n",
index, node->name);
if (_size)
*_size = resource_size(&res);
return regs;
}
static int pktdma_init_rx_chan(struct knav_dma_chan *chan, u32 flow)
{
struct knav_dma_device *dma = chan->dma;
chan->flow = flow;
chan->reg_rx_flow = dma->reg_rx_flow + flow;
chan->channel = DMA_INVALID_ID;
dev_dbg(kdev->dev, "rx flow(%d) (%p)\n", chan->flow, chan->reg_rx_flow);
return 0;
}
static int pktdma_init_tx_chan(struct knav_dma_chan *chan, u32 channel)
{
struct knav_dma_device *dma = chan->dma;
chan->channel = channel;
chan->reg_chan = dma->reg_tx_chan + channel;
chan->reg_tx_sched = dma->reg_tx_sched + channel;
chan->flow = DMA_INVALID_ID;
dev_dbg(kdev->dev, "tx channel(%d) (%p)\n", chan->channel, chan->reg_chan);
return 0;
}
static int pktdma_init_chan(struct knav_dma_device *dma,
enum dma_transfer_direction dir,
unsigned chan_num)
{
struct device *dev = kdev->dev;
struct knav_dma_chan *chan;
int ret = -EINVAL;
chan = devm_kzalloc(dev, sizeof(*chan), GFP_KERNEL);
if (!chan)
return -ENOMEM;
INIT_LIST_HEAD(&chan->list);
chan->dma = dma;
chan->direction = DMA_NONE;
atomic_set(&chan->ref_count, 0);
spin_lock_init(&chan->lock);
if (dir == DMA_MEM_TO_DEV) {
chan->direction = dir;
ret = pktdma_init_tx_chan(chan, chan_num);
} else if (dir == DMA_DEV_TO_MEM) {
chan->direction = dir;
ret = pktdma_init_rx_chan(chan, chan_num);
} else {
dev_err(dev, "channel(%d) direction unknown\n", chan_num);
}
list_add_tail(&chan->list, &dma->chan_list);
return ret;
}
static int dma_init(struct device_node *cloud, struct device_node *dma_node)
{
unsigned max_tx_chan, max_rx_chan, max_rx_flow, max_tx_sched;
struct device_node *node = dma_node;
struct knav_dma_device *dma;
int ret, len, num_chan = 0;
resource_size_t size;
u32 timeout;
u32 i;
dma = devm_kzalloc(kdev->dev, sizeof(*dma), GFP_KERNEL);
if (!dma) {
dev_err(kdev->dev, "could not allocate driver mem\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&dma->list);
INIT_LIST_HEAD(&dma->chan_list);
if (!of_find_property(cloud, "ti,navigator-cloud-address", &len)) {
dev_err(kdev->dev, "unspecified navigator cloud addresses\n");
return -ENODEV;
}
dma->logical_queue_managers = len / sizeof(u32);
if (dma->logical_queue_managers > DMA_MAX_QMS) {
dev_warn(kdev->dev, "too many queue mgrs(>%d) rest ignored\n",
dma->logical_queue_managers);
dma->logical_queue_managers = DMA_MAX_QMS;
}
ret = of_property_read_u32_array(cloud, "ti,navigator-cloud-address",
dma->qm_base_address,
dma->logical_queue_managers);
if (ret) {
dev_err(kdev->dev, "invalid navigator cloud addresses\n");
return -ENODEV;
}
dma->reg_global = pktdma_get_regs(dma, node, 0, &size);
if (!dma->reg_global)
return -ENODEV;
if (size < sizeof(struct reg_global)) {
dev_err(kdev->dev, "bad size %pa for global regs\n", &size);
return -ENODEV;
}
dma->reg_tx_chan = pktdma_get_regs(dma, node, 1, &size);
if (!dma->reg_tx_chan)
return -ENODEV;
max_tx_chan = size / sizeof(struct reg_chan);
dma->reg_rx_chan = pktdma_get_regs(dma, node, 2, &size);
if (!dma->reg_rx_chan)
return -ENODEV;
max_rx_chan = size / sizeof(struct reg_chan);
dma->reg_tx_sched = pktdma_get_regs(dma, node, 3, &size);
if (!dma->reg_tx_sched)
return -ENODEV;
max_tx_sched = size / sizeof(struct reg_tx_sched);
dma->reg_rx_flow = pktdma_get_regs(dma, node, 4, &size);
if (!dma->reg_rx_flow)
return -ENODEV;
max_rx_flow = size / sizeof(struct reg_rx_flow);
dma->rx_priority = DMA_PRIO_DEFAULT;
dma->tx_priority = DMA_PRIO_DEFAULT;
dma->enable_all = (of_get_property(node, "ti,enable-all", NULL) != NULL);
dma->loopback = (of_get_property(node, "ti,loop-back", NULL) != NULL);
ret = of_property_read_u32(node, "ti,rx-retry-timeout", &timeout);
if (ret < 0) {
dev_dbg(kdev->dev, "unspecified rx timeout using value %d\n",
DMA_RX_TIMEOUT_DEFAULT);
timeout = DMA_RX_TIMEOUT_DEFAULT;
}
dma->rx_timeout = timeout;
dma->max_rx_chan = max_rx_chan;
dma->max_rx_flow = max_rx_flow;
dma->max_tx_chan = min(max_tx_chan, max_tx_sched);
atomic_set(&dma->ref_count, 0);
strcpy(dma->name, node->name);
spin_lock_init(&dma->lock);
for (i = 0; i < dma->max_tx_chan; i++) {
if (pktdma_init_chan(dma, DMA_MEM_TO_DEV, i) >= 0)
num_chan++;
}
for (i = 0; i < dma->max_rx_flow; i++) {
if (pktdma_init_chan(dma, DMA_DEV_TO_MEM, i) >= 0)
num_chan++;
}
list_add_tail(&dma->list, &kdev->list);
/*
* For DSP software usecases or userpace transport software, setup all
* the DMA hardware resources.
*/
if (dma->enable_all) {
atomic_inc(&dma->ref_count);
knav_dma_hw_init(dma);
dma_hw_enable_all(dma);
}
dev_info(kdev->dev, "DMA %s registered %d logical channels, flows %d, tx chans: %d, rx chans: %d%s\n",
dma->name, num_chan, dma->max_rx_flow,
dma->max_tx_chan, dma->max_rx_chan,
dma->loopback ? ", loopback" : "");
return 0;
}
static int knav_dma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct device_node *child;
int ret = 0;
if (!node) {
dev_err(&pdev->dev, "could not find device info\n");
return -EINVAL;
}
kdev = devm_kzalloc(dev,
sizeof(struct knav_dma_pool_device), GFP_KERNEL);
if (!kdev) {
dev_err(dev, "could not allocate driver mem\n");
return -ENOMEM;
}
kdev->dev = dev;
INIT_LIST_HEAD(&kdev->list);
pm_runtime_enable(kdev->dev);
ret = pm_runtime_get_sync(kdev->dev);
if (ret < 0) {
dev_err(kdev->dev, "unable to enable pktdma, err %d\n", ret);
return ret;
}
/* Initialise all packet dmas */
for_each_child_of_node(node, child) {
ret = dma_init(node, child);
if (ret) {
dev_err(&pdev->dev, "init failed with %d\n", ret);
break;
}
}
if (list_empty(&kdev->list)) {
dev_err(dev, "no valid dma instance\n");
return -ENODEV;
}
debugfs_create_file("knav_dma", S_IFREG | S_IRUGO, NULL, NULL,
&knav_dma_debug_ops);
return ret;
}
static int knav_dma_remove(struct platform_device *pdev)
{
struct knav_dma_device *dma;
list_for_each_entry(dma, &kdev->list, list) {
if (atomic_dec_return(&dma->ref_count) == 0)
knav_dma_hw_destroy(dma);
}
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
static struct of_device_id of_match[] = {
{ .compatible = "ti,keystone-navigator-dma", },
{},
};
MODULE_DEVICE_TABLE(of, of_match);
static struct platform_driver knav_dma_driver = {
.probe = knav_dma_probe,
.remove = knav_dma_remove,
.driver = {
.name = "keystone-navigator-dma",
.owner = THIS_MODULE,
.of_match_table = of_match,
},
};
module_platform_driver(knav_dma_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("TI Keystone Navigator Packet DMA driver");
MODULE_AUTHOR("Sandeep Nair <sandeep_n@ti.com>");
MODULE_AUTHOR("Santosh Shilimkar <santosh.shilimkar@ti.com>");
/*
* Keystone Navigator QMSS driver internal header
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
* Author: Sandeep Nair <sandeep_n@ti.com>
* Cyril Chemparathy <cyril@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#ifndef __KNAV_QMSS_H__
#define __KNAV_QMSS_H__
#define THRESH_GTE BIT(7)
#define THRESH_LT 0
#define PDSP_CTRL_PC_MASK 0xffff0000
#define PDSP_CTRL_SOFT_RESET BIT(0)
#define PDSP_CTRL_ENABLE BIT(1)
#define PDSP_CTRL_RUNNING BIT(15)
#define ACC_MAX_CHANNEL 48
#define ACC_DEFAULT_PERIOD 25 /* usecs */
#define ACC_CHANNEL_INT_BASE 2
#define ACC_LIST_ENTRY_TYPE 1
#define ACC_LIST_ENTRY_WORDS (1 << ACC_LIST_ENTRY_TYPE)
#define ACC_LIST_ENTRY_QUEUE_IDX 0
#define ACC_LIST_ENTRY_DESC_IDX (ACC_LIST_ENTRY_WORDS - 1)
#define ACC_CMD_DISABLE_CHANNEL 0x80
#define ACC_CMD_ENABLE_CHANNEL 0x81
#define ACC_CFG_MULTI_QUEUE BIT(21)
#define ACC_INTD_OFFSET_EOI (0x0010)
#define ACC_INTD_OFFSET_COUNT(ch) (0x0300 + 4 * (ch))
#define ACC_INTD_OFFSET_STATUS(ch) (0x0200 + 4 * ((ch) / 32))
#define RANGE_MAX_IRQS 64
#define ACC_DESCS_MAX SZ_1K
#define ACC_DESCS_MASK (ACC_DESCS_MAX - 1)
#define DESC_SIZE_MASK 0xful
#define DESC_PTR_MASK (~DESC_SIZE_MASK)
#define KNAV_NAME_SIZE 32
enum knav_acc_result {
ACC_RET_IDLE,
ACC_RET_SUCCESS,
ACC_RET_INVALID_COMMAND,
ACC_RET_INVALID_CHANNEL,
ACC_RET_INACTIVE_CHANNEL,
ACC_RET_ACTIVE_CHANNEL,
ACC_RET_INVALID_QUEUE,
ACC_RET_INVALID_RET,
};
struct knav_reg_config {
u32 revision;
u32 __pad1;
u32 divert;
u32 link_ram_base0;
u32 link_ram_size0;
u32 link_ram_base1;
u32 __pad2[2];
u32 starvation[0];
};
struct knav_reg_region {
u32 base;
u32 start_index;
u32 size_count;
u32 __pad;
};
struct knav_reg_pdsp_regs {
u32 control;
u32 status;
u32 cycle_count;
u32 stall_count;
};
struct knav_reg_acc_command {
u32 command;
u32 queue_mask;
u32 list_phys;
u32 queue_num;
u32 timer_config;
};
struct knav_link_ram_block {
dma_addr_t phys;
void *virt;
size_t size;
};
struct knav_acc_info {
u32 pdsp_id;
u32 start_channel;
u32 list_entries;
u32 pacing_mode;
u32 timer_count;
int mem_size;
int list_size;
struct knav_pdsp_info *pdsp;
};
struct knav_acc_channel {
u32 channel;
u32 list_index;
u32 open_mask;
u32 *list_cpu[2];
dma_addr_t list_dma[2];
char name[KNAV_NAME_SIZE];
atomic_t retrigger_count;
};
struct knav_pdsp_info {
const char *name;
struct knav_reg_pdsp_regs __iomem *regs;
union {
void __iomem *command;
struct knav_reg_acc_command __iomem *acc_command;
u32 __iomem *qos_command;
};
void __iomem *intd;
u32 __iomem *iram;
const char *firmware;
u32 id;
struct list_head list;
};
struct knav_qmgr_info {
unsigned start_queue;
unsigned num_queues;
struct knav_reg_config __iomem *reg_config;
struct knav_reg_region __iomem *reg_region;
struct knav_reg_queue __iomem *reg_push, *reg_pop, *reg_peek;
void __iomem *reg_status;
struct list_head list;
};
#define KNAV_NUM_LINKRAM 2
/**
* struct knav_queue_stats: queue statistics
* pushes: number of push operations
* pops: number of pop operations
* push_errors: number of push errors
* pop_errors: number of pop errors
* notifies: notifier counts
*/
struct knav_queue_stats {
atomic_t pushes;
atomic_t pops;
atomic_t push_errors;
atomic_t pop_errors;
atomic_t notifies;
};
/**
* struct knav_reg_queue: queue registers
* @entry_count: valid entries in the queue
* @byte_count: total byte count in thhe queue
* @packet_size: packet size for the queue
* @ptr_size_thresh: packet pointer size threshold
*/
struct knav_reg_queue {
u32 entry_count;
u32 byte_count;
u32 packet_size;
u32 ptr_size_thresh;
};
/**
* struct knav_region: qmss region info
* @dma_start, dma_end: start and end dma address
* @virt_start, virt_end: start and end virtual address
* @desc_size: descriptor size
* @used_desc: consumed descriptors
* @id: region number
* @num_desc: total descriptors
* @link_index: index of the first descriptor
* @name: region name
* @list: instance in the device's region list
* @pools: list of descriptor pools in the region
*/
struct knav_region {
dma_addr_t dma_start, dma_end;
void *virt_start, *virt_end;
unsigned desc_size;
unsigned used_desc;
unsigned id;
unsigned num_desc;
unsigned link_index;
const char *name;
struct list_head list;
struct list_head pools;
};
/**
* struct knav_pool: qmss pools
* @dev: device pointer
* @region: qmss region info
* @queue: queue registers
* @kdev: qmss device pointer
* @region_offset: offset from the base
* @num_desc: total descriptors
* @desc_size: descriptor size
* @region_id: region number
* @name: pool name
* @list: list head
* @region_inst: instance in the region's pool list
*/
struct knav_pool {
struct device *dev;
struct knav_region *region;
struct knav_queue *queue;
struct knav_device *kdev;
int region_offset;
int num_desc;
int desc_size;
int region_id;
const char *name;
struct list_head list;
struct list_head region_inst;
};
/**
* struct knav_queue_inst: qmss queue instace properties
* @descs: descriptor pointer
* @desc_head, desc_tail, desc_count: descriptor counters
* @acc: accumulator channel pointer
* @kdev: qmss device pointer
* @range: range info
* @qmgr: queue manager info
* @id: queue instace id
* @irq_num: irq line number
* @notify_needed: notifier needed based on queue type
* @num_notifiers: total notifiers
* @handles: list head
* @name: queue instance name
* @irq_name: irq line name
*/
struct knav_queue_inst {
u32 *descs;
atomic_t desc_head, desc_tail, desc_count;
struct knav_acc_channel *acc;
struct knav_device *kdev;
struct knav_range_info *range;
struct knav_qmgr_info *qmgr;
u32 id;
int irq_num;
int notify_needed;
atomic_t num_notifiers;
struct list_head handles;
const char *name;
const char *irq_name;
};
/**
* struct knav_queue: qmss queue properties
* @reg_push, reg_pop, reg_peek: push, pop queue registers
* @inst: qmss queue instace properties
* @notifier_fn: notifier function
* @notifier_fn_arg: notifier function argument
* @notifier_enabled: notier enabled for a give queue
* @rcu: rcu head
* @flags: queue flags
* @list: list head
*/
struct knav_queue {
struct knav_reg_queue __iomem *reg_push, *reg_pop, *reg_peek;
struct knav_queue_inst *inst;
struct knav_queue_stats stats;
knav_queue_notify_fn notifier_fn;
void *notifier_fn_arg;
atomic_t notifier_enabled;
struct rcu_head rcu;
unsigned flags;
struct list_head list;
};
struct knav_device {
struct device *dev;
unsigned base_id;
unsigned num_queues;
unsigned num_queues_in_use;
unsigned inst_shift;
struct knav_link_ram_block link_rams[KNAV_NUM_LINKRAM];
void *instances;
struct list_head regions;
struct list_head queue_ranges;
struct list_head pools;
struct list_head pdsps;
struct list_head qmgrs;
};
struct knav_range_ops {
int (*init_range)(struct knav_range_info *range);
int (*free_range)(struct knav_range_info *range);
int (*init_queue)(struct knav_range_info *range,
struct knav_queue_inst *inst);
int (*open_queue)(struct knav_range_info *range,
struct knav_queue_inst *inst, unsigned flags);
int (*close_queue)(struct knav_range_info *range,
struct knav_queue_inst *inst);
int (*set_notify)(struct knav_range_info *range,
struct knav_queue_inst *inst, bool enabled);
};
struct knav_irq_info {
int irq;
u32 cpu_map;
};
struct knav_range_info {
const char *name;
struct knav_device *kdev;
unsigned queue_base;
unsigned num_queues;
void *queue_base_inst;
unsigned flags;
struct list_head list;
struct knav_range_ops *ops;
struct knav_acc_info acc_info;
struct knav_acc_channel *acc;
unsigned num_irqs;
struct knav_irq_info irqs[RANGE_MAX_IRQS];
};
#define RANGE_RESERVED BIT(0)
#define RANGE_HAS_IRQ BIT(1)
#define RANGE_HAS_ACCUMULATOR BIT(2)
#define RANGE_MULTI_QUEUE BIT(3)
#define for_each_region(kdev, region) \
list_for_each_entry(region, &kdev->regions, list)
#define first_region(kdev) \
list_first_entry(&kdev->regions, \
struct knav_region, list)
#define for_each_queue_range(kdev, range) \
list_for_each_entry(range, &kdev->queue_ranges, list)
#define first_queue_range(kdev) \
list_first_entry(&kdev->queue_ranges, \
struct knav_range_info, list)
#define for_each_pool(kdev, pool) \
list_for_each_entry(pool, &kdev->pools, list)
#define for_each_pdsp(kdev, pdsp) \
list_for_each_entry(pdsp, &kdev->pdsps, list)
#define for_each_qmgr(kdev, qmgr) \
list_for_each_entry(qmgr, &kdev->qmgrs, list)
static inline struct knav_pdsp_info *
knav_find_pdsp(struct knav_device *kdev, unsigned pdsp_id)
{
struct knav_pdsp_info *pdsp;
for_each_pdsp(kdev, pdsp)
if (pdsp_id == pdsp->id)
return pdsp;
return NULL;
}
extern int knav_init_acc_range(struct knav_device *kdev,
struct device_node *node,
struct knav_range_info *range);
extern void knav_queue_notify(struct knav_queue_inst *inst);
#endif /* __KNAV_QMSS_H__ */
/*
* Keystone accumulator queue manager
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
* Author: Sandeep Nair <sandeep_n@ti.com>
* Cyril Chemparathy <cyril@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/soc/ti/knav_qmss.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/firmware.h>
#include "knav_qmss.h"
#define knav_range_offset_to_inst(kdev, range, q) \
(range->queue_base_inst + (q << kdev->inst_shift))
static void __knav_acc_notify(struct knav_range_info *range,
struct knav_acc_channel *acc)
{
struct knav_device *kdev = range->kdev;
struct knav_queue_inst *inst;
int range_base, queue;
range_base = kdev->base_id + range->queue_base;
if (range->flags & RANGE_MULTI_QUEUE) {
for (queue = 0; queue < range->num_queues; queue++) {
inst = knav_range_offset_to_inst(kdev, range,
queue);
if (inst->notify_needed) {
inst->notify_needed = 0;
dev_dbg(kdev->dev, "acc-irq: notifying %d\n",
range_base + queue);
knav_queue_notify(inst);
}
}
} else {
queue = acc->channel - range->acc_info.start_channel;
inst = knav_range_offset_to_inst(kdev, range, queue);
dev_dbg(kdev->dev, "acc-irq: notifying %d\n",
range_base + queue);
knav_queue_notify(inst);
}
}
static int knav_acc_set_notify(struct knav_range_info *range,
struct knav_queue_inst *kq,
bool enabled)
{
struct knav_pdsp_info *pdsp = range->acc_info.pdsp;
struct knav_device *kdev = range->kdev;
u32 mask, offset;
/*
* when enabling, we need to re-trigger an interrupt if we
* have descriptors pending
*/
if (!enabled || atomic_read(&kq->desc_count) <= 0)
return 0;
kq->notify_needed = 1;
atomic_inc(&kq->acc->retrigger_count);
mask = BIT(kq->acc->channel % 32);
offset = ACC_INTD_OFFSET_STATUS(kq->acc->channel);
dev_dbg(kdev->dev, "setup-notify: re-triggering irq for %s\n",
kq->acc->name);
writel_relaxed(mask, pdsp->intd + offset);
return 0;
}
static irqreturn_t knav_acc_int_handler(int irq, void *_instdata)
{
struct knav_acc_channel *acc;
struct knav_queue_inst *kq = NULL;
struct knav_range_info *range;
struct knav_pdsp_info *pdsp;
struct knav_acc_info *info;
struct knav_device *kdev;
u32 *list, *list_cpu, val, idx, notifies;
int range_base, channel, queue = 0;
dma_addr_t list_dma;
range = _instdata;
info = &range->acc_info;
kdev = range->kdev;
pdsp = range->acc_info.pdsp;
acc = range->acc;
range_base = kdev->base_id + range->queue_base;
if ((range->flags & RANGE_MULTI_QUEUE) == 0) {
for (queue = 0; queue < range->num_irqs; queue++)
if (range->irqs[queue].irq == irq)
break;
kq = knav_range_offset_to_inst(kdev, range, queue);
acc += queue;
}
channel = acc->channel;
list_dma = acc->list_dma[acc->list_index];
list_cpu = acc->list_cpu[acc->list_index];
dev_dbg(kdev->dev, "acc-irq: channel %d, list %d, virt %p, phys %x\n",
channel, acc->list_index, list_cpu, list_dma);
if (atomic_read(&acc->retrigger_count)) {
atomic_dec(&acc->retrigger_count);
__knav_acc_notify(range, acc);
writel_relaxed(1, pdsp->intd + ACC_INTD_OFFSET_COUNT(channel));
/* ack the interrupt */
writel_relaxed(ACC_CHANNEL_INT_BASE + channel,
pdsp->intd + ACC_INTD_OFFSET_EOI);
return IRQ_HANDLED;
}
notifies = readl_relaxed(pdsp->intd + ACC_INTD_OFFSET_COUNT(channel));
WARN_ON(!notifies);
dma_sync_single_for_cpu(kdev->dev, list_dma, info->list_size,
DMA_FROM_DEVICE);
for (list = list_cpu; list < list_cpu + (info->list_size / sizeof(u32));
list += ACC_LIST_ENTRY_WORDS) {
if (ACC_LIST_ENTRY_WORDS == 1) {
dev_dbg(kdev->dev,
"acc-irq: list %d, entry @%p, %08x\n",
acc->list_index, list, list[0]);
} else if (ACC_LIST_ENTRY_WORDS == 2) {
dev_dbg(kdev->dev,
"acc-irq: list %d, entry @%p, %08x %08x\n",
acc->list_index, list, list[0], list[1]);
} else if (ACC_LIST_ENTRY_WORDS == 4) {
dev_dbg(kdev->dev,
"acc-irq: list %d, entry @%p, %08x %08x %08x %08x\n",
acc->list_index, list, list[0], list[1],
list[2], list[3]);
}
val = list[ACC_LIST_ENTRY_DESC_IDX];
if (!val)
break;
if (range->flags & RANGE_MULTI_QUEUE) {
queue = list[ACC_LIST_ENTRY_QUEUE_IDX] >> 16;
if (queue < range_base ||
queue >= range_base + range->num_queues) {
dev_err(kdev->dev,
"bad queue %d, expecting %d-%d\n",
queue, range_base,
range_base + range->num_queues);
break;
}
queue -= range_base;
kq = knav_range_offset_to_inst(kdev, range,
queue);
}
if (atomic_inc_return(&kq->desc_count) >= ACC_DESCS_MAX) {
atomic_dec(&kq->desc_count);
dev_err(kdev->dev,
"acc-irq: queue %d full, entry dropped\n",
queue + range_base);
continue;
}
idx = atomic_inc_return(&kq->desc_tail) & ACC_DESCS_MASK;
kq->descs[idx] = val;
kq->notify_needed = 1;
dev_dbg(kdev->dev, "acc-irq: enqueue %08x at %d, queue %d\n",
val, idx, queue + range_base);
}
__knav_acc_notify(range, acc);
memset(list_cpu, 0, info->list_size);
dma_sync_single_for_device(kdev->dev, list_dma, info->list_size,
DMA_TO_DEVICE);
/* flip to the other list */
acc->list_index ^= 1;
/* reset the interrupt counter */
writel_relaxed(1, pdsp->intd + ACC_INTD_OFFSET_COUNT(channel));
/* ack the interrupt */
writel_relaxed(ACC_CHANNEL_INT_BASE + channel,
pdsp->intd + ACC_INTD_OFFSET_EOI);
return IRQ_HANDLED;
}
int knav_range_setup_acc_irq(struct knav_range_info *range,
int queue, bool enabled)
{
struct knav_device *kdev = range->kdev;
struct knav_acc_channel *acc;
unsigned long cpu_map;
int ret = 0, irq;
u32 old, new;
if (range->flags & RANGE_MULTI_QUEUE) {
acc = range->acc;
irq = range->irqs[0].irq;
cpu_map = range->irqs[0].cpu_map;
} else {
acc = range->acc + queue;
irq = range->irqs[queue].irq;
cpu_map = range->irqs[queue].cpu_map;
}
old = acc->open_mask;
if (enabled)
new = old | BIT(queue);
else
new = old & ~BIT(queue);
acc->open_mask = new;
dev_dbg(kdev->dev,
"setup-acc-irq: open mask old %08x, new %08x, channel %s\n",
old, new, acc->name);
if (likely(new == old))
return 0;
if (new && !old) {
dev_dbg(kdev->dev,
"setup-acc-irq: requesting %s for channel %s\n",
acc->name, acc->name);
ret = request_irq(irq, knav_acc_int_handler, 0, acc->name,
range);
if (!ret && cpu_map) {
ret = irq_set_affinity_hint(irq, to_cpumask(&cpu_map));
if (ret) {
dev_warn(range->kdev->dev,
"Failed to set IRQ affinity\n");
return ret;
}
}
}
if (old && !new) {
dev_dbg(kdev->dev, "setup-acc-irq: freeing %s for channel %s\n",
acc->name, acc->name);
free_irq(irq, range);
}
return ret;
}
static const char *knav_acc_result_str(enum knav_acc_result result)
{
static const char * const result_str[] = {
[ACC_RET_IDLE] = "idle",
[ACC_RET_SUCCESS] = "success",
[ACC_RET_INVALID_COMMAND] = "invalid command",
[ACC_RET_INVALID_CHANNEL] = "invalid channel",
[ACC_RET_INACTIVE_CHANNEL] = "inactive channel",
[ACC_RET_ACTIVE_CHANNEL] = "active channel",
[ACC_RET_INVALID_QUEUE] = "invalid queue",
[ACC_RET_INVALID_RET] = "invalid return code",
};
if (result >= ARRAY_SIZE(result_str))
return result_str[ACC_RET_INVALID_RET];
else
return result_str[result];
}
static enum knav_acc_result
knav_acc_write(struct knav_device *kdev, struct knav_pdsp_info *pdsp,
struct knav_reg_acc_command *cmd)
{
u32 result;
dev_dbg(kdev->dev, "acc command %08x %08x %08x %08x %08x\n",
cmd->command, cmd->queue_mask, cmd->list_phys,
cmd->queue_num, cmd->timer_config);
writel_relaxed(cmd->timer_config, &pdsp->acc_command->timer_config);
writel_relaxed(cmd->queue_num, &pdsp->acc_command->queue_num);
writel_relaxed(cmd->list_phys, &pdsp->acc_command->list_phys);
writel_relaxed(cmd->queue_mask, &pdsp->acc_command->queue_mask);
writel_relaxed(cmd->command, &pdsp->acc_command->command);
/* wait for the command to clear */
do {
result = readl_relaxed(&pdsp->acc_command->command);
} while ((result >> 8) & 0xff);
return (result >> 24) & 0xff;
}
static void knav_acc_setup_cmd(struct knav_device *kdev,
struct knav_range_info *range,
struct knav_reg_acc_command *cmd,
int queue)
{
struct knav_acc_info *info = &range->acc_info;
struct knav_acc_channel *acc;
int queue_base;
u32 queue_mask;
if (range->flags & RANGE_MULTI_QUEUE) {
acc = range->acc;
queue_base = range->queue_base;
queue_mask = BIT(range->num_queues) - 1;
} else {
acc = range->acc + queue;
queue_base = range->queue_base + queue;
queue_mask = 0;
}
memset(cmd, 0, sizeof(*cmd));
cmd->command = acc->channel;
cmd->queue_mask = queue_mask;
cmd->list_phys = acc->list_dma[0];
cmd->queue_num = info->list_entries << 16;
cmd->queue_num |= queue_base;
cmd->timer_config = ACC_LIST_ENTRY_TYPE << 18;
if (range->flags & RANGE_MULTI_QUEUE)
cmd->timer_config |= ACC_CFG_MULTI_QUEUE;
cmd->timer_config |= info->pacing_mode << 16;
cmd->timer_config |= info->timer_count;
}
static void knav_acc_stop(struct knav_device *kdev,
struct knav_range_info *range,
int queue)
{
struct knav_reg_acc_command cmd;
struct knav_acc_channel *acc;
enum knav_acc_result result;
acc = range->acc + queue;
knav_acc_setup_cmd(kdev, range, &cmd, queue);
cmd.command |= ACC_CMD_DISABLE_CHANNEL << 8;
result = knav_acc_write(kdev, range->acc_info.pdsp, &cmd);
dev_dbg(kdev->dev, "stopped acc channel %s, result %s\n",
acc->name, knav_acc_result_str(result));
}
static enum knav_acc_result knav_acc_start(struct knav_device *kdev,
struct knav_range_info *range,
int queue)
{
struct knav_reg_acc_command cmd;
struct knav_acc_channel *acc;
enum knav_acc_result result;
acc = range->acc + queue;
knav_acc_setup_cmd(kdev, range, &cmd, queue);
cmd.command |= ACC_CMD_ENABLE_CHANNEL << 8;
result = knav_acc_write(kdev, range->acc_info.pdsp, &cmd);
dev_dbg(kdev->dev, "started acc channel %s, result %s\n",
acc->name, knav_acc_result_str(result));
return result;
}
static int knav_acc_init_range(struct knav_range_info *range)
{
struct knav_device *kdev = range->kdev;
struct knav_acc_channel *acc;
enum knav_acc_result result;
int queue;
for (queue = 0; queue < range->num_queues; queue++) {
acc = range->acc + queue;
knav_acc_stop(kdev, range, queue);
acc->list_index = 0;
result = knav_acc_start(kdev, range, queue);
if (result != ACC_RET_SUCCESS)
return -EIO;
if (range->flags & RANGE_MULTI_QUEUE)
return 0;
}
return 0;
}
static int knav_acc_init_queue(struct knav_range_info *range,
struct knav_queue_inst *kq)
{
unsigned id = kq->id - range->queue_base;
kq->descs = devm_kzalloc(range->kdev->dev,
ACC_DESCS_MAX * sizeof(u32), GFP_KERNEL);
if (!kq->descs)
return -ENOMEM;
kq->acc = range->acc;
if ((range->flags & RANGE_MULTI_QUEUE) == 0)
kq->acc += id;
return 0;
}
static int knav_acc_open_queue(struct knav_range_info *range,
struct knav_queue_inst *inst, unsigned flags)
{
unsigned id = inst->id - range->queue_base;
return knav_range_setup_acc_irq(range, id, true);
}
static int knav_acc_close_queue(struct knav_range_info *range,
struct knav_queue_inst *inst)
{
unsigned id = inst->id - range->queue_base;
return knav_range_setup_acc_irq(range, id, false);
}
static int knav_acc_free_range(struct knav_range_info *range)
{
struct knav_device *kdev = range->kdev;
struct knav_acc_channel *acc;
struct knav_acc_info *info;
int channel, channels;
info = &range->acc_info;
if (range->flags & RANGE_MULTI_QUEUE)
channels = 1;
else
channels = range->num_queues;
for (channel = 0; channel < channels; channel++) {
acc = range->acc + channel;
if (!acc->list_cpu[0])
continue;
dma_unmap_single(kdev->dev, acc->list_dma[0],
info->mem_size, DMA_BIDIRECTIONAL);
free_pages_exact(acc->list_cpu[0], info->mem_size);
}
devm_kfree(range->kdev->dev, range->acc);
return 0;
}
struct knav_range_ops knav_acc_range_ops = {
.set_notify = knav_acc_set_notify,
.init_queue = knav_acc_init_queue,
.open_queue = knav_acc_open_queue,
.close_queue = knav_acc_close_queue,
.init_range = knav_acc_init_range,
.free_range = knav_acc_free_range,
};
/**
* knav_init_acc_range: Initialise accumulator ranges
*
* @kdev: qmss device
* @node: device node
* @range: qmms range information
*
* Return 0 on success or error
*/
int knav_init_acc_range(struct knav_device *kdev,
struct device_node *node,
struct knav_range_info *range)
{
struct knav_acc_channel *acc;
struct knav_pdsp_info *pdsp;
struct knav_acc_info *info;
int ret, channel, channels;
int list_size, mem_size;
dma_addr_t list_dma;
void *list_mem;
u32 config[5];
range->flags |= RANGE_HAS_ACCUMULATOR;
info = &range->acc_info;
ret = of_property_read_u32_array(node, "accumulator", config, 5);
if (ret)
return ret;
info->pdsp_id = config[0];
info->start_channel = config[1];
info->list_entries = config[2];
info->pacing_mode = config[3];
info->timer_count = config[4] / ACC_DEFAULT_PERIOD;
if (info->start_channel > ACC_MAX_CHANNEL) {
dev_err(kdev->dev, "channel %d invalid for range %s\n",
info->start_channel, range->name);
return -EINVAL;
}
if (info->pacing_mode > 3) {
dev_err(kdev->dev, "pacing mode %d invalid for range %s\n",
info->pacing_mode, range->name);
return -EINVAL;
}
pdsp = knav_find_pdsp(kdev, info->pdsp_id);
if (!pdsp) {
dev_err(kdev->dev, "pdsp id %d not found for range %s\n",
info->pdsp_id, range->name);
return -EINVAL;
}
info->pdsp = pdsp;
channels = range->num_queues;
if (of_get_property(node, "multi-queue", NULL)) {
range->flags |= RANGE_MULTI_QUEUE;
channels = 1;
if (range->queue_base & (32 - 1)) {
dev_err(kdev->dev,
"misaligned multi-queue accumulator range %s\n",
range->name);
return -EINVAL;
}
if (range->num_queues > 32) {
dev_err(kdev->dev,
"too many queues in accumulator range %s\n",
range->name);
return -EINVAL;
}
}
/* figure out list size */
list_size = info->list_entries;
list_size *= ACC_LIST_ENTRY_WORDS * sizeof(u32);
info->list_size = list_size;
mem_size = PAGE_ALIGN(list_size * 2);
info->mem_size = mem_size;
range->acc = devm_kzalloc(kdev->dev, channels * sizeof(*range->acc),
GFP_KERNEL);
if (!range->acc)
return -ENOMEM;
for (channel = 0; channel < channels; channel++) {
acc = range->acc + channel;
acc->channel = info->start_channel + channel;
/* allocate memory for the two lists */
list_mem = alloc_pages_exact(mem_size, GFP_KERNEL | GFP_DMA);
if (!list_mem)
return -ENOMEM;
list_dma = dma_map_single(kdev->dev, list_mem, mem_size,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(kdev->dev, list_dma)) {
free_pages_exact(list_mem, mem_size);
return -ENOMEM;
}
memset(list_mem, 0, mem_size);
dma_sync_single_for_device(kdev->dev, list_dma, mem_size,
DMA_TO_DEVICE);
scnprintf(acc->name, sizeof(acc->name), "hwqueue-acc-%d",
acc->channel);
acc->list_cpu[0] = list_mem;
acc->list_cpu[1] = list_mem + list_size;
acc->list_dma[0] = list_dma;
acc->list_dma[1] = list_dma + list_size;
dev_dbg(kdev->dev, "%s: channel %d, phys %08x, virt %8p\n",
acc->name, acc->channel, list_dma, list_mem);
}
range->ops = &knav_acc_range_ops;
return 0;
}
EXPORT_SYMBOL_GPL(knav_init_acc_range);
/*
* Keystone Queue Manager subsystem driver
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
* Authors: Sandeep Nair <sandeep_n@ti.com>
* Cyril Chemparathy <cyril@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pm_runtime.h>
#include <linux/firmware.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/soc/ti/knav_qmss.h>
#include "knav_qmss.h"
static struct knav_device *kdev;
static DEFINE_MUTEX(knav_dev_lock);
/* Queue manager register indices in DTS */
#define KNAV_QUEUE_PEEK_REG_INDEX 0
#define KNAV_QUEUE_STATUS_REG_INDEX 1
#define KNAV_QUEUE_CONFIG_REG_INDEX 2
#define KNAV_QUEUE_REGION_REG_INDEX 3
#define KNAV_QUEUE_PUSH_REG_INDEX 4
#define KNAV_QUEUE_POP_REG_INDEX 5
/* PDSP register indices in DTS */
#define KNAV_QUEUE_PDSP_IRAM_REG_INDEX 0
#define KNAV_QUEUE_PDSP_REGS_REG_INDEX 1
#define KNAV_QUEUE_PDSP_INTD_REG_INDEX 2
#define KNAV_QUEUE_PDSP_CMD_REG_INDEX 3
#define knav_queue_idx_to_inst(kdev, idx) \
(kdev->instances + (idx << kdev->inst_shift))
#define for_each_handle_rcu(qh, inst) \
list_for_each_entry_rcu(qh, &inst->handles, list)
#define for_each_instance(idx, inst, kdev) \
for (idx = 0, inst = kdev->instances; \
idx < (kdev)->num_queues_in_use; \
idx++, inst = knav_queue_idx_to_inst(kdev, idx))
/**
* knav_queue_notify: qmss queue notfier call
*
* @inst: qmss queue instance like accumulator
*/
void knav_queue_notify(struct knav_queue_inst *inst)
{
struct knav_queue *qh;
if (!inst)
return;
rcu_read_lock();
for_each_handle_rcu(qh, inst) {
if (atomic_read(&qh->notifier_enabled) <= 0)
continue;
if (WARN_ON(!qh->notifier_fn))
continue;
atomic_inc(&qh->stats.notifies);
qh->notifier_fn(qh->notifier_fn_arg);
}
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(knav_queue_notify);
static irqreturn_t knav_queue_int_handler(int irq, void *_instdata)
{
struct knav_queue_inst *inst = _instdata;
knav_queue_notify(inst);
return IRQ_HANDLED;
}
static int knav_queue_setup_irq(struct knav_range_info *range,
struct knav_queue_inst *inst)
{
unsigned queue = inst->id - range->queue_base;
unsigned long cpu_map;
int ret = 0, irq;
if (range->flags & RANGE_HAS_IRQ) {
irq = range->irqs[queue].irq;
cpu_map = range->irqs[queue].cpu_map;
ret = request_irq(irq, knav_queue_int_handler, 0,
inst->irq_name, inst);
if (ret)
return ret;
disable_irq(irq);
if (cpu_map) {
ret = irq_set_affinity_hint(irq, to_cpumask(&cpu_map));
if (ret) {
dev_warn(range->kdev->dev,
"Failed to set IRQ affinity\n");
return ret;
}
}
}
return ret;
}
static void knav_queue_free_irq(struct knav_queue_inst *inst)
{
struct knav_range_info *range = inst->range;
unsigned queue = inst->id - inst->range->queue_base;
int irq;
if (range->flags & RANGE_HAS_IRQ) {
irq = range->irqs[queue].irq;
irq_set_affinity_hint(irq, NULL);
free_irq(irq, inst);
}
}
static inline bool knav_queue_is_busy(struct knav_queue_inst *inst)
{
return !list_empty(&inst->handles);
}
static inline bool knav_queue_is_reserved(struct knav_queue_inst *inst)
{
return inst->range->flags & RANGE_RESERVED;
}
static inline bool knav_queue_is_shared(struct knav_queue_inst *inst)
{
struct knav_queue *tmp;
rcu_read_lock();
for_each_handle_rcu(tmp, inst) {
if (tmp->flags & KNAV_QUEUE_SHARED) {
rcu_read_unlock();
return true;
}
}
rcu_read_unlock();
return false;
}
static inline bool knav_queue_match_type(struct knav_queue_inst *inst,
unsigned type)
{
if ((type == KNAV_QUEUE_QPEND) &&
(inst->range->flags & RANGE_HAS_IRQ)) {
return true;
} else if ((type == KNAV_QUEUE_ACC) &&
(inst->range->flags & RANGE_HAS_ACCUMULATOR)) {
return true;
} else if ((type == KNAV_QUEUE_GP) &&
!(inst->range->flags &
(RANGE_HAS_ACCUMULATOR | RANGE_HAS_IRQ))) {
return true;
}
return false;
}
static inline struct knav_queue_inst *
knav_queue_match_id_to_inst(struct knav_device *kdev, unsigned id)
{
struct knav_queue_inst *inst;
int idx;
for_each_instance(idx, inst, kdev) {
if (inst->id == id)
return inst;
}
return NULL;
}
static inline struct knav_queue_inst *knav_queue_find_by_id(int id)
{
if (kdev->base_id <= id &&
kdev->base_id + kdev->num_queues > id) {
id -= kdev->base_id;
return knav_queue_match_id_to_inst(kdev, id);
}
return NULL;
}
static struct knav_queue *__knav_queue_open(struct knav_queue_inst *inst,
const char *name, unsigned flags)
{
struct knav_queue *qh;
unsigned id;
int ret = 0;
qh = devm_kzalloc(inst->kdev->dev, sizeof(*qh), GFP_KERNEL);
if (!qh)
return ERR_PTR(-ENOMEM);
qh->flags = flags;
qh->inst = inst;
id = inst->id - inst->qmgr->start_queue;
qh->reg_push = &inst->qmgr->reg_push[id];
qh->reg_pop = &inst->qmgr->reg_pop[id];
qh->reg_peek = &inst->qmgr->reg_peek[id];
/* first opener? */
if (!knav_queue_is_busy(inst)) {
struct knav_range_info *range = inst->range;
inst->name = kstrndup(name, KNAV_NAME_SIZE, GFP_KERNEL);
if (range->ops && range->ops->open_queue)
ret = range->ops->open_queue(range, inst, flags);
if (ret) {
devm_kfree(inst->kdev->dev, qh);
return ERR_PTR(ret);
}
}
list_add_tail_rcu(&qh->list, &inst->handles);
return qh;
}
static struct knav_queue *
knav_queue_open_by_id(const char *name, unsigned id, unsigned flags)
{
struct knav_queue_inst *inst;
struct knav_queue *qh;
mutex_lock(&knav_dev_lock);
qh = ERR_PTR(-ENODEV);
inst = knav_queue_find_by_id(id);
if (!inst)
goto unlock_ret;
qh = ERR_PTR(-EEXIST);
if (!(flags & KNAV_QUEUE_SHARED) && knav_queue_is_busy(inst))
goto unlock_ret;
qh = ERR_PTR(-EBUSY);
if ((flags & KNAV_QUEUE_SHARED) &&
(knav_queue_is_busy(inst) && !knav_queue_is_shared(inst)))
goto unlock_ret;
qh = __knav_queue_open(inst, name, flags);
unlock_ret:
mutex_unlock(&knav_dev_lock);
return qh;
}
static struct knav_queue *knav_queue_open_by_type(const char *name,
unsigned type, unsigned flags)
{
struct knav_queue_inst *inst;
struct knav_queue *qh = ERR_PTR(-EINVAL);
int idx;
mutex_lock(&knav_dev_lock);
for_each_instance(idx, inst, kdev) {
if (knav_queue_is_reserved(inst))
continue;
if (!knav_queue_match_type(inst, type))
continue;
if (knav_queue_is_busy(inst))
continue;
qh = __knav_queue_open(inst, name, flags);
goto unlock_ret;
}
unlock_ret:
mutex_unlock(&knav_dev_lock);
return qh;
}
static void knav_queue_set_notify(struct knav_queue_inst *inst, bool enabled)
{
struct knav_range_info *range = inst->range;
if (range->ops && range->ops->set_notify)
range->ops->set_notify(range, inst, enabled);
}
static int knav_queue_enable_notifier(struct knav_queue *qh)
{
struct knav_queue_inst *inst = qh->inst;
bool first;
if (WARN_ON(!qh->notifier_fn))
return -EINVAL;
/* Adjust the per handle notifier count */
first = (atomic_inc_return(&qh->notifier_enabled) == 1);
if (!first)
return 0; /* nothing to do */
/* Now adjust the per instance notifier count */
first = (atomic_inc_return(&inst->num_notifiers) == 1);
if (first)
knav_queue_set_notify(inst, true);
return 0;
}
static int knav_queue_disable_notifier(struct knav_queue *qh)
{
struct knav_queue_inst *inst = qh->inst;
bool last;
last = (atomic_dec_return(&qh->notifier_enabled) == 0);
if (!last)
return 0; /* nothing to do */
last = (atomic_dec_return(&inst->num_notifiers) == 0);
if (last)
knav_queue_set_notify(inst, false);
return 0;
}
static int knav_queue_set_notifier(struct knav_queue *qh,
struct knav_queue_notify_config *cfg)
{
knav_queue_notify_fn old_fn = qh->notifier_fn;
if (!cfg)
return -EINVAL;
if (!(qh->inst->range->flags & (RANGE_HAS_ACCUMULATOR | RANGE_HAS_IRQ)))
return -ENOTSUPP;
if (!cfg->fn && old_fn)
knav_queue_disable_notifier(qh);
qh->notifier_fn = cfg->fn;
qh->notifier_fn_arg = cfg->fn_arg;
if (cfg->fn && !old_fn)
knav_queue_enable_notifier(qh);
return 0;
}
static int knav_gp_set_notify(struct knav_range_info *range,
struct knav_queue_inst *inst,
bool enabled)
{
unsigned queue;
if (range->flags & RANGE_HAS_IRQ) {
queue = inst->id - range->queue_base;
if (enabled)
enable_irq(range->irqs[queue].irq);
else
disable_irq_nosync(range->irqs[queue].irq);
}
return 0;
}
static int knav_gp_open_queue(struct knav_range_info *range,
struct knav_queue_inst *inst, unsigned flags)
{
return knav_queue_setup_irq(range, inst);
}
static int knav_gp_close_queue(struct knav_range_info *range,
struct knav_queue_inst *inst)
{
knav_queue_free_irq(inst);
return 0;
}
struct knav_range_ops knav_gp_range_ops = {
.set_notify = knav_gp_set_notify,
.open_queue = knav_gp_open_queue,
.close_queue = knav_gp_close_queue,
};
static int knav_queue_get_count(void *qhandle)
{
struct knav_queue *qh = qhandle;
struct knav_queue_inst *inst = qh->inst;
return readl_relaxed(&qh->reg_peek[0].entry_count) +
atomic_read(&inst->desc_count);
}
static void knav_queue_debug_show_instance(struct seq_file *s,
struct knav_queue_inst *inst)
{
struct knav_device *kdev = inst->kdev;
struct knav_queue *qh;
if (!knav_queue_is_busy(inst))
return;
seq_printf(s, "\tqueue id %d (%s)\n",
kdev->base_id + inst->id, inst->name);
for_each_handle_rcu(qh, inst) {
seq_printf(s, "\t\thandle %p: ", qh);
seq_printf(s, "pushes %8d, ",
atomic_read(&qh->stats.pushes));
seq_printf(s, "pops %8d, ",
atomic_read(&qh->stats.pops));
seq_printf(s, "count %8d, ",
knav_queue_get_count(qh));
seq_printf(s, "notifies %8d, ",
atomic_read(&qh->stats.notifies));
seq_printf(s, "push errors %8d, ",
atomic_read(&qh->stats.push_errors));
seq_printf(s, "pop errors %8d\n",
atomic_read(&qh->stats.pop_errors));
}
}
static int knav_queue_debug_show(struct seq_file *s, void *v)
{
struct knav_queue_inst *inst;
int idx;
mutex_lock(&knav_dev_lock);
seq_printf(s, "%s: %u-%u\n",
dev_name(kdev->dev), kdev->base_id,
kdev->base_id + kdev->num_queues - 1);
for_each_instance(idx, inst, kdev)
knav_queue_debug_show_instance(s, inst);
mutex_unlock(&knav_dev_lock);
return 0;
}
static int knav_queue_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, knav_queue_debug_show, NULL);
}
static const struct file_operations knav_queue_debug_ops = {
.open = knav_queue_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static inline int knav_queue_pdsp_wait(u32 * __iomem addr, unsigned timeout,
u32 flags)
{
unsigned long end;
u32 val = 0;
end = jiffies + msecs_to_jiffies(timeout);
while (time_after(end, jiffies)) {
val = readl_relaxed(addr);
if (flags)
val &= flags;
if (!val)
break;
cpu_relax();
}
return val ? -ETIMEDOUT : 0;
}
static int knav_queue_flush(struct knav_queue *qh)
{
struct knav_queue_inst *inst = qh->inst;
unsigned id = inst->id - inst->qmgr->start_queue;
atomic_set(&inst->desc_count, 0);
writel_relaxed(0, &inst->qmgr->reg_push[id].ptr_size_thresh);
return 0;
}
/**
* knav_queue_open() - open a hardware queue
* @name - name to give the queue handle
* @id - desired queue number if any or specifes the type
* of queue
* @flags - the following flags are applicable to queues:
* KNAV_QUEUE_SHARED - allow the queue to be shared. Queues are
* exclusive by default.
* Subsequent attempts to open a shared queue should
* also have this flag.
*
* Returns a handle to the open hardware queue if successful. Use IS_ERR()
* to check the returned value for error codes.
*/
void *knav_queue_open(const char *name, unsigned id,
unsigned flags)
{
struct knav_queue *qh = ERR_PTR(-EINVAL);
switch (id) {
case KNAV_QUEUE_QPEND:
case KNAV_QUEUE_ACC:
case KNAV_QUEUE_GP:
qh = knav_queue_open_by_type(name, id, flags);
break;
default:
qh = knav_queue_open_by_id(name, id, flags);
break;
}
return qh;
}
EXPORT_SYMBOL_GPL(knav_queue_open);
/**
* knav_queue_close() - close a hardware queue handle
* @qh - handle to close
*/
void knav_queue_close(void *qhandle)
{
struct knav_queue *qh = qhandle;
struct knav_queue_inst *inst = qh->inst;
while (atomic_read(&qh->notifier_enabled) > 0)
knav_queue_disable_notifier(qh);
mutex_lock(&knav_dev_lock);
list_del_rcu(&qh->list);
mutex_unlock(&knav_dev_lock);
synchronize_rcu();
if (!knav_queue_is_busy(inst)) {
struct knav_range_info *range = inst->range;
if (range->ops && range->ops->close_queue)
range->ops->close_queue(range, inst);
}
devm_kfree(inst->kdev->dev, qh);
}
EXPORT_SYMBOL_GPL(knav_queue_close);
/**
* knav_queue_device_control() - Perform control operations on a queue
* @qh - queue handle
* @cmd - control commands
* @arg - command argument
*
* Returns 0 on success, errno otherwise.
*/
int knav_queue_device_control(void *qhandle, enum knav_queue_ctrl_cmd cmd,
unsigned long arg)
{
struct knav_queue *qh = qhandle;
struct knav_queue_notify_config *cfg;
int ret;
switch ((int)cmd) {
case KNAV_QUEUE_GET_ID:
ret = qh->inst->kdev->base_id + qh->inst->id;
break;
case KNAV_QUEUE_FLUSH:
ret = knav_queue_flush(qh);
break;
case KNAV_QUEUE_SET_NOTIFIER:
cfg = (void *)arg;
ret = knav_queue_set_notifier(qh, cfg);
break;
case KNAV_QUEUE_ENABLE_NOTIFY:
ret = knav_queue_enable_notifier(qh);
break;
case KNAV_QUEUE_DISABLE_NOTIFY:
ret = knav_queue_disable_notifier(qh);
break;
case KNAV_QUEUE_GET_COUNT:
ret = knav_queue_get_count(qh);
break;
default:
ret = -ENOTSUPP;
break;
}
return ret;
}
EXPORT_SYMBOL_GPL(knav_queue_device_control);
/**
* knav_queue_push() - push data (or descriptor) to the tail of a queue
* @qh - hardware queue handle
* @data - data to push
* @size - size of data to push
* @flags - can be used to pass additional information
*
* Returns 0 on success, errno otherwise.
*/
int knav_queue_push(void *qhandle, dma_addr_t dma,
unsigned size, unsigned flags)
{
struct knav_queue *qh = qhandle;
u32 val;
val = (u32)dma | ((size / 16) - 1);
writel_relaxed(val, &qh->reg_push[0].ptr_size_thresh);
atomic_inc(&qh->stats.pushes);
return 0;
}
/**
* knav_queue_pop() - pop data (or descriptor) from the head of a queue
* @qh - hardware queue handle
* @size - (optional) size of the data pop'ed.
*
* Returns a DMA address on success, 0 on failure.
*/
dma_addr_t knav_queue_pop(void *qhandle, unsigned *size)
{
struct knav_queue *qh = qhandle;
struct knav_queue_inst *inst = qh->inst;
dma_addr_t dma;
u32 val, idx;
/* are we accumulated? */
if (inst->descs) {
if (unlikely(atomic_dec_return(&inst->desc_count) < 0)) {
atomic_inc(&inst->desc_count);
return 0;
}
idx = atomic_inc_return(&inst->desc_head);
idx &= ACC_DESCS_MASK;
val = inst->descs[idx];
} else {
val = readl_relaxed(&qh->reg_pop[0].ptr_size_thresh);
if (unlikely(!val))
return 0;
}
dma = val & DESC_PTR_MASK;
if (size)
*size = ((val & DESC_SIZE_MASK) + 1) * 16;
atomic_inc(&qh->stats.pops);
return dma;
}
/* carve out descriptors and push into queue */
static void kdesc_fill_pool(struct knav_pool *pool)
{
struct knav_region *region;
int i;
region = pool->region;
pool->desc_size = region->desc_size;
for (i = 0; i < pool->num_desc; i++) {
int index = pool->region_offset + i;
dma_addr_t dma_addr;
unsigned dma_size;
dma_addr = region->dma_start + (region->desc_size * index);
dma_size = ALIGN(pool->desc_size, SMP_CACHE_BYTES);
dma_sync_single_for_device(pool->dev, dma_addr, dma_size,
DMA_TO_DEVICE);
knav_queue_push(pool->queue, dma_addr, dma_size, 0);
}
}
/* pop out descriptors and close the queue */
static void kdesc_empty_pool(struct knav_pool *pool)
{
dma_addr_t dma;
unsigned size;
void *desc;
int i;
if (!pool->queue)
return;
for (i = 0;; i++) {
dma = knav_queue_pop(pool->queue, &size);
if (!dma)
break;
desc = knav_pool_desc_dma_to_virt(pool, dma);
if (!desc) {
dev_dbg(pool->kdev->dev,
"couldn't unmap desc, continuing\n");
continue;
}
}
WARN_ON(i != pool->num_desc);
knav_queue_close(pool->queue);
}
/* Get the DMA address of a descriptor */
dma_addr_t knav_pool_desc_virt_to_dma(void *ph, void *virt)
{
struct knav_pool *pool = ph;
return pool->region->dma_start + (virt - pool->region->virt_start);
}
void *knav_pool_desc_dma_to_virt(void *ph, dma_addr_t dma)
{
struct knav_pool *pool = ph;
return pool->region->virt_start + (dma - pool->region->dma_start);
}
/**
* knav_pool_create() - Create a pool of descriptors
* @name - name to give the pool handle
* @num_desc - numbers of descriptors in the pool
* @region_id - QMSS region id from which the descriptors are to be
* allocated.
*
* Returns a pool handle on success.
* Use IS_ERR_OR_NULL() to identify error values on return.
*/
void *knav_pool_create(const char *name,
int num_desc, int region_id)
{
struct knav_region *reg_itr, *region = NULL;
struct knav_pool *pool, *pi;
struct list_head *node;
unsigned last_offset;
bool slot_found;
int ret;
if (!kdev->dev)
return ERR_PTR(-ENODEV);
pool = devm_kzalloc(kdev->dev, sizeof(*pool), GFP_KERNEL);
if (!pool) {
dev_err(kdev->dev, "out of memory allocating pool\n");
return ERR_PTR(-ENOMEM);
}
for_each_region(kdev, reg_itr) {
if (reg_itr->id != region_id)
continue;
region = reg_itr;
break;
}
if (!region) {
dev_err(kdev->dev, "region-id(%d) not found\n", region_id);
ret = -EINVAL;
goto err;
}
pool->queue = knav_queue_open(name, KNAV_QUEUE_GP, 0);
if (IS_ERR_OR_NULL(pool->queue)) {
dev_err(kdev->dev,
"failed to open queue for pool(%s), error %ld\n",
name, PTR_ERR(pool->queue));
ret = PTR_ERR(pool->queue);
goto err;
}
pool->name = kstrndup(name, KNAV_NAME_SIZE, GFP_KERNEL);
pool->kdev = kdev;
pool->dev = kdev->dev;
mutex_lock(&knav_dev_lock);
if (num_desc > (region->num_desc - region->used_desc)) {
dev_err(kdev->dev, "out of descs in region(%d) for pool(%s)\n",
region_id, name);
ret = -ENOMEM;
goto err;
}
/* Region maintains a sorted (by region offset) list of pools
* use the first free slot which is large enough to accomodate
* the request
*/
last_offset = 0;
slot_found = false;
node = &region->pools;
list_for_each_entry(pi, &region->pools, region_inst) {
if ((pi->region_offset - last_offset) >= num_desc) {
slot_found = true;
break;
}
last_offset = pi->region_offset + pi->num_desc;
}
node = &pi->region_inst;
if (slot_found) {
pool->region = region;
pool->num_desc = num_desc;
pool->region_offset = last_offset;
region->used_desc += num_desc;
list_add_tail(&pool->list, &kdev->pools);
list_add_tail(&pool->region_inst, node);
} else {
dev_err(kdev->dev, "pool(%s) create failed: fragmented desc pool in region(%d)\n",
name, region_id);
ret = -ENOMEM;
goto err;
}
mutex_unlock(&knav_dev_lock);
kdesc_fill_pool(pool);
return pool;
err:
mutex_unlock(&knav_dev_lock);
kfree(pool->name);
devm_kfree(kdev->dev, pool);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(knav_pool_create);
/**
* knav_pool_destroy() - Free a pool of descriptors
* @pool - pool handle
*/
void knav_pool_destroy(void *ph)
{
struct knav_pool *pool = ph;
if (!pool)
return;
if (!pool->region)
return;
kdesc_empty_pool(pool);
mutex_lock(&knav_dev_lock);
pool->region->used_desc -= pool->num_desc;
list_del(&pool->region_inst);
list_del(&pool->list);
mutex_unlock(&knav_dev_lock);
kfree(pool->name);
devm_kfree(kdev->dev, pool);
}
EXPORT_SYMBOL_GPL(knav_pool_destroy);
/**
* knav_pool_desc_get() - Get a descriptor from the pool
* @pool - pool handle
*
* Returns descriptor from the pool.
*/
void *knav_pool_desc_get(void *ph)
{
struct knav_pool *pool = ph;
dma_addr_t dma;
unsigned size;
void *data;
dma = knav_queue_pop(pool->queue, &size);
if (unlikely(!dma))
return ERR_PTR(-ENOMEM);
data = knav_pool_desc_dma_to_virt(pool, dma);
return data;
}
/**
* knav_pool_desc_put() - return a descriptor to the pool
* @pool - pool handle
*/
void knav_pool_desc_put(void *ph, void *desc)
{
struct knav_pool *pool = ph;
dma_addr_t dma;
dma = knav_pool_desc_virt_to_dma(pool, desc);
knav_queue_push(pool->queue, dma, pool->region->desc_size, 0);
}
/**
* knav_pool_desc_map() - Map descriptor for DMA transfer
* @pool - pool handle
* @desc - address of descriptor to map
* @size - size of descriptor to map
* @dma - DMA address return pointer
* @dma_sz - adjusted return pointer
*
* Returns 0 on success, errno otherwise.
*/
int knav_pool_desc_map(void *ph, void *desc, unsigned size,
dma_addr_t *dma, unsigned *dma_sz)
{
struct knav_pool *pool = ph;
*dma = knav_pool_desc_virt_to_dma(pool, desc);
size = min(size, pool->region->desc_size);
size = ALIGN(size, SMP_CACHE_BYTES);
*dma_sz = size;
dma_sync_single_for_device(pool->dev, *dma, size, DMA_TO_DEVICE);
/* Ensure the descriptor reaches to the memory */
__iowmb();
return 0;
}
/**
* knav_pool_desc_unmap() - Unmap descriptor after DMA transfer
* @pool - pool handle
* @dma - DMA address of descriptor to unmap
* @dma_sz - size of descriptor to unmap
*
* Returns descriptor address on success, Use IS_ERR_OR_NULL() to identify
* error values on return.
*/
void *knav_pool_desc_unmap(void *ph, dma_addr_t dma, unsigned dma_sz)
{
struct knav_pool *pool = ph;
unsigned desc_sz;
void *desc;
desc_sz = min(dma_sz, pool->region->desc_size);
desc = knav_pool_desc_dma_to_virt(pool, dma);
dma_sync_single_for_cpu(pool->dev, dma, desc_sz, DMA_FROM_DEVICE);
prefetch(desc);
return desc;
}
/**
* knav_pool_count() - Get the number of descriptors in pool.
* @pool - pool handle
* Returns number of elements in the pool.
*/
int knav_pool_count(void *ph)
{
struct knav_pool *pool = ph;
return knav_queue_get_count(pool->queue);
}
static void knav_queue_setup_region(struct knav_device *kdev,
struct knav_region *region)
{
unsigned hw_num_desc, hw_desc_size, size;
struct knav_reg_region __iomem *regs;
struct knav_qmgr_info *qmgr;
struct knav_pool *pool;
int id = region->id;
struct page *page;
/* unused region? */
if (!region->num_desc) {
dev_warn(kdev->dev, "unused region %s\n", region->name);
return;
}
/* get hardware descriptor value */
hw_num_desc = ilog2(region->num_desc - 1) + 1;
/* did we force fit ourselves into nothingness? */
if (region->num_desc < 32) {
region->num_desc = 0;
dev_warn(kdev->dev, "too few descriptors in region %s\n",
region->name);
return;
}
size = region->num_desc * region->desc_size;
region->virt_start = alloc_pages_exact(size, GFP_KERNEL | GFP_DMA |
GFP_DMA32);
if (!region->virt_start) {
region->num_desc = 0;
dev_err(kdev->dev, "memory alloc failed for region %s\n",
region->name);
return;
}
region->virt_end = region->virt_start + size;
page = virt_to_page(region->virt_start);
region->dma_start = dma_map_page(kdev->dev, page, 0, size,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(kdev->dev, region->dma_start)) {
dev_err(kdev->dev, "dma map failed for region %s\n",
region->name);
goto fail;
}
region->dma_end = region->dma_start + size;
pool = devm_kzalloc(kdev->dev, sizeof(*pool), GFP_KERNEL);
if (!pool) {
dev_err(kdev->dev, "out of memory allocating dummy pool\n");
goto fail;
}
pool->num_desc = 0;
pool->region_offset = region->num_desc;
list_add(&pool->region_inst, &region->pools);
dev_dbg(kdev->dev,
"region %s (%d): size:%d, link:%d@%d, phys:%08x-%08x, virt:%p-%p\n",
region->name, id, region->desc_size, region->num_desc,
region->link_index, region->dma_start, region->dma_end,
region->virt_start, region->virt_end);
hw_desc_size = (region->desc_size / 16) - 1;
hw_num_desc -= 5;
for_each_qmgr(kdev, qmgr) {
regs = qmgr->reg_region + id;
writel_relaxed(region->dma_start, &regs->base);
writel_relaxed(region->link_index, &regs->start_index);
writel_relaxed(hw_desc_size << 16 | hw_num_desc,
&regs->size_count);
}
return;
fail:
if (region->dma_start)
dma_unmap_page(kdev->dev, region->dma_start, size,
DMA_BIDIRECTIONAL);
if (region->virt_start)
free_pages_exact(region->virt_start, size);
region->num_desc = 0;
return;
}
static const char *knav_queue_find_name(struct device_node *node)
{
const char *name;
if (of_property_read_string(node, "label", &name) < 0)
name = node->name;
if (!name)
name = "unknown";
return name;
}
static int knav_queue_setup_regions(struct knav_device *kdev,
struct device_node *regions)
{
struct device *dev = kdev->dev;
struct knav_region *region;
struct device_node *child;
u32 temp[2];
int ret;
for_each_child_of_node(regions, child) {
region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL);
if (!region) {
dev_err(dev, "out of memory allocating region\n");
return -ENOMEM;
}
region->name = knav_queue_find_name(child);
of_property_read_u32(child, "id", &region->id);
ret = of_property_read_u32_array(child, "region-spec", temp, 2);
if (!ret) {
region->num_desc = temp[0];
region->desc_size = temp[1];
} else {
dev_err(dev, "invalid region info %s\n", region->name);
devm_kfree(dev, region);
continue;
}
if (!of_get_property(child, "link-index", NULL)) {
dev_err(dev, "No link info for %s\n", region->name);
devm_kfree(dev, region);
continue;
}
ret = of_property_read_u32(child, "link-index",
&region->link_index);
if (ret) {
dev_err(dev, "link index not found for %s\n",
region->name);
devm_kfree(dev, region);
continue;
}
INIT_LIST_HEAD(&region->pools);
list_add_tail(&region->list, &kdev->regions);
}
if (list_empty(&kdev->regions)) {
dev_err(dev, "no valid region information found\n");
return -ENODEV;
}
/* Next, we run through the regions and set things up */
for_each_region(kdev, region)
knav_queue_setup_region(kdev, region);
return 0;
}
static int knav_get_link_ram(struct knav_device *kdev,
const char *name,
struct knav_link_ram_block *block)
{
struct platform_device *pdev = to_platform_device(kdev->dev);
struct device_node *node = pdev->dev.of_node;
u32 temp[2];
/*
* Note: link ram resources are specified in "entry" sized units. In
* reality, although entries are ~40bits in hardware, we treat them as
* 64-bit entities here.
*
* For example, to specify the internal link ram for Keystone-I class
* devices, we would set the linkram0 resource to 0x80000-0x83fff.
*
* This gets a bit weird when other link rams are used. For example,
* if the range specified is 0x0c000000-0x0c003fff (i.e., 16K entries
* in MSMC SRAM), the actual memory used is 0x0c000000-0x0c020000,
* which accounts for 64-bits per entry, for 16K entries.
*/
if (!of_property_read_u32_array(node, name , temp, 2)) {
if (temp[0]) {
/*
* queue_base specified => using internal or onchip
* link ram WARNING - we do not "reserve" this block
*/
block->phys = (dma_addr_t)temp[0];
block->virt = NULL;
block->size = temp[1];
} else {
block->size = temp[1];
/* queue_base not specific => allocate requested size */
block->virt = dmam_alloc_coherent(kdev->dev,
8 * block->size, &block->phys,
GFP_KERNEL);
if (!block->virt) {
dev_err(kdev->dev, "failed to alloc linkram\n");
return -ENOMEM;
}
}
} else {
return -ENODEV;
}
return 0;
}
static int knav_queue_setup_link_ram(struct knav_device *kdev)
{
struct knav_link_ram_block *block;
struct knav_qmgr_info *qmgr;
for_each_qmgr(kdev, qmgr) {
block = &kdev->link_rams[0];
dev_dbg(kdev->dev, "linkram0: phys:%x, virt:%p, size:%x\n",
block->phys, block->virt, block->size);
writel_relaxed(block->phys, &qmgr->reg_config->link_ram_base0);
writel_relaxed(block->size, &qmgr->reg_config->link_ram_size0);
block++;
if (!block->size)
return 0;
dev_dbg(kdev->dev, "linkram1: phys:%x, virt:%p, size:%x\n",
block->phys, block->virt, block->size);
writel_relaxed(block->phys, &qmgr->reg_config->link_ram_base1);
}
return 0;
}
static int knav_setup_queue_range(struct knav_device *kdev,
struct device_node *node)
{
struct device *dev = kdev->dev;
struct knav_range_info *range;
struct knav_qmgr_info *qmgr;
u32 temp[2], start, end, id, index;
int ret, i;
range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
if (!range) {
dev_err(dev, "out of memory allocating range\n");
return -ENOMEM;
}
range->kdev = kdev;
range->name = knav_queue_find_name(node);
ret = of_property_read_u32_array(node, "qrange", temp, 2);
if (!ret) {
range->queue_base = temp[0] - kdev->base_id;
range->num_queues = temp[1];
} else {
dev_err(dev, "invalid queue range %s\n", range->name);
devm_kfree(dev, range);
return -EINVAL;
}
for (i = 0; i < RANGE_MAX_IRQS; i++) {
struct of_phandle_args oirq;
if (of_irq_parse_one(node, i, &oirq))
break;
range->irqs[i].irq = irq_create_of_mapping(&oirq);
if (range->irqs[i].irq == IRQ_NONE)
break;
range->num_irqs++;
if (oirq.args_count == 3)
range->irqs[i].cpu_map =
(oirq.args[2] & 0x0000ff00) >> 8;
}
range->num_irqs = min(range->num_irqs, range->num_queues);
if (range->num_irqs)
range->flags |= RANGE_HAS_IRQ;
if (of_get_property(node, "qalloc-by-id", NULL))
range->flags |= RANGE_RESERVED;
if (of_get_property(node, "accumulator", NULL)) {
ret = knav_init_acc_range(kdev, node, range);
if (ret < 0) {
devm_kfree(dev, range);
return ret;
}
} else {
range->ops = &knav_gp_range_ops;
}
/* set threshold to 1, and flush out the queues */
for_each_qmgr(kdev, qmgr) {
start = max(qmgr->start_queue, range->queue_base);
end = min(qmgr->start_queue + qmgr->num_queues,
range->queue_base + range->num_queues);
for (id = start; id < end; id++) {
index = id - qmgr->start_queue;
writel_relaxed(THRESH_GTE | 1,
&qmgr->reg_peek[index].ptr_size_thresh);
writel_relaxed(0,
&qmgr->reg_push[index].ptr_size_thresh);
}
}
list_add_tail(&range->list, &kdev->queue_ranges);
dev_dbg(dev, "added range %s: %d-%d, %d irqs%s%s%s\n",
range->name, range->queue_base,
range->queue_base + range->num_queues - 1,
range->num_irqs,
(range->flags & RANGE_HAS_IRQ) ? ", has irq" : "",
(range->flags & RANGE_RESERVED) ? ", reserved" : "",
(range->flags & RANGE_HAS_ACCUMULATOR) ? ", acc" : "");
kdev->num_queues_in_use += range->num_queues;
return 0;
}
static int knav_setup_queue_pools(struct knav_device *kdev,
struct device_node *queue_pools)
{
struct device_node *type, *range;
int ret;
for_each_child_of_node(queue_pools, type) {
for_each_child_of_node(type, range) {
ret = knav_setup_queue_range(kdev, range);
/* return value ignored, we init the rest... */
}
}
/* ... and barf if they all failed! */
if (list_empty(&kdev->queue_ranges)) {
dev_err(kdev->dev, "no valid queue range found\n");
return -ENODEV;
}
return 0;
}
static void knav_free_queue_range(struct knav_device *kdev,
struct knav_range_info *range)
{
if (range->ops && range->ops->free_range)
range->ops->free_range(range);
list_del(&range->list);
devm_kfree(kdev->dev, range);
}
static void knav_free_queue_ranges(struct knav_device *kdev)
{
struct knav_range_info *range;
for (;;) {
range = first_queue_range(kdev);
if (!range)
break;
knav_free_queue_range(kdev, range);
}
}
static void knav_queue_free_regions(struct knav_device *kdev)
{
struct knav_region *region;
struct knav_pool *pool;
unsigned size;
for (;;) {
region = first_region(kdev);
if (!region)
break;
list_for_each_entry(pool, &region->pools, region_inst)
knav_pool_destroy(pool);
size = region->virt_end - region->virt_start;
if (size)
free_pages_exact(region->virt_start, size);
list_del(&region->list);
devm_kfree(kdev->dev, region);
}
}
static void __iomem *knav_queue_map_reg(struct knav_device *kdev,
struct device_node *node, int index)
{
struct resource res;
void __iomem *regs;
int ret;
ret = of_address_to_resource(node, index, &res);
if (ret) {
dev_err(kdev->dev, "Can't translate of node(%s) address for index(%d)\n",
node->name, index);
return ERR_PTR(ret);
}
regs = devm_ioremap_resource(kdev->dev, &res);
if (IS_ERR(regs))
dev_err(kdev->dev, "Failed to map register base for index(%d) node(%s)\n",
index, node->name);
return regs;
}
static int knav_queue_init_qmgrs(struct knav_device *kdev,
struct device_node *qmgrs)
{
struct device *dev = kdev->dev;
struct knav_qmgr_info *qmgr;
struct device_node *child;
u32 temp[2];
int ret;
for_each_child_of_node(qmgrs, child) {
qmgr = devm_kzalloc(dev, sizeof(*qmgr), GFP_KERNEL);
if (!qmgr) {
dev_err(dev, "out of memory allocating qmgr\n");
return -ENOMEM;
}
ret = of_property_read_u32_array(child, "managed-queues",
temp, 2);
if (!ret) {
qmgr->start_queue = temp[0];
qmgr->num_queues = temp[1];
} else {
dev_err(dev, "invalid qmgr queue range\n");
devm_kfree(dev, qmgr);
continue;
}
dev_info(dev, "qmgr start queue %d, number of queues %d\n",
qmgr->start_queue, qmgr->num_queues);
qmgr->reg_peek =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_PEEK_REG_INDEX);
qmgr->reg_status =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_STATUS_REG_INDEX);
qmgr->reg_config =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_CONFIG_REG_INDEX);
qmgr->reg_region =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_REGION_REG_INDEX);
qmgr->reg_push =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_PUSH_REG_INDEX);
qmgr->reg_pop =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_POP_REG_INDEX);
if (IS_ERR(qmgr->reg_peek) || IS_ERR(qmgr->reg_status) ||
IS_ERR(qmgr->reg_config) || IS_ERR(qmgr->reg_region) ||
IS_ERR(qmgr->reg_push) || IS_ERR(qmgr->reg_pop)) {
dev_err(dev, "failed to map qmgr regs\n");
if (!IS_ERR(qmgr->reg_peek))
devm_iounmap(dev, qmgr->reg_peek);
if (!IS_ERR(qmgr->reg_status))
devm_iounmap(dev, qmgr->reg_status);
if (!IS_ERR(qmgr->reg_config))
devm_iounmap(dev, qmgr->reg_config);
if (!IS_ERR(qmgr->reg_region))
devm_iounmap(dev, qmgr->reg_region);
if (!IS_ERR(qmgr->reg_push))
devm_iounmap(dev, qmgr->reg_push);
if (!IS_ERR(qmgr->reg_pop))
devm_iounmap(dev, qmgr->reg_pop);
devm_kfree(dev, qmgr);
continue;
}
list_add_tail(&qmgr->list, &kdev->qmgrs);
dev_info(dev, "added qmgr start queue %d, num of queues %d, reg_peek %p, reg_status %p, reg_config %p, reg_region %p, reg_push %p, reg_pop %p\n",
qmgr->start_queue, qmgr->num_queues,
qmgr->reg_peek, qmgr->reg_status,
qmgr->reg_config, qmgr->reg_region,
qmgr->reg_push, qmgr->reg_pop);
}
return 0;
}
static int knav_queue_init_pdsps(struct knav_device *kdev,
struct device_node *pdsps)
{
struct device *dev = kdev->dev;
struct knav_pdsp_info *pdsp;
struct device_node *child;
int ret;
for_each_child_of_node(pdsps, child) {
pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL);
if (!pdsp) {
dev_err(dev, "out of memory allocating pdsp\n");
return -ENOMEM;
}
pdsp->name = knav_queue_find_name(child);
ret = of_property_read_string(child, "firmware",
&pdsp->firmware);
if (ret < 0 || !pdsp->firmware) {
dev_err(dev, "unknown firmware for pdsp %s\n",
pdsp->name);
devm_kfree(dev, pdsp);
continue;
}
dev_dbg(dev, "pdsp name %s fw name :%s\n", pdsp->name,
pdsp->firmware);
pdsp->iram =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_PDSP_IRAM_REG_INDEX);
pdsp->regs =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_PDSP_REGS_REG_INDEX);
pdsp->intd =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_PDSP_INTD_REG_INDEX);
pdsp->command =
knav_queue_map_reg(kdev, child,
KNAV_QUEUE_PDSP_CMD_REG_INDEX);
if (IS_ERR(pdsp->command) || IS_ERR(pdsp->iram) ||
IS_ERR(pdsp->regs) || IS_ERR(pdsp->intd)) {
dev_err(dev, "failed to map pdsp %s regs\n",
pdsp->name);
if (!IS_ERR(pdsp->command))
devm_iounmap(dev, pdsp->command);
if (!IS_ERR(pdsp->iram))
devm_iounmap(dev, pdsp->iram);
if (!IS_ERR(pdsp->regs))
devm_iounmap(dev, pdsp->regs);
if (!IS_ERR(pdsp->intd))
devm_iounmap(dev, pdsp->intd);
devm_kfree(dev, pdsp);
continue;
}
of_property_read_u32(child, "id", &pdsp->id);
list_add_tail(&pdsp->list, &kdev->pdsps);
dev_dbg(dev, "added pdsp %s: command %p, iram %p, regs %p, intd %p, firmware %s\n",
pdsp->name, pdsp->command, pdsp->iram, pdsp->regs,
pdsp->intd, pdsp->firmware);
}
return 0;
}
static int knav_queue_stop_pdsp(struct knav_device *kdev,
struct knav_pdsp_info *pdsp)
{
u32 val, timeout = 1000;
int ret;
val = readl_relaxed(&pdsp->regs->control) & ~PDSP_CTRL_ENABLE;
writel_relaxed(val, &pdsp->regs->control);
ret = knav_queue_pdsp_wait(&pdsp->regs->control, timeout,
PDSP_CTRL_RUNNING);
if (ret < 0) {
dev_err(kdev->dev, "timed out on pdsp %s stop\n", pdsp->name);
return ret;
}
return 0;
}
static int knav_queue_load_pdsp(struct knav_device *kdev,
struct knav_pdsp_info *pdsp)
{
int i, ret, fwlen;
const struct firmware *fw;
u32 *fwdata;
ret = request_firmware(&fw, pdsp->firmware, kdev->dev);
if (ret) {
dev_err(kdev->dev, "failed to get firmware %s for pdsp %s\n",
pdsp->firmware, pdsp->name);
return ret;
}
writel_relaxed(pdsp->id + 1, pdsp->command + 0x18);
/* download the firmware */
fwdata = (u32 *)fw->data;
fwlen = (fw->size + sizeof(u32) - 1) / sizeof(u32);
for (i = 0; i < fwlen; i++)
writel_relaxed(be32_to_cpu(fwdata[i]), pdsp->iram + i);
release_firmware(fw);
return 0;
}
static int knav_queue_start_pdsp(struct knav_device *kdev,
struct knav_pdsp_info *pdsp)
{
u32 val, timeout = 1000;
int ret;
/* write a command for sync */
writel_relaxed(0xffffffff, pdsp->command);
while (readl_relaxed(pdsp->command) != 0xffffffff)
cpu_relax();
/* soft reset the PDSP */
val = readl_relaxed(&pdsp->regs->control);
val &= ~(PDSP_CTRL_PC_MASK | PDSP_CTRL_SOFT_RESET);
writel_relaxed(val, &pdsp->regs->control);
/* enable pdsp */
val = readl_relaxed(&pdsp->regs->control) | PDSP_CTRL_ENABLE;
writel_relaxed(val, &pdsp->regs->control);
/* wait for command register to clear */
ret = knav_queue_pdsp_wait(pdsp->command, timeout, 0);
if (ret < 0) {
dev_err(kdev->dev,
"timed out on pdsp %s command register wait\n",
pdsp->name);
return ret;
}
return 0;
}
static void knav_queue_stop_pdsps(struct knav_device *kdev)
{
struct knav_pdsp_info *pdsp;
/* disable all pdsps */
for_each_pdsp(kdev, pdsp)
knav_queue_stop_pdsp(kdev, pdsp);
}
static int knav_queue_start_pdsps(struct knav_device *kdev)
{
struct knav_pdsp_info *pdsp;
int ret;
knav_queue_stop_pdsps(kdev);
/* now load them all */
for_each_pdsp(kdev, pdsp) {
ret = knav_queue_load_pdsp(kdev, pdsp);
if (ret < 0)
return ret;
}
for_each_pdsp(kdev, pdsp) {
ret = knav_queue_start_pdsp(kdev, pdsp);
WARN_ON(ret);
}
return 0;
}
static inline struct knav_qmgr_info *knav_find_qmgr(unsigned id)
{
struct knav_qmgr_info *qmgr;
for_each_qmgr(kdev, qmgr) {
if ((id >= qmgr->start_queue) &&
(id < qmgr->start_queue + qmgr->num_queues))
return qmgr;
}
return NULL;
}
static int knav_queue_init_queue(struct knav_device *kdev,
struct knav_range_info *range,
struct knav_queue_inst *inst,
unsigned id)
{
char irq_name[KNAV_NAME_SIZE];
inst->qmgr = knav_find_qmgr(id);
if (!inst->qmgr)
return -1;
INIT_LIST_HEAD(&inst->handles);
inst->kdev = kdev;
inst->range = range;
inst->irq_num = -1;
inst->id = id;
scnprintf(irq_name, sizeof(irq_name), "hwqueue-%d", id);
inst->irq_name = kstrndup(irq_name, sizeof(irq_name), GFP_KERNEL);
if (range->ops && range->ops->init_queue)
return range->ops->init_queue(range, inst);
else
return 0;
}
static int knav_queue_init_queues(struct knav_device *kdev)
{
struct knav_range_info *range;
int size, id, base_idx;
int idx = 0, ret = 0;
/* how much do we need for instance data? */
size = sizeof(struct knav_queue_inst);
/* round this up to a power of 2, keep the index to instance
* arithmetic fast.
* */
kdev->inst_shift = order_base_2(size);
size = (1 << kdev->inst_shift) * kdev->num_queues_in_use;
kdev->instances = devm_kzalloc(kdev->dev, size, GFP_KERNEL);
if (!kdev->instances)
return -1;
for_each_queue_range(kdev, range) {
if (range->ops && range->ops->init_range)
range->ops->init_range(range);
base_idx = idx;
for (id = range->queue_base;
id < range->queue_base + range->num_queues; id++, idx++) {
ret = knav_queue_init_queue(kdev, range,
knav_queue_idx_to_inst(kdev, idx), id);
if (ret < 0)
return ret;
}
range->queue_base_inst =
knav_queue_idx_to_inst(kdev, base_idx);
}
return 0;
}
static int knav_queue_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device_node *qmgrs, *queue_pools, *regions, *pdsps;
struct device *dev = &pdev->dev;
u32 temp[2];
int ret;
if (!node) {
dev_err(dev, "device tree info unavailable\n");
return -ENODEV;
}
kdev = devm_kzalloc(dev, sizeof(struct knav_device), GFP_KERNEL);
if (!kdev) {
dev_err(dev, "memory allocation failed\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, kdev);
kdev->dev = dev;
INIT_LIST_HEAD(&kdev->queue_ranges);
INIT_LIST_HEAD(&kdev->qmgrs);
INIT_LIST_HEAD(&kdev->pools);
INIT_LIST_HEAD(&kdev->regions);
INIT_LIST_HEAD(&kdev->pdsps);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
dev_err(dev, "Failed to enable QMSS\n");
return ret;
}
if (of_property_read_u32_array(node, "queue-range", temp, 2)) {
dev_err(dev, "queue-range not specified\n");
ret = -ENODEV;
goto err;
}
kdev->base_id = temp[0];
kdev->num_queues = temp[1];
/* Initialize queue managers using device tree configuration */
qmgrs = of_get_child_by_name(node, "qmgrs");
if (!qmgrs) {
dev_err(dev, "queue manager info not specified\n");
ret = -ENODEV;
goto err;
}
ret = knav_queue_init_qmgrs(kdev, qmgrs);
of_node_put(qmgrs);
if (ret)
goto err;
/* get pdsp configuration values from device tree */
pdsps = of_get_child_by_name(node, "pdsps");
if (pdsps) {
ret = knav_queue_init_pdsps(kdev, pdsps);
if (ret)
goto err;
ret = knav_queue_start_pdsps(kdev);
if (ret)
goto err;
}
of_node_put(pdsps);
/* get usable queue range values from device tree */
queue_pools = of_get_child_by_name(node, "queue-pools");
if (!queue_pools) {
dev_err(dev, "queue-pools not specified\n");
ret = -ENODEV;
goto err;
}
ret = knav_setup_queue_pools(kdev, queue_pools);
of_node_put(queue_pools);
if (ret)
goto err;
ret = knav_get_link_ram(kdev, "linkram0", &kdev->link_rams[0]);
if (ret) {
dev_err(kdev->dev, "could not setup linking ram\n");
goto err;
}
ret = knav_get_link_ram(kdev, "linkram1", &kdev->link_rams[1]);
if (ret) {
/*
* nothing really, we have one linking ram already, so we just
* live within our means
*/
}
ret = knav_queue_setup_link_ram(kdev);
if (ret)
goto err;
regions = of_get_child_by_name(node, "descriptor-regions");
if (!regions) {
dev_err(dev, "descriptor-regions not specified\n");
goto err;
}
ret = knav_queue_setup_regions(kdev, regions);
of_node_put(regions);
if (ret)
goto err;
ret = knav_queue_init_queues(kdev);
if (ret < 0) {
dev_err(dev, "hwqueue initialization failed\n");
goto err;
}
debugfs_create_file("qmss", S_IFREG | S_IRUGO, NULL, NULL,
&knav_queue_debug_ops);
return 0;
err:
knav_queue_stop_pdsps(kdev);
knav_queue_free_regions(kdev);
knav_free_queue_ranges(kdev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
}
static int knav_queue_remove(struct platform_device *pdev)
{
/* TODO: Free resources */
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
/* Match table for of_platform binding */
static struct of_device_id keystone_qmss_of_match[] = {
{ .compatible = "ti,keystone-navigator-qmss", },
{},
};
MODULE_DEVICE_TABLE(of, keystone_qmss_of_match);
static struct platform_driver keystone_qmss_driver = {
.probe = knav_queue_probe,
.remove = knav_queue_remove,
.driver = {
.name = "keystone-navigator-qmss",
.owner = THIS_MODULE,
.of_match_table = keystone_qmss_of_match,
},
};
module_platform_driver(keystone_qmss_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("TI QMSS driver for Keystone SOCs");
MODULE_AUTHOR("Sandeep Nair <sandeep_n@ti.com>");
MODULE_AUTHOR("Santosh Shilimkar <santosh.shilimkar@ti.com>");
#
# ARM Versatile SoC drivers
#
config SOC_REALVIEW
bool "SoC bus device for the ARM RealView platforms"
depends on ARCH_REALVIEW
select SOC_BUS
help
Include support for the SoC bus on the ARM RealView platforms
providing some sysfs information about the ASIC variant.
obj-$(CONFIG_SOC_REALVIEW) += soc-realview.o
/*
* Copyright (C) 2014 Linaro Ltd.
*
* Author: Linus Walleij <linus.walleij@linaro.org>
*
* 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/init.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/of.h>
/* System ID in syscon */
#define REALVIEW_SYS_ID_OFFSET 0x00
static const struct of_device_id realview_soc_of_match[] = {
{ .compatible = "arm,realview-eb-soc", },
{ .compatible = "arm,realview-pb1176-soc", },
{ .compatible = "arm,realview-pb11mp-soc", },
{ .compatible = "arm,realview-pba8-soc", },
{ .compatible = "arm,realview-pbx-soc", },
};
static u32 realview_coreid;
static const char *realview_board_str(u32 id)
{
switch ((id >> 16) & 0xfff) {
case 0x0147:
return "HBI-0147";
default:
return "Unknown";
}
}
static const char *realview_arch_str(u32 id)
{
switch ((id >> 8) & 0xf) {
case 0x05:
return "Multi-layer AXI";
default:
return "Unknown";
}
}
static ssize_t realview_get_manf(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%02x\n", realview_coreid >> 24);
}
static struct device_attribute realview_manf_attr =
__ATTR(manufacturer, S_IRUGO, realview_get_manf, NULL);
static ssize_t realview_get_board(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%s\n", realview_board_str(realview_coreid));
}
static struct device_attribute realview_board_attr =
__ATTR(board, S_IRUGO, realview_get_board, NULL);
static ssize_t realview_get_arch(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%s\n", realview_arch_str(realview_coreid));
}
static struct device_attribute realview_arch_attr =
__ATTR(fpga, S_IRUGO, realview_get_arch, NULL);
static ssize_t realview_get_build(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%02x\n", (realview_coreid & 0xFF));
}
static struct device_attribute realview_build_attr =
__ATTR(build, S_IRUGO, realview_get_build, NULL);
static int realview_soc_probe(struct platform_device *pdev)
{
static struct regmap *syscon_regmap;
struct soc_device *soc_dev;
struct soc_device_attribute *soc_dev_attr;
struct device_node *np = pdev->dev.of_node;
int ret;
syscon_regmap = syscon_regmap_lookup_by_phandle(np, "regmap");
if (IS_ERR(syscon_regmap))
return PTR_ERR(syscon_regmap);
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return -ENOMEM;
ret = of_property_read_string(np, "compatible",
&soc_dev_attr->soc_id);
if (ret)
return -EINVAL;
soc_dev_attr->machine = "RealView";
soc_dev_attr->family = "Versatile";
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
kfree(soc_dev_attr);
return -ENODEV;
}
ret = regmap_read(syscon_regmap, REALVIEW_SYS_ID_OFFSET,
&realview_coreid);
if (ret)
return -ENODEV;
device_create_file(soc_device_to_device(soc_dev), &realview_manf_attr);
device_create_file(soc_device_to_device(soc_dev), &realview_board_attr);
device_create_file(soc_device_to_device(soc_dev), &realview_arch_attr);
device_create_file(soc_device_to_device(soc_dev), &realview_build_attr);
dev_info(&pdev->dev, "RealView Syscon Core ID: 0x%08x\n",
realview_coreid);
/* FIXME: add attributes for SoC to sysfs */
return 0;
}
static struct platform_driver realview_soc_driver = {
.probe = realview_soc_probe,
.driver = {
.name = "realview-soc",
.of_match_table = realview_soc_of_match,
},
};
module_platform_driver(realview_soc_driver);
/**
* irq-omap-intc.h - INTC Idle Functions
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
*
* Author: Felipe Balbi <balbi@ti.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 of
* the License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H
#define __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H
void omap2_init_irq(void);
void omap3_init_irq(void);
void ti81xx_init_irq(void);
int omap_irq_pending(void);
void omap_intc_save_context(void);
void omap_intc_restore_context(void);
void omap3_intc_suspend(void);
void omap3_intc_prepare_idle(void);
void omap3_intc_resume_idle(void);
#endif /* __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H */
/*
* Copyright (C) 2014 Texas Instruments Incorporated
* Authors: Sandeep Nair <sandeep_n@ti.com
* Cyril Chemparathy <cyril@ti.com
Santosh Shilimkar <santosh.shilimkar@ti.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 Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_TI_KEYSTONE_NAVIGATOR_DMA_H__
#define __SOC_TI_KEYSTONE_NAVIGATOR_DMA_H__
/*
* PKTDMA descriptor manipulation macros for host packet descriptor
*/
#define MASK(x) (BIT(x) - 1)
#define KNAV_DMA_DESC_PKT_LEN_MASK MASK(22)
#define KNAV_DMA_DESC_PKT_LEN_SHIFT 0
#define KNAV_DMA_DESC_PS_INFO_IN_SOP BIT(22)
#define KNAV_DMA_DESC_PS_INFO_IN_DESC 0
#define KNAV_DMA_DESC_TAG_MASK MASK(8)
#define KNAV_DMA_DESC_SAG_HI_SHIFT 24
#define KNAV_DMA_DESC_STAG_LO_SHIFT 16
#define KNAV_DMA_DESC_DTAG_HI_SHIFT 8
#define KNAV_DMA_DESC_DTAG_LO_SHIFT 0
#define KNAV_DMA_DESC_HAS_EPIB BIT(31)
#define KNAV_DMA_DESC_NO_EPIB 0
#define KNAV_DMA_DESC_PSLEN_SHIFT 24
#define KNAV_DMA_DESC_PSLEN_MASK MASK(6)
#define KNAV_DMA_DESC_ERR_FLAG_SHIFT 20
#define KNAV_DMA_DESC_ERR_FLAG_MASK MASK(4)
#define KNAV_DMA_DESC_PSFLAG_SHIFT 16
#define KNAV_DMA_DESC_PSFLAG_MASK MASK(4)
#define KNAV_DMA_DESC_RETQ_SHIFT 0
#define KNAV_DMA_DESC_RETQ_MASK MASK(14)
#define KNAV_DMA_DESC_BUF_LEN_MASK MASK(22)
#define KNAV_DMA_NUM_EPIB_WORDS 4
#define KNAV_DMA_NUM_PS_WORDS 16
#define KNAV_DMA_FDQ_PER_CHAN 4
/* Tx channel scheduling priority */
enum knav_dma_tx_priority {
DMA_PRIO_HIGH = 0,
DMA_PRIO_MED_H,
DMA_PRIO_MED_L,
DMA_PRIO_LOW
};
/* Rx channel error handling mode during buffer starvation */
enum knav_dma_rx_err_mode {
DMA_DROP = 0,
DMA_RETRY
};
/* Rx flow size threshold configuration */
enum knav_dma_rx_thresholds {
DMA_THRESH_NONE = 0,
DMA_THRESH_0 = 1,
DMA_THRESH_0_1 = 3,
DMA_THRESH_0_1_2 = 7
};
/* Descriptor type */
enum knav_dma_desc_type {
DMA_DESC_HOST = 0,
DMA_DESC_MONOLITHIC = 2
};
/**
* struct knav_dma_tx_cfg: Tx channel configuration
* @filt_einfo: Filter extended packet info
* @filt_pswords: Filter PS words present
* @knav_dma_tx_priority: Tx channel scheduling priority
*/
struct knav_dma_tx_cfg {
bool filt_einfo;
bool filt_pswords;
enum knav_dma_tx_priority priority;
};
/**
* struct knav_dma_rx_cfg: Rx flow configuration
* @einfo_present: Extended packet info present
* @psinfo_present: PS words present
* @knav_dma_rx_err_mode: Error during buffer starvation
* @knav_dma_desc_type: Host or Monolithic desc
* @psinfo_at_sop: PS word located at start of packet
* @sop_offset: Start of packet offset
* @dst_q: Destination queue for a given flow
* @thresh: Rx flow size threshold
* @fdq[]: Free desc Queue array
* @sz_thresh0: RX packet size threshold 0
* @sz_thresh1: RX packet size threshold 1
* @sz_thresh2: RX packet size threshold 2
*/
struct knav_dma_rx_cfg {
bool einfo_present;
bool psinfo_present;
enum knav_dma_rx_err_mode err_mode;
enum knav_dma_desc_type desc_type;
bool psinfo_at_sop;
unsigned int sop_offset;
unsigned int dst_q;
enum knav_dma_rx_thresholds thresh;
unsigned int fdq[KNAV_DMA_FDQ_PER_CHAN];
unsigned int sz_thresh0;
unsigned int sz_thresh1;
unsigned int sz_thresh2;
};
/**
* struct knav_dma_cfg: Pktdma channel configuration
* @sl_cfg: Slave configuration
* @tx: Tx channel configuration
* @rx: Rx flow configuration
*/
struct knav_dma_cfg {
enum dma_transfer_direction direction;
union {
struct knav_dma_tx_cfg tx;
struct knav_dma_rx_cfg rx;
} u;
};
/**
* struct knav_dma_desc: Host packet descriptor layout
* @desc_info: Descriptor information like id, type, length
* @tag_info: Flow tag info written in during RX
* @packet_info: Queue Manager, policy, flags etc
* @buff_len: Buffer length in bytes
* @buff: Buffer pointer
* @next_desc: For chaining the descriptors
* @orig_len: length since 'buff_len' can be overwritten
* @orig_buff: buff pointer since 'buff' can be overwritten
* @epib: Extended packet info block
* @psdata: Protocol specific
*/
struct knav_dma_desc {
u32 desc_info;
u32 tag_info;
u32 packet_info;
u32 buff_len;
u32 buff;
u32 next_desc;
u32 orig_len;
u32 orig_buff;
u32 epib[KNAV_DMA_NUM_EPIB_WORDS];
u32 psdata[KNAV_DMA_NUM_PS_WORDS];
u32 pad[4];
} ____cacheline_aligned;
#if IS_ENABLED(CONFIG_KEYSTONE_NAVIGATOR_DMA)
void *knav_dma_open_channel(struct device *dev, const char *name,
struct knav_dma_cfg *config);
void knav_dma_close_channel(void *channel);
#else
static inline void *knav_dma_open_channel(struct device *dev, const char *name,
struct knav_dma_cfg *config)
{
return (void *) NULL;
}
static inline void knav_dma_close_channel(void *channel)
{}
#endif
#endif /* __SOC_TI_KEYSTONE_NAVIGATOR_DMA_H__ */
/*
* Keystone Navigator Queue Management Sub-System header
*
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
* Author: Sandeep Nair <sandeep_n@ti.com>
* Cyril Chemparathy <cyril@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.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 Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __SOC_TI_KNAV_QMSS_H__
#define __SOC_TI_KNAV_QMSS_H__
#include <linux/err.h>
#include <linux/time.h>
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/fcntl.h>
#include <linux/dma-mapping.h>
/* queue types */
#define KNAV_QUEUE_QPEND ((unsigned)-2) /* interruptible qpend queue */
#define KNAV_QUEUE_ACC ((unsigned)-3) /* Accumulated queue */
#define KNAV_QUEUE_GP ((unsigned)-4) /* General purpose queue */
/* queue flags */
#define KNAV_QUEUE_SHARED 0x0001 /* Queue can be shared */
/**
* enum knav_queue_ctrl_cmd - queue operations.
* @KNAV_QUEUE_GET_ID: Get the ID number for an open queue
* @KNAV_QUEUE_FLUSH: forcibly empty a queue if possible
* @KNAV_QUEUE_SET_NOTIFIER: Set a notifier callback to a queue handle.
* @KNAV_QUEUE_ENABLE_NOTIFY: Enable notifier callback for a queue handle.
* @KNAV_QUEUE_DISABLE_NOTIFY: Disable notifier callback for a queue handle.
* @KNAV_QUEUE_GET_COUNT: Get number of queues.
*/
enum knav_queue_ctrl_cmd {
KNAV_QUEUE_GET_ID,
KNAV_QUEUE_FLUSH,
KNAV_QUEUE_SET_NOTIFIER,
KNAV_QUEUE_ENABLE_NOTIFY,
KNAV_QUEUE_DISABLE_NOTIFY,
KNAV_QUEUE_GET_COUNT
};
/* Queue notifier callback prototype */
typedef void (*knav_queue_notify_fn)(void *arg);
/**
* struct knav_queue_notify_config: Notifier configuration
* @fn: Notifier function
* @fn_arg: Notifier function arguments
*/
struct knav_queue_notify_config {
knav_queue_notify_fn fn;
void *fn_arg;
};
void *knav_queue_open(const char *name, unsigned id,
unsigned flags);
void knav_queue_close(void *qhandle);
int knav_queue_device_control(void *qhandle,
enum knav_queue_ctrl_cmd cmd,
unsigned long arg);
dma_addr_t knav_queue_pop(void *qhandle, unsigned *size);
int knav_queue_push(void *qhandle, dma_addr_t dma,
unsigned size, unsigned flags);
void *knav_pool_create(const char *name,
int num_desc, int region_id);
void knav_pool_destroy(void *ph);
int knav_pool_count(void *ph);
void *knav_pool_desc_get(void *ph);
void knav_pool_desc_put(void *ph, void *desc);
int knav_pool_desc_map(void *ph, void *desc, unsigned size,
dma_addr_t *dma, unsigned *dma_sz);
void *knav_pool_desc_unmap(void *ph, dma_addr_t dma, unsigned dma_sz);
dma_addr_t knav_pool_desc_virt_to_dma(void *ph, void *virt);
void *knav_pool_desc_dma_to_virt(void *ph, dma_addr_t dma);
#endif /* __SOC_TI_KNAV_QMSS_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment