diff options
125 files changed, 12989 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/c6x/clocks.txt b/Documentation/devicetree/bindings/c6x/clocks.txt new file mode 100644 index 000000000000..a04f5fd30122 --- /dev/null +++ b/Documentation/devicetree/bindings/c6x/clocks.txt | |||
@@ -0,0 +1,40 @@ | |||
1 | C6X PLL Clock Controllers | ||
2 | ------------------------- | ||
3 | |||
4 | This is a first-cut support for the SoC clock controllers. This is still | ||
5 | under development and will probably change as the common device tree | ||
6 | clock support is added to the kernel. | ||
7 | |||
8 | Required properties: | ||
9 | |||
10 | - compatible: "ti,c64x+pll" | ||
11 | May also have SoC-specific value to support SoC-specific initialization | ||
12 | in the driver. One of: | ||
13 | "ti,c6455-pll" | ||
14 | "ti,c6457-pll" | ||
15 | "ti,c6472-pll" | ||
16 | "ti,c6474-pll" | ||
17 | |||
18 | - reg: base address and size of register area | ||
19 | - clock-frequency: input clock frequency in hz | ||
20 | |||
21 | |||
22 | Optional properties: | ||
23 | |||
24 | - ti,c64x+pll-bypass-delay: CPU cycles to delay when entering bypass mode | ||
25 | |||
26 | - ti,c64x+pll-reset-delay: CPU cycles to delay after PLL reset | ||
27 | |||
28 | - ti,c64x+pll-lock-delay: CPU cycles to delay after PLL frequency change | ||
29 | |||
30 | Example: | ||
31 | |||
32 | clock-controller@29a0000 { | ||
33 | compatible = "ti,c6472-pll", "ti,c64x+pll"; | ||
34 | reg = <0x029a0000 0x200>; | ||
35 | clock-frequency = <25000000>; | ||
36 | |||
37 | ti,c64x+pll-bypass-delay = <200>; | ||
38 | ti,c64x+pll-reset-delay = <12000>; | ||
39 | ti,c64x+pll-lock-delay = <80000>; | ||
40 | }; | ||
diff --git a/Documentation/devicetree/bindings/c6x/dscr.txt b/Documentation/devicetree/bindings/c6x/dscr.txt new file mode 100644 index 000000000000..d847758f2b20 --- /dev/null +++ b/Documentation/devicetree/bindings/c6x/dscr.txt | |||
@@ -0,0 +1,127 @@ | |||
1 | Device State Configuration Registers | ||
2 | ------------------------------------ | ||
3 | |||
4 | TI C6X SoCs contain a region of miscellaneous registers which provide various | ||
5 | function for SoC control or status. Details vary considerably among from SoC | ||
6 | to SoC with no two being alike. | ||
7 | |||
8 | In general, the Device State Configuraion Registers (DSCR) will provide one or | ||
9 | more configuration registers often protected by a lock register where one or | ||
10 | more key values must be written to a lock register in order to unlock the | ||
11 | configuration register for writes. These configuration register may be used to | ||
12 | enable (and disable in some cases) SoC pin drivers, select peripheral clock | ||
13 | sources (internal or pin), etc. In some cases, a configuration register is | ||
14 | write once or the individual bits are write once. In addition to device config, | ||
15 | the DSCR block may provide registers which which are used to reset peripherals, | ||
16 | provide device ID information, provide ethernet MAC addresses, as well as other | ||
17 | miscellaneous functions. | ||
18 | |||
19 | For device state control (enable/disable), each device control is assigned an | ||
20 | id which is used by individual device drivers to control the state as needed. | ||
21 | |||
22 | Required properties: | ||
23 | |||
24 | - compatible: must be "ti,c64x+dscr" | ||
25 | - reg: register area base and size | ||
26 | |||
27 | Optional properties: | ||
28 | |||
29 | NOTE: These are optional in that not all SoCs will have all properties. For | ||
30 | SoCs which do support a given property, leaving the property out of the | ||
31 | device tree will result in reduced functionality or possibly driver | ||
32 | failure. | ||
33 | |||
34 | - ti,dscr-devstat | ||
35 | offset of the devstat register | ||
36 | |||
37 | - ti,dscr-silicon-rev | ||
38 | offset, start bit, and bitsize of silicon revision field | ||
39 | |||
40 | - ti,dscr-rmii-resets | ||
41 | offset and bitmask of RMII reset field. May have multiple tuples if more | ||
42 | than one ethernet port is available. | ||
43 | |||
44 | - ti,dscr-locked-regs | ||
45 | possibly multiple tuples describing registers which are write protected by | ||
46 | a lock register. Each tuple consists of the register offset, lock register | ||
47 | offsset, and the key value used to unlock the register. | ||
48 | |||
49 | - ti,dscr-kick-regs | ||
50 | offset and key values of two "kick" registers used to write protect other | ||
51 | registers in DSCR. On SoCs using kick registers, the first key must be | ||
52 | written to the first kick register and the second key must be written to | ||
53 | the second register before other registers in the area are write-enabled. | ||
54 | |||
55 | - ti,dscr-mac-fuse-regs | ||
56 | MAC addresses are contained in two registers. Each element of a MAC address | ||
57 | is contained in a single byte. This property has two tuples. Each tuple has | ||
58 | a register offset and four cells representing bytes in the register from | ||
59 | most significant to least. The value of these four cells is the MAC byte | ||
60 | index (1-6) of the byte within the register. A value of 0 means the byte | ||
61 | is unused in the MAC address. | ||
62 | |||
63 | - ti,dscr-devstate-ctl-regs | ||
64 | This property describes the bitfields used to control the state of devices. | ||
65 | Each tuple describes a range of identical bitfields used to control one or | ||
66 | more devices (one bitfield per device). The layout of each tuple is: | ||
67 | |||
68 | start_id num_ids reg enable disable start_bit nbits | ||
69 | |||
70 | Where: | ||
71 | start_id is device id for the first device control in the range | ||
72 | num_ids is the number of device controls in the range | ||
73 | reg is the offset of the register holding the control bits | ||
74 | enable is the value to enable a device | ||
75 | disable is the value to disable a device (0xffffffff if cannot disable) | ||
76 | start_bit is the bit number of the first bit in the range | ||
77 | nbits is the number of bits per device control | ||
78 | |||
79 | - ti,dscr-devstate-stat-regs | ||
80 | This property describes the bitfields used to provide device state status | ||
81 | for device states controlled by the DSCR. Each tuple describes a range of | ||
82 | identical bitfields used to provide status for one or more devices (one | ||
83 | bitfield per device). The layout of each tuple is: | ||
84 | |||
85 | start_id num_ids reg enable disable start_bit nbits | ||
86 | |||
87 | Where: | ||
88 | start_id is device id for the first device status in the range | ||
89 | num_ids is the number of devices covered by the range | ||
90 | reg is the offset of the register holding the status bits | ||
91 | enable is the value indicating device is enabled | ||
92 | disable is the value indicating device is disabled | ||
93 | start_bit is the bit number of the first bit in the range | ||
94 | nbits is the number of bits per device status | ||
95 | |||
96 | - ti,dscr-privperm | ||
97 | Offset and default value for register used to set access privilege for | ||
98 | some SoC devices. | ||
99 | |||
100 | |||
101 | Example: | ||
102 | |||
103 | device-state-config-regs@2a80000 { | ||
104 | compatible = "ti,c64x+dscr"; | ||
105 | reg = <0x02a80000 0x41000>; | ||
106 | |||
107 | ti,dscr-devstat = <0>; | ||
108 | ti,dscr-silicon-rev = <8 28 0xf>; | ||
109 | ti,dscr-rmii-resets = <0x40020 0x00040000>; | ||
110 | |||
111 | ti,dscr-locked-regs = <0x40008 0x40004 0x0f0a0b00>; | ||
112 | ti,dscr-devstate-ctl-regs = | ||
113 | <0 12 0x40008 1 0 0 2 | ||
114 | 12 1 0x40008 3 0 30 2 | ||
115 | 13 2 0x4002c 1 0xffffffff 0 1>; | ||
116 | ti,dscr-devstate-stat-regs = | ||
117 | <0 10 0x40014 1 0 0 3 | ||
118 | 10 2 0x40018 1 0 0 3>; | ||
119 | |||
120 | ti,dscr-mac-fuse-regs = <0x700 1 2 3 4 | ||
121 | 0x704 5 6 0 0>; | ||
122 | |||
123 | ti,dscr-privperm = <0x41c 0xaaaaaaaa>; | ||
124 | |||
125 | ti,dscr-kick-regs = <0x38 0x83E70B13 | ||
126 | 0x3c 0x95A4F1E0>; | ||
127 | }; | ||
diff --git a/Documentation/devicetree/bindings/c6x/emifa.txt b/Documentation/devicetree/bindings/c6x/emifa.txt new file mode 100644 index 000000000000..0ff6e9b9a13f --- /dev/null +++ b/Documentation/devicetree/bindings/c6x/emifa.txt | |||
@@ -0,0 +1,62 @@ | |||
1 | External Memory Interface | ||
2 | ------------------------- | ||
3 | |||
4 | The emifa node describes a simple external bus controller found on some C6X | ||
5 | SoCs. This interface provides external busses with a number of chip selects. | ||
6 | |||
7 | Required properties: | ||
8 | |||
9 | - compatible: must be "ti,c64x+emifa", "simple-bus" | ||
10 | - reg: register area base and size | ||
11 | - #address-cells: must be 2 (chip-select + offset) | ||
12 | - #size-cells: must be 1 | ||
13 | - ranges: mapping from EMIFA space to parent space | ||
14 | |||
15 | |||
16 | Optional properties: | ||
17 | |||
18 | - ti,dscr-dev-enable: Device ID if EMIF is enabled/disabled from DSCR | ||
19 | |||
20 | - ti,emifa-burst-priority: | ||
21 | Number of memory transfers after which the EMIF will elevate the priority | ||
22 | of the oldest command in the command FIFO. Setting this field to 255 | ||
23 | disables this feature, thereby allowing old commands to stay in the FIFO | ||
24 | indefinitely. | ||
25 | |||
26 | - ti,emifa-ce-config: | ||
27 | Configuration values for each of the supported chip selects. | ||
28 | |||
29 | Example: | ||
30 | |||
31 | emifa@70000000 { | ||
32 | compatible = "ti,c64x+emifa", "simple-bus"; | ||
33 | #address-cells = <2>; | ||
34 | #size-cells = <1>; | ||
35 | reg = <0x70000000 0x100>; | ||
36 | ranges = <0x2 0x0 0xa0000000 0x00000008 | ||
37 | 0x3 0x0 0xb0000000 0x00400000 | ||
38 | 0x4 0x0 0xc0000000 0x10000000 | ||
39 | 0x5 0x0 0xD0000000 0x10000000>; | ||
40 | |||
41 | ti,dscr-dev-enable = <13>; | ||
42 | ti,emifa-burst-priority = <255>; | ||
43 | ti,emifa-ce-config = <0x00240120 | ||
44 | 0x00240120 | ||
45 | 0x00240122 | ||
46 | 0x00240122>; | ||
47 | |||
48 | flash@3,0 { | ||
49 | #address-cells = <1>; | ||
50 | #size-cells = <1>; | ||
51 | compatible = "cfi-flash"; | ||
52 | reg = <0x3 0x0 0x400000>; | ||
53 | bank-width = <1>; | ||
54 | device-width = <1>; | ||
55 | partition@0 { | ||
56 | reg = <0x0 0x400000>; | ||
57 | label = "NOR"; | ||
58 | }; | ||
59 | }; | ||
60 | }; | ||
61 | |||
62 | This shows a flash chip attached to chip select 3. | ||
diff --git a/Documentation/devicetree/bindings/c6x/interrupt.txt b/Documentation/devicetree/bindings/c6x/interrupt.txt new file mode 100644 index 000000000000..42bb796cc4ad --- /dev/null +++ b/Documentation/devicetree/bindings/c6x/interrupt.txt | |||
@@ -0,0 +1,104 @@ | |||
1 | C6X Interrupt Chips | ||
2 | ------------------- | ||
3 | |||
4 | * C64X+ Core Interrupt Controller | ||
5 | |||
6 | The core interrupt controller provides 16 prioritized interrupts to the | ||
7 | C64X+ core. Priority 0 and 1 are used for reset and NMI respectively. | ||
8 | Priority 2 and 3 are reserved. Priority 4-15 are used for interrupt | ||
9 | sources coming from outside the core. | ||
10 | |||
11 | Required properties: | ||
12 | -------------------- | ||
13 | - compatible: Should be "ti,c64x+core-pic"; | ||
14 | - #interrupt-cells: <1> | ||
15 | |||
16 | Interrupt Specifier Definition | ||
17 | ------------------------------ | ||
18 | Single cell specifying the core interrupt priority level (4-15) where | ||
19 | 4 is highest priority and 15 is lowest priority. | ||
20 | |||
21 | Example | ||
22 | ------- | ||
23 | core_pic: interrupt-controller@0 { | ||
24 | interrupt-controller; | ||
25 | #interrupt-cells = <1>; | ||
26 | compatible = "ti,c64x+core-pic"; | ||
27 | }; | ||
28 | |||
29 | |||
30 | |||
31 | * C64x+ Megamodule Interrupt Controller | ||
32 | |||
33 | The megamodule PIC consists of four interrupt mupliplexers each of which | ||
34 | combine up to 32 interrupt inputs into a single interrupt output which | ||
35 | may be cascaded into the core interrupt controller. The megamodule PIC | ||
36 | has a total of 12 outputs cascading into the core interrupt controller. | ||
37 | One for each core interrupt priority level. In addition to the combined | ||
38 | interrupt sources, individual megamodule interrupts may be cascaded to | ||
39 | the core interrupt controller. When an individual interrupt is cascaded, | ||
40 | it is no longer handled through a megamodule interrupt combiner and is | ||
41 | considered to have the core interrupt controller as the parent. | ||
42 | |||
43 | Required properties: | ||
44 | -------------------- | ||
45 | - compatible: "ti,c64x+megamod-pic" | ||
46 | - interrupt-controller | ||
47 | - #interrupt-cells: <1> | ||
48 | - reg: base address and size of register area | ||
49 | - interrupt-parent: must be core interrupt controller | ||
50 | - interrupts: This should have four cells; one for each interrupt combiner. | ||
51 | The cells contain the core priority interrupt to which the | ||
52 | corresponding combiner output is wired. | ||
53 | |||
54 | Optional properties: | ||
55 | -------------------- | ||
56 | - ti,c64x+megamod-pic-mux: Array of 12 cells correspnding to the 12 core | ||
57 | priority interrupts. The first cell corresponds to | ||
58 | core priority 4 and the last cell corresponds to | ||
59 | core priority 15. The value of each cell is the | ||
60 | megamodule interrupt source which is MUXed to | ||
61 | the core interrupt corresponding to the cell | ||
62 | position. Allowed values are 4 - 127. Mapping for | ||
63 | interrupts 0 - 3 (combined interrupt sources) are | ||
64 | ignored. | ||
65 | |||
66 | Interrupt Specifier Definition | ||
67 | ------------------------------ | ||
68 | Single cell specifying the megamodule interrupt source (4-127). Note that | ||
69 | interrupts mapped directly to the core with "ti,c64x+megamod-pic-mux" will | ||
70 | use the core interrupt controller as their parent and the specifier will | ||
71 | be the core priority level, not the megamodule interrupt number. | ||
72 | |||
73 | Examples | ||
74 | -------- | ||
75 | megamod_pic: interrupt-controller@1800000 { | ||
76 | compatible = "ti,c64x+megamod-pic"; | ||
77 | interrupt-controller; | ||
78 | #interrupt-cells = <1>; | ||
79 | reg = <0x1800000 0x1000>; | ||
80 | interrupt-parent = <&core_pic>; | ||
81 | interrupts = < 12 13 14 15 >; | ||
82 | }; | ||
83 | |||
84 | This is a minimal example where all individual interrupts go through a | ||
85 | combiner. Combiner-0 is mapped to core interrupt 12, combiner-1 is mapped | ||
86 | to interrupt 13, etc. | ||
87 | |||
88 | |||
89 | megamod_pic: interrupt-controller@1800000 { | ||
90 | compatible = "ti,c64x+megamod-pic"; | ||
91 | interrupt-controller; | ||
92 | #interrupt-cells = <1>; | ||
93 | reg = <0x1800000 0x1000>; | ||
94 | interrupt-parent = <&core_pic>; | ||
95 | interrupts = < 12 13 14 15 >; | ||
96 | ti,c64x+megamod-pic-mux = < 0 0 0 0 | ||
97 | 32 0 0 0 | ||
98 | 0 0 0 0 >; | ||
99 | }; | ||
100 | |||
101 | This the same as the first example except that megamodule interrupt 32 is | ||
102 | mapped directly to core priority interrupt 8. The node using this interrupt | ||
103 | must set the core controller as its interrupt parent and use 8 in the | ||
104 | interrupt specifier value. | ||
diff --git a/Documentation/devicetree/bindings/c6x/soc.txt b/Documentation/devicetree/bindings/c6x/soc.txt new file mode 100644 index 000000000000..b1e4973b5769 --- /dev/null +++ b/Documentation/devicetree/bindings/c6x/soc.txt | |||
@@ -0,0 +1,28 @@ | |||
1 | C6X System-on-Chip | ||
2 | ------------------ | ||
3 | |||
4 | Required properties: | ||
5 | |||
6 | - compatible: "simple-bus" | ||
7 | - #address-cells: must be 1 | ||
8 | - #size-cells: must be 1 | ||
9 | - ranges | ||
10 | |||
11 | Optional properties: | ||
12 | |||
13 | - model: specific SoC model | ||
14 | |||
15 | - nodes for IP blocks within SoC | ||
16 | |||
17 | |||
18 | Example: | ||
19 | |||
20 | soc { | ||
21 | compatible = "simple-bus"; | ||
22 | model = "tms320c6455"; | ||
23 | #address-cells = <1>; | ||
24 | #size-cells = <1>; | ||
25 | ranges; | ||
26 | |||
27 | ... | ||
28 | }; | ||
diff --git a/Documentation/devicetree/bindings/c6x/timer64.txt b/Documentation/devicetree/bindings/c6x/timer64.txt new file mode 100644 index 000000000000..95911fe70224 --- /dev/null +++ b/Documentation/devicetree/bindings/c6x/timer64.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | Timer64 | ||
2 | ------- | ||
3 | |||
4 | The timer64 node describes C6X event timers. | ||
5 | |||
6 | Required properties: | ||
7 | |||
8 | - compatible: must be "ti,c64x+timer64" | ||
9 | - reg: base address and size of register region | ||
10 | - interrupt-parent: interrupt controller | ||
11 | - interrupts: interrupt id | ||
12 | |||
13 | Optional properties: | ||
14 | |||
15 | - ti,dscr-dev-enable: Device ID used to enable timer IP through DSCR interface. | ||
16 | |||
17 | - ti,core-mask: on multi-core SoCs, bitmask of cores allowed to use this timer. | ||
18 | |||
19 | Example: | ||
20 | timer0: timer@25e0000 { | ||
21 | compatible = "ti,c64x+timer64"; | ||
22 | ti,core-mask = < 0x01 >; | ||
23 | reg = <0x25e0000 0x40>; | ||
24 | interrupt-parent = <&megamod_pic>; | ||
25 | interrupts = < 16 >; | ||
26 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 0460f6550e4b..311b0c405572 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -1645,6 +1645,14 @@ T: git git://git.alsa-project.org/alsa-kernel.git | |||
1645 | S: Maintained | 1645 | S: Maintained |
1646 | F: sound/pci/oxygen/ | 1646 | F: sound/pci/oxygen/ |
1647 | 1647 | ||
1648 | C6X ARCHITECTURE | ||
1649 | M: Mark Salter <msalter@redhat.com> | ||
1650 | M: Aurelien Jacquiot <a-jacquiot@ti.com> | ||
1651 | L: linux-c6x-dev@linux-c6x.org | ||
1652 | W: http://www.linux-c6x.org/wiki/index.php/Main_Page | ||
1653 | S: Maintained | ||
1654 | F: arch/c6x/ | ||
1655 | |||
1648 | CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS | 1656 | CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS |
1649 | M: David Howells <dhowells@redhat.com> | 1657 | M: David Howells <dhowells@redhat.com> |
1650 | L: linux-cachefs@redhat.com | 1658 | L: linux-cachefs@redhat.com |
diff --git a/arch/c6x/Kconfig b/arch/c6x/Kconfig new file mode 100644 index 000000000000..26e67f0f0051 --- /dev/null +++ b/arch/c6x/Kconfig | |||
@@ -0,0 +1,174 @@ | |||
1 | # | ||
2 | # For a description of the syntax of this configuration file, | ||
3 | # see Documentation/kbuild/kconfig-language.txt. | ||
4 | # | ||
5 | |||
6 | config TMS320C6X | ||
7 | def_bool y | ||
8 | select CLKDEV_LOOKUP | ||
9 | select GENERIC_IRQ_SHOW | ||
10 | select HAVE_ARCH_TRACEHOOK | ||
11 | select HAVE_DMA_API_DEBUG | ||
12 | select HAVE_GENERIC_HARDIRQS | ||
13 | select HAVE_MEMBLOCK | ||
14 | select HAVE_SPARSE_IRQ | ||
15 | select OF | ||
16 | select OF_EARLY_FLATTREE | ||
17 | |||
18 | config MMU | ||
19 | def_bool n | ||
20 | |||
21 | config ZONE_DMA | ||
22 | def_bool y | ||
23 | |||
24 | config FPU | ||
25 | def_bool n | ||
26 | |||
27 | config HIGHMEM | ||
28 | def_bool n | ||
29 | |||
30 | config NUMA | ||
31 | def_bool n | ||
32 | |||
33 | config RWSEM_GENERIC_SPINLOCK | ||
34 | def_bool y | ||
35 | |||
36 | config RWSEM_XCHGADD_ALGORITHM | ||
37 | def_bool n | ||
38 | |||
39 | config GENERIC_CALIBRATE_DELAY | ||
40 | def_bool y | ||
41 | |||
42 | config GENERIC_HWEIGHT | ||
43 | def_bool y | ||
44 | |||
45 | config GENERIC_CLOCKEVENTS | ||
46 | def_bool y | ||
47 | |||
48 | config GENERIC_CLOCKEVENTS_BROADCAST | ||
49 | bool | ||
50 | |||
51 | config GENERIC_BUG | ||
52 | def_bool y | ||
53 | |||
54 | config COMMON_CLKDEV | ||
55 | def_bool y | ||
56 | |||
57 | config C6X_BIG_KERNEL | ||
58 | bool "Build a big kernel" | ||
59 | help | ||
60 | The C6X function call instruction has a limited range of +/- 2MiB. | ||
61 | This is sufficient for most kernels, but some kernel configurations | ||
62 | with lots of compiled-in functionality may require a larger range | ||
63 | for function calls. Use this option to have the compiler generate | ||
64 | function calls with 32-bit range. This will make the kernel both | ||
65 | larger and slower. | ||
66 | |||
67 | If unsure, say N. | ||
68 | |||
69 | source "init/Kconfig" | ||
70 | |||
71 | # Use the generic interrupt handling code in kernel/irq/ | ||
72 | |||
73 | source "kernel/Kconfig.freezer" | ||
74 | |||
75 | config CMDLINE_BOOL | ||
76 | bool "Default bootloader kernel arguments" | ||
77 | |||
78 | config CMDLINE | ||
79 | string "Kernel command line" | ||
80 | depends on CMDLINE_BOOL | ||
81 | default "console=ttyS0,57600" | ||
82 | help | ||
83 | On some architectures there is currently no way for the boot loader | ||
84 | to pass arguments to the kernel. For these architectures, you should | ||
85 | supply some command-line options at build time by entering them | ||
86 | here. | ||
87 | |||
88 | config CMDLINE_FORCE | ||
89 | bool "Force default kernel command string" | ||
90 | depends on CMDLINE_BOOL | ||
91 | default n | ||
92 | help | ||
93 | Set this to have arguments from the default kernel command string | ||
94 | override those passed by the boot loader. | ||
95 | |||
96 | config CPU_BIG_ENDIAN | ||
97 | bool "Build big-endian kernel" | ||
98 | default n | ||
99 | help | ||
100 | Say Y if you plan on running a kernel in big-endian mode. | ||
101 | Note that your board must be properly built and your board | ||
102 | port must properly enable any big-endian related features | ||
103 | of your chipset/board/processor. | ||
104 | |||
105 | config FORCE_MAX_ZONEORDER | ||
106 | int "Maximum zone order" | ||
107 | default "13" | ||
108 | help | ||
109 | The kernel memory allocator divides physically contiguous memory | ||
110 | blocks into "zones", where each zone is a power of two number of | ||
111 | pages. This option selects the largest power of two that the kernel | ||
112 | keeps in the memory allocator. If you need to allocate very large | ||
113 | blocks of physically contiguous memory, then you may need to | ||
114 | increase this value. | ||
115 | |||
116 | This config option is actually maximum order plus one. For example, | ||
117 | a value of 11 means that the largest free memory block is 2^10 pages. | ||
118 | |||
119 | menu "Processor type and features" | ||
120 | |||
121 | source "arch/c6x/platforms/Kconfig" | ||
122 | |||
123 | config TMS320C6X_CACHES_ON | ||
124 | bool "L2 cache support" | ||
125 | default y | ||
126 | |||
127 | config KERNEL_RAM_BASE_ADDRESS | ||
128 | hex "Virtual address of memory base" | ||
129 | default 0xe0000000 if SOC_TMS320C6455 | ||
130 | default 0xe0000000 if SOC_TMS320C6457 | ||
131 | default 0xe0000000 if SOC_TMS320C6472 | ||
132 | default 0x80000000 | ||
133 | |||
134 | source "mm/Kconfig" | ||
135 | |||
136 | source "kernel/Kconfig.preempt" | ||
137 | |||
138 | source "kernel/Kconfig.hz" | ||
139 | source "kernel/time/Kconfig" | ||
140 | |||
141 | endmenu | ||
142 | |||
143 | menu "Executable file formats" | ||
144 | |||
145 | source "fs/Kconfig.binfmt" | ||
146 | |||
147 | endmenu | ||
148 | |||
149 | source "net/Kconfig" | ||
150 | |||
151 | source "drivers/Kconfig" | ||
152 | |||
153 | source "fs/Kconfig" | ||
154 | |||
155 | source "security/Kconfig" | ||
156 | |||
157 | source "crypto/Kconfig" | ||
158 | |||
159 | source "lib/Kconfig" | ||
160 | |||
161 | menu "Kernel hacking" | ||
162 | |||
163 | source "lib/Kconfig.debug" | ||
164 | |||
165 | config ACCESS_CHECK | ||
166 | bool "Check the user pointer address" | ||
167 | default y | ||
168 | help | ||
169 | Usually the pointer transfer from user space is checked to see if its | ||
170 | address is in the kernel space. | ||
171 | |||
172 | Say N here to disable that check to improve the performance. | ||
173 | |||
174 | endmenu | ||
diff --git a/arch/c6x/Makefile b/arch/c6x/Makefile new file mode 100644 index 000000000000..1d08dd070277 --- /dev/null +++ b/arch/c6x/Makefile | |||
@@ -0,0 +1,60 @@ | |||
1 | # | ||
2 | # linux/arch/c6x/Makefile | ||
3 | # | ||
4 | # This file is subject to the terms and conditions of the GNU General Public | ||
5 | # License. See the file "COPYING" in the main directory of this archive | ||
6 | # for more details. | ||
7 | # | ||
8 | |||
9 | cflags-y += -mno-dsbt -msdata=none | ||
10 | |||
11 | cflags-$(CONFIG_C6X_BIG_KERNEL) += -mlong-calls | ||
12 | |||
13 | CFLAGS_MODULE += -mlong-calls -mno-dsbt -msdata=none | ||
14 | |||
15 | CHECKFLAGS += | ||
16 | |||
17 | KBUILD_CFLAGS += $(cflags-y) | ||
18 | KBUILD_AFLAGS += $(cflags-y) | ||
19 | |||
20 | ifdef CONFIG_CPU_BIG_ENDIAN | ||
21 | KBUILD_CFLAGS += -mbig-endian | ||
22 | KBUILD_AFLAGS += -mbig-endian | ||
23 | LINKFLAGS += -mbig-endian | ||
24 | KBUILD_LDFLAGS += -mbig-endian | ||
25 | LDFLAGS += -EB | ||
26 | endif | ||
27 | |||
28 | head-y := arch/c6x/kernel/head.o | ||
29 | core-y += arch/c6x/kernel/ arch/c6x/mm/ arch/c6x/platforms/ | ||
30 | libs-y += arch/c6x/lib/ | ||
31 | |||
32 | # Default to vmlinux.bin, override when needed | ||
33 | all: vmlinux.bin | ||
34 | |||
35 | boot := arch/$(ARCH)/boot | ||
36 | |||
37 | # Are we making a dtbImage.<boardname> target? If so, crack out the boardname | ||
38 | DTB:=$(subst dtbImage.,,$(filter dtbImage.%, $(MAKECMDGOALS))) | ||
39 | export DTB | ||
40 | |||
41 | ifneq ($(DTB),) | ||
42 | core-y += $(boot)/ | ||
43 | endif | ||
44 | |||
45 | # With make 3.82 we cannot mix normal and wildcard targets | ||
46 | |||
47 | vmlinux.bin: vmlinux | ||
48 | $(Q)$(MAKE) $(build)=$(boot) $(patsubst %,$(boot)/%,$@) | ||
49 | |||
50 | dtbImage.%: vmlinux | ||
51 | $(Q)$(MAKE) $(build)=$(boot) $(patsubst %,$(boot)/%,$@) | ||
52 | |||
53 | archclean: | ||
54 | $(Q)$(MAKE) $(clean)=$(boot) | ||
55 | |||
56 | define archhelp | ||
57 | @echo ' vmlinux.bin - Binary kernel image (arch/$(ARCH)/boot/vmlinux.bin)' | ||
58 | @echo ' dtbImage.<dt> - ELF image with $(arch)/boot/dts/<dt>.dts linked in' | ||
59 | @echo ' - stripped elf with fdt blob' | ||
60 | endef | ||
diff --git a/arch/c6x/boot/Makefile b/arch/c6x/boot/Makefile new file mode 100644 index 000000000000..ecca820e6041 --- /dev/null +++ b/arch/c6x/boot/Makefile | |||
@@ -0,0 +1,30 @@ | |||
1 | # | ||
2 | # Makefile for bootable kernel images | ||
3 | # | ||
4 | |||
5 | OBJCOPYFLAGS_vmlinux.bin := -O binary | ||
6 | $(obj)/vmlinux.bin: vmlinux FORCE | ||
7 | $(call if_changed,objcopy) | ||
8 | |||
9 | DTC_FLAGS ?= -p 1024 | ||
10 | |||
11 | ifneq ($(DTB),) | ||
12 | obj-y += linked_dtb.o | ||
13 | endif | ||
14 | |||
15 | $(obj)/%.dtb: $(src)/dts/%.dts FORCE | ||
16 | $(call cmd,dtc) | ||
17 | |||
18 | quiet_cmd_cp = CP $< $@$2 | ||
19 | cmd_cp = cat $< >$@$2 || (rm -f $@ && echo false) | ||
20 | |||
21 | # Generate builtin.dtb from $(DTB).dtb | ||
22 | $(obj)/builtin.dtb: $(obj)/$(DTB).dtb | ||
23 | $(call if_changed,cp) | ||
24 | |||
25 | $(obj)/linked_dtb.o: $(obj)/builtin.dtb | ||
26 | |||
27 | $(obj)/dtbImage.%: vmlinux | ||
28 | $(call if_changed,objcopy) | ||
29 | |||
30 | clean-files := $(obj)/*.dtb | ||
diff --git a/arch/c6x/boot/dts/dsk6455.dts b/arch/c6x/boot/dts/dsk6455.dts new file mode 100644 index 000000000000..2b71f800618d --- /dev/null +++ b/arch/c6x/boot/dts/dsk6455.dts | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * arch/c6x/boot/dts/dsk6455.dts | ||
3 | * | ||
4 | * DSK6455 Evaluation Platform For TMS320C6455 | ||
5 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
6 | * | ||
7 | * Author: Mark Salter <msalter@redhat.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (at your option) any later version. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | /dts-v1/; | ||
17 | |||
18 | /include/ "tms320c6455.dtsi" | ||
19 | |||
20 | / { | ||
21 | model = "Spectrum Digital DSK6455"; | ||
22 | compatible = "spectrum-digital,dsk6455"; | ||
23 | |||
24 | chosen { | ||
25 | bootargs = "root=/dev/nfs ip=dhcp rw"; | ||
26 | }; | ||
27 | |||
28 | memory { | ||
29 | device_type = "memory"; | ||
30 | reg = <0xE0000000 0x08000000>; | ||
31 | }; | ||
32 | |||
33 | soc { | ||
34 | megamod_pic: interrupt-controller@1800000 { | ||
35 | interrupts = < 12 13 14 15 >; | ||
36 | }; | ||
37 | |||
38 | emifa@70000000 { | ||
39 | flash@3,0 { | ||
40 | #address-cells = <1>; | ||
41 | #size-cells = <1>; | ||
42 | compatible = "cfi-flash"; | ||
43 | reg = <0x3 0x0 0x400000>; | ||
44 | bank-width = <1>; | ||
45 | device-width = <1>; | ||
46 | partition@0 { | ||
47 | reg = <0x0 0x400000>; | ||
48 | label = "NOR"; | ||
49 | }; | ||
50 | }; | ||
51 | }; | ||
52 | |||
53 | timer1: timer@2980000 { | ||
54 | interrupt-parent = <&megamod_pic>; | ||
55 | interrupts = < 69 >; | ||
56 | }; | ||
57 | |||
58 | clock-controller@029a0000 { | ||
59 | clock-frequency = <50000000>; | ||
60 | }; | ||
61 | }; | ||
62 | }; | ||
diff --git a/arch/c6x/boot/dts/evmc6457.dts b/arch/c6x/boot/dts/evmc6457.dts new file mode 100644 index 000000000000..0301eb9a8ff8 --- /dev/null +++ b/arch/c6x/boot/dts/evmc6457.dts | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * arch/c6x/boot/dts/evmc6457.dts | ||
3 | * | ||
4 | * EVMC6457 Evaluation Platform For TMS320C6457 | ||
5 | * | ||
6 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
7 | * | ||
8 | * Author: Mark Salter <msalter@redhat.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version 2 | ||
13 | * of the License, or (at your option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | /dts-v1/; | ||
18 | |||
19 | /include/ "tms320c6457.dtsi" | ||
20 | |||
21 | / { | ||
22 | model = "eInfochips EVMC6457"; | ||
23 | compatible = "einfochips,evmc6457"; | ||
24 | |||
25 | chosen { | ||
26 | bootargs = "console=hvc root=/dev/nfs ip=dhcp rw"; | ||
27 | }; | ||
28 | |||
29 | memory { | ||
30 | device_type = "memory"; | ||
31 | reg = <0xE0000000 0x10000000>; | ||
32 | }; | ||
33 | |||
34 | soc { | ||
35 | megamod_pic: interrupt-controller@1800000 { | ||
36 | interrupts = < 12 13 14 15 >; | ||
37 | }; | ||
38 | |||
39 | timer0: timer@2940000 { | ||
40 | interrupt-parent = <&megamod_pic>; | ||
41 | interrupts = < 67 >; | ||
42 | }; | ||
43 | |||
44 | clock-controller@29a0000 { | ||
45 | clock-frequency = <60000000>; | ||
46 | }; | ||
47 | }; | ||
48 | }; | ||
diff --git a/arch/c6x/boot/dts/evmc6472.dts b/arch/c6x/boot/dts/evmc6472.dts new file mode 100644 index 000000000000..3e207b449a93 --- /dev/null +++ b/arch/c6x/boot/dts/evmc6472.dts | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * arch/c6x/boot/dts/evmc6472.dts | ||
3 | * | ||
4 | * EVMC6472 Evaluation Platform For TMS320C6472 | ||
5 | * | ||
6 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
7 | * | ||
8 | * Author: Mark Salter <msalter@redhat.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version 2 | ||
13 | * of the License, or (at your option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | /dts-v1/; | ||
18 | |||
19 | /include/ "tms320c6472.dtsi" | ||
20 | |||
21 | / { | ||
22 | model = "eInfochips EVMC6472"; | ||
23 | compatible = "einfochips,evmc6472"; | ||
24 | |||
25 | chosen { | ||
26 | bootargs = "console=hvc root=/dev/nfs ip=dhcp rw"; | ||
27 | }; | ||
28 | |||
29 | memory { | ||
30 | device_type = "memory"; | ||
31 | reg = <0xE0000000 0x10000000>; | ||
32 | }; | ||
33 | |||
34 | soc { | ||
35 | megamod_pic: interrupt-controller@1800000 { | ||
36 | interrupts = < 12 13 14 15 >; | ||
37 | }; | ||
38 | |||
39 | timer0: timer@25e0000 { | ||
40 | interrupt-parent = <&megamod_pic>; | ||
41 | interrupts = < 16 >; | ||
42 | }; | ||
43 | |||
44 | timer1: timer@25f0000 { | ||
45 | interrupt-parent = <&megamod_pic>; | ||
46 | interrupts = < 16 >; | ||
47 | }; | ||
48 | |||
49 | timer2: timer@2600000 { | ||
50 | interrupt-parent = <&megamod_pic>; | ||
51 | interrupts = < 16 >; | ||
52 | }; | ||
53 | |||
54 | timer3: timer@2610000 { | ||
55 | interrupt-parent = <&megamod_pic>; | ||
56 | interrupts = < 16 >; | ||
57 | }; | ||
58 | |||
59 | timer4: timer@2620000 { | ||
60 | interrupt-parent = <&megamod_pic>; | ||
61 | interrupts = < 16 >; | ||
62 | }; | ||
63 | |||
64 | timer5: timer@2630000 { | ||
65 | interrupt-parent = <&megamod_pic>; | ||
66 | interrupts = < 16 >; | ||
67 | }; | ||
68 | |||
69 | clock-controller@29a0000 { | ||
70 | clock-frequency = <25000000>; | ||
71 | }; | ||
72 | }; | ||
73 | }; | ||
diff --git a/arch/c6x/boot/dts/evmc6474.dts b/arch/c6x/boot/dts/evmc6474.dts new file mode 100644 index 000000000000..4dc291292bc4 --- /dev/null +++ b/arch/c6x/boot/dts/evmc6474.dts | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * arch/c6x/boot/dts/evmc6474.dts | ||
3 | * | ||
4 | * EVMC6474 Evaluation Platform For TMS320C6474 | ||
5 | * | ||
6 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
7 | * | ||
8 | * Author: Mark Salter <msalter@redhat.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version 2 | ||
13 | * of the License, or (at your option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | /dts-v1/; | ||
18 | |||
19 | /include/ "tms320c6474.dtsi" | ||
20 | |||
21 | / { | ||
22 | model = "Spectrum Digital EVMC6474"; | ||
23 | compatible = "spectrum-digital,evmc6474"; | ||
24 | |||
25 | chosen { | ||
26 | bootargs = "console=hvc root=/dev/nfs ip=dhcp rw"; | ||
27 | }; | ||
28 | |||
29 | memory { | ||
30 | device_type = "memory"; | ||
31 | reg = <0x80000000 0x08000000>; | ||
32 | }; | ||
33 | |||
34 | soc { | ||
35 | megamod_pic: interrupt-controller@1800000 { | ||
36 | interrupts = < 12 13 14 15 >; | ||
37 | }; | ||
38 | |||
39 | timer3: timer@2940000 { | ||
40 | interrupt-parent = <&megamod_pic>; | ||
41 | interrupts = < 39 >; | ||
42 | }; | ||
43 | |||
44 | timer4: timer@2950000 { | ||
45 | interrupt-parent = <&megamod_pic>; | ||
46 | interrupts = < 41 >; | ||
47 | }; | ||
48 | |||
49 | timer5: timer@2960000 { | ||
50 | interrupt-parent = <&megamod_pic>; | ||
51 | interrupts = < 43 >; | ||
52 | }; | ||
53 | |||
54 | clock-controller@29a0000 { | ||
55 | clock-frequency = <50000000>; | ||
56 | }; | ||
57 | }; | ||
58 | }; | ||
diff --git a/arch/c6x/boot/dts/tms320c6455.dtsi b/arch/c6x/boot/dts/tms320c6455.dtsi new file mode 100644 index 000000000000..a804ec1e018b --- /dev/null +++ b/arch/c6x/boot/dts/tms320c6455.dtsi | |||
@@ -0,0 +1,96 @@ | |||
1 | |||
2 | / { | ||
3 | #address-cells = <1>; | ||
4 | #size-cells = <1>; | ||
5 | |||
6 | cpus { | ||
7 | #address-cells = <1>; | ||
8 | #size-cells = <0>; | ||
9 | |||
10 | cpu@0 { | ||
11 | device_type = "cpu"; | ||
12 | model = "ti,c64x+"; | ||
13 | reg = <0>; | ||
14 | }; | ||
15 | }; | ||
16 | |||
17 | soc { | ||
18 | compatible = "simple-bus"; | ||
19 | model = "tms320c6455"; | ||
20 | #address-cells = <1>; | ||
21 | #size-cells = <1>; | ||
22 | ranges; | ||
23 | |||
24 | core_pic: interrupt-controller { | ||
25 | interrupt-controller; | ||
26 | #interrupt-cells = <1>; | ||
27 | compatible = "ti,c64x+core-pic"; | ||
28 | }; | ||
29 | |||
30 | /* | ||
31 | * Megamodule interrupt controller | ||
32 | */ | ||
33 | megamod_pic: interrupt-controller@1800000 { | ||
34 | compatible = "ti,c64x+megamod-pic"; | ||
35 | interrupt-controller; | ||
36 | #interrupt-cells = <1>; | ||
37 | reg = <0x1800000 0x1000>; | ||
38 | interrupt-parent = <&core_pic>; | ||
39 | }; | ||
40 | |||
41 | cache-controller@1840000 { | ||
42 | compatible = "ti,c64x+cache"; | ||
43 | reg = <0x01840000 0x8400>; | ||
44 | }; | ||
45 | |||
46 | emifa@70000000 { | ||
47 | compatible = "ti,c64x+emifa", "simple-bus"; | ||
48 | #address-cells = <2>; | ||
49 | #size-cells = <1>; | ||
50 | reg = <0x70000000 0x100>; | ||
51 | ranges = <0x2 0x0 0xa0000000 0x00000008 | ||
52 | 0x3 0x0 0xb0000000 0x00400000 | ||
53 | 0x4 0x0 0xc0000000 0x10000000 | ||
54 | 0x5 0x0 0xD0000000 0x10000000>; | ||
55 | |||
56 | ti,dscr-dev-enable = <13>; | ||
57 | ti,emifa-burst-priority = <255>; | ||
58 | ti,emifa-ce-config = <0x00240120 | ||
59 | 0x00240120 | ||
60 | 0x00240122 | ||
61 | 0x00240122>; | ||
62 | }; | ||
63 | |||
64 | timer1: timer@2980000 { | ||
65 | compatible = "ti,c64x+timer64"; | ||
66 | reg = <0x2980000 0x40>; | ||
67 | ti,dscr-dev-enable = <4>; | ||
68 | }; | ||
69 | |||
70 | clock-controller@029a0000 { | ||
71 | compatible = "ti,c6455-pll", "ti,c64x+pll"; | ||
72 | reg = <0x029a0000 0x200>; | ||
73 | ti,c64x+pll-bypass-delay = <1440>; | ||
74 | ti,c64x+pll-reset-delay = <15360>; | ||
75 | ti,c64x+pll-lock-delay = <24000>; | ||
76 | }; | ||
77 | |||
78 | device-state-config-regs@2a80000 { | ||
79 | compatible = "ti,c64x+dscr"; | ||
80 | reg = <0x02a80000 0x41000>; | ||
81 | |||
82 | ti,dscr-devstat = <0>; | ||
83 | ti,dscr-silicon-rev = <8 28 0xf>; | ||
84 | ti,dscr-rmii-resets = <0 0x40020 0x00040000>; | ||
85 | |||
86 | ti,dscr-locked-regs = <0x40008 0x40004 0x0f0a0b00>; | ||
87 | ti,dscr-devstate-ctl-regs = | ||
88 | <0 12 0x40008 1 0 0 2 | ||
89 | 12 1 0x40008 3 0 30 2 | ||
90 | 13 2 0x4002c 1 0xffffffff 0 1>; | ||
91 | ti,dscr-devstate-stat-regs = | ||
92 | <0 10 0x40014 1 0 0 3 | ||
93 | 10 2 0x40018 1 0 0 3>; | ||
94 | }; | ||
95 | }; | ||
96 | }; | ||
diff --git a/arch/c6x/boot/dts/tms320c6457.dtsi b/arch/c6x/boot/dts/tms320c6457.dtsi new file mode 100644 index 000000000000..35f40709a719 --- /dev/null +++ b/arch/c6x/boot/dts/tms320c6457.dtsi | |||
@@ -0,0 +1,68 @@ | |||
1 | |||
2 | / { | ||
3 | #address-cells = <1>; | ||
4 | #size-cells = <1>; | ||
5 | |||
6 | cpus { | ||
7 | #address-cells = <1>; | ||
8 | #size-cells = <0>; | ||
9 | |||
10 | cpu@0 { | ||
11 | device_type = "cpu"; | ||
12 | model = "ti,c64x+"; | ||
13 | reg = <0>; | ||
14 | }; | ||
15 | }; | ||
16 | |||
17 | soc { | ||
18 | compatible = "simple-bus"; | ||
19 | model = "tms320c6457"; | ||
20 | #address-cells = <1>; | ||
21 | #size-cells = <1>; | ||
22 | ranges; | ||
23 | |||
24 | core_pic: interrupt-controller { | ||
25 | interrupt-controller; | ||
26 | #interrupt-cells = <1>; | ||
27 | compatible = "ti,c64x+core-pic"; | ||
28 | }; | ||
29 | |||
30 | megamod_pic: interrupt-controller@1800000 { | ||
31 | compatible = "ti,c64x+megamod-pic"; | ||
32 | interrupt-controller; | ||
33 | #interrupt-cells = <1>; | ||
34 | interrupt-parent = <&core_pic>; | ||
35 | reg = <0x1800000 0x1000>; | ||
36 | }; | ||
37 | |||
38 | cache-controller@1840000 { | ||
39 | compatible = "ti,c64x+cache"; | ||
40 | reg = <0x01840000 0x8400>; | ||
41 | }; | ||
42 | |||
43 | device-state-controller@2880800 { | ||
44 | compatible = "ti,c64x+dscr"; | ||
45 | reg = <0x02880800 0x400>; | ||
46 | |||
47 | ti,dscr-devstat = <0x20>; | ||
48 | ti,dscr-silicon-rev = <0x18 28 0xf>; | ||
49 | ti,dscr-mac-fuse-regs = <0x114 3 4 5 6 | ||
50 | 0x118 0 0 1 2>; | ||
51 | ti,dscr-kick-regs = <0x38 0x83E70B13 | ||
52 | 0x3c 0x95A4F1E0>; | ||
53 | }; | ||
54 | |||
55 | timer0: timer@2940000 { | ||
56 | compatible = "ti,c64x+timer64"; | ||
57 | reg = <0x2940000 0x40>; | ||
58 | }; | ||
59 | |||
60 | clock-controller@29a0000 { | ||
61 | compatible = "ti,c6457-pll", "ti,c64x+pll"; | ||
62 | reg = <0x029a0000 0x200>; | ||
63 | ti,c64x+pll-bypass-delay = <300>; | ||
64 | ti,c64x+pll-reset-delay = <24000>; | ||
65 | ti,c64x+pll-lock-delay = <50000>; | ||
66 | }; | ||
67 | }; | ||
68 | }; | ||
diff --git a/arch/c6x/boot/dts/tms320c6472.dtsi b/arch/c6x/boot/dts/tms320c6472.dtsi new file mode 100644 index 000000000000..b488aaec65c0 --- /dev/null +++ b/arch/c6x/boot/dts/tms320c6472.dtsi | |||
@@ -0,0 +1,134 @@ | |||
1 | |||
2 | / { | ||
3 | #address-cells = <1>; | ||
4 | #size-cells = <1>; | ||
5 | |||
6 | cpus { | ||
7 | #address-cells = <1>; | ||
8 | #size-cells = <0>; | ||
9 | |||
10 | cpu@0 { | ||
11 | device_type = "cpu"; | ||
12 | reg = <0>; | ||
13 | model = "ti,c64x+"; | ||
14 | }; | ||
15 | cpu@1 { | ||
16 | device_type = "cpu"; | ||
17 | reg = <1>; | ||
18 | model = "ti,c64x+"; | ||
19 | }; | ||
20 | cpu@2 { | ||
21 | device_type = "cpu"; | ||
22 | reg = <2>; | ||
23 | model = "ti,c64x+"; | ||
24 | }; | ||
25 | cpu@3 { | ||
26 | device_type = "cpu"; | ||
27 | reg = <3>; | ||
28 | model = "ti,c64x+"; | ||
29 | }; | ||
30 | cpu@4 { | ||
31 | device_type = "cpu"; | ||
32 | reg = <4>; | ||
33 | model = "ti,c64x+"; | ||
34 | }; | ||
35 | cpu@5 { | ||
36 | device_type = "cpu"; | ||
37 | reg = <5>; | ||
38 | model = "ti,c64x+"; | ||
39 | }; | ||
40 | }; | ||
41 | |||
42 | soc { | ||
43 | compatible = "simple-bus"; | ||
44 | model = "tms320c6472"; | ||
45 | #address-cells = <1>; | ||
46 | #size-cells = <1>; | ||
47 | ranges; | ||
48 | |||
49 | core_pic: interrupt-controller { | ||
50 | compatible = "ti,c64x+core-pic"; | ||
51 | interrupt-controller; | ||
52 | #interrupt-cells = <1>; | ||
53 | }; | ||
54 | |||
55 | megamod_pic: interrupt-controller@1800000 { | ||
56 | compatible = "ti,c64x+megamod-pic"; | ||
57 | interrupt-controller; | ||
58 | #interrupt-cells = <1>; | ||
59 | reg = <0x1800000 0x1000>; | ||
60 | interrupt-parent = <&core_pic>; | ||
61 | }; | ||
62 | |||
63 | cache-controller@1840000 { | ||
64 | compatible = "ti,c64x+cache"; | ||
65 | reg = <0x01840000 0x8400>; | ||
66 | }; | ||
67 | |||
68 | timer0: timer@25e0000 { | ||
69 | compatible = "ti,c64x+timer64"; | ||
70 | ti,core-mask = < 0x01 >; | ||
71 | reg = <0x25e0000 0x40>; | ||
72 | }; | ||
73 | |||
74 | timer1: timer@25f0000 { | ||
75 | compatible = "ti,c64x+timer64"; | ||
76 | ti,core-mask = < 0x02 >; | ||
77 | reg = <0x25f0000 0x40>; | ||
78 | }; | ||
79 | |||
80 | timer2: timer@2600000 { | ||
81 | compatible = "ti,c64x+timer64"; | ||
82 | ti,core-mask = < 0x04 >; | ||
83 | reg = <0x2600000 0x40>; | ||
84 | }; | ||
85 | |||
86 | timer3: timer@2610000 { | ||
87 | compatible = "ti,c64x+timer64"; | ||
88 | ti,core-mask = < 0x08 >; | ||
89 | reg = <0x2610000 0x40>; | ||
90 | }; | ||
91 | |||
92 | timer4: timer@2620000 { | ||
93 | compatible = "ti,c64x+timer64"; | ||
94 | ti,core-mask = < 0x10 >; | ||
95 | reg = <0x2620000 0x40>; | ||
96 | }; | ||
97 | |||
98 | timer5: timer@2630000 { | ||
99 | compatible = "ti,c64x+timer64"; | ||
100 | ti,core-mask = < 0x20 >; | ||
101 | reg = <0x2630000 0x40>; | ||
102 | }; | ||
103 | |||
104 | clock-controller@29a0000 { | ||
105 | compatible = "ti,c6472-pll", "ti,c64x+pll"; | ||
106 | reg = <0x029a0000 0x200>; | ||
107 | ti,c64x+pll-bypass-delay = <200>; | ||
108 | ti,c64x+pll-reset-delay = <12000>; | ||
109 | ti,c64x+pll-lock-delay = <80000>; | ||
110 | }; | ||
111 | |||
112 | device-state-controller@2a80000 { | ||
113 | compatible = "ti,c64x+dscr"; | ||
114 | reg = <0x02a80000 0x1000>; | ||
115 | |||
116 | ti,dscr-devstat = <0>; | ||
117 | ti,dscr-silicon-rev = <0x70c 16 0xff>; | ||
118 | |||
119 | ti,dscr-mac-fuse-regs = <0x700 1 2 3 4 | ||
120 | 0x704 5 6 0 0>; | ||
121 | |||
122 | ti,dscr-rmii-resets = <0x208 1 | ||
123 | 0x20c 1>; | ||
124 | |||
125 | ti,dscr-locked-regs = <0x200 0x204 0x0a1e183a | ||
126 | 0x40c 0x420 0xbea7 | ||
127 | 0x41c 0x420 0xbea7>; | ||
128 | |||
129 | ti,dscr-privperm = <0x41c 0xaaaaaaaa>; | ||
130 | |||
131 | ti,dscr-devstate-ctl-regs = <0 13 0x200 1 0 0 1>; | ||
132 | }; | ||
133 | }; | ||
134 | }; | ||
diff --git a/arch/c6x/boot/dts/tms320c6474.dtsi b/arch/c6x/boot/dts/tms320c6474.dtsi new file mode 100644 index 000000000000..cc601bf348a1 --- /dev/null +++ b/arch/c6x/boot/dts/tms320c6474.dtsi | |||
@@ -0,0 +1,89 @@ | |||
1 | |||
2 | / { | ||
3 | #address-cells = <1>; | ||
4 | #size-cells = <1>; | ||
5 | |||
6 | cpus { | ||
7 | #address-cells = <1>; | ||
8 | #size-cells = <0>; | ||
9 | |||
10 | cpu@0 { | ||
11 | device_type = "cpu"; | ||
12 | reg = <0>; | ||
13 | model = "ti,c64x+"; | ||
14 | }; | ||
15 | cpu@1 { | ||
16 | device_type = "cpu"; | ||
17 | reg = <1>; | ||
18 | model = "ti,c64x+"; | ||
19 | }; | ||
20 | cpu@2 { | ||
21 | device_type = "cpu"; | ||
22 | reg = <2>; | ||
23 | model = "ti,c64x+"; | ||
24 | }; | ||
25 | }; | ||
26 | |||
27 | soc { | ||
28 | compatible = "simple-bus"; | ||
29 | model = "tms320c6474"; | ||
30 | #address-cells = <1>; | ||
31 | #size-cells = <1>; | ||
32 | ranges; | ||
33 | |||
34 | core_pic: interrupt-controller { | ||
35 | interrupt-controller; | ||
36 | #interrupt-cells = <1>; | ||
37 | compatible = "ti,c64x+core-pic"; | ||
38 | }; | ||
39 | |||
40 | megamod_pic: interrupt-controller@1800000 { | ||
41 | compatible = "ti,c64x+megamod-pic"; | ||
42 | interrupt-controller; | ||
43 | #interrupt-cells = <1>; | ||
44 | reg = <0x1800000 0x1000>; | ||
45 | interrupt-parent = <&core_pic>; | ||
46 | }; | ||
47 | |||
48 | cache-controller@1840000 { | ||
49 | compatible = "ti,c64x+cache"; | ||
50 | reg = <0x01840000 0x8400>; | ||
51 | }; | ||
52 | |||
53 | timer3: timer@2940000 { | ||
54 | compatible = "ti,c64x+timer64"; | ||
55 | ti,core-mask = < 0x04 >; | ||
56 | reg = <0x2940000 0x40>; | ||
57 | }; | ||
58 | |||
59 | timer4: timer@2950000 { | ||
60 | compatible = "ti,c64x+timer64"; | ||
61 | ti,core-mask = < 0x02 >; | ||
62 | reg = <0x2950000 0x40>; | ||
63 | }; | ||
64 | |||
65 | timer5: timer@2960000 { | ||
66 | compatible = "ti,c64x+timer64"; | ||
67 | ti,core-mask = < 0x01 >; | ||
68 | reg = <0x2960000 0x40>; | ||
69 | }; | ||
70 | |||
71 | device-state-controller@2880800 { | ||
72 | compatible = "ti,c64x+dscr"; | ||
73 | reg = <0x02880800 0x400>; | ||
74 | |||
75 | ti,dscr-devstat = <0x004>; | ||
76 | ti,dscr-silicon-rev = <0x014 28 0xf>; | ||
77 | ti,dscr-mac-fuse-regs = <0x34 3 4 5 6 | ||
78 | 0x38 0 0 1 2>; | ||
79 | }; | ||
80 | |||
81 | clock-controller@29a0000 { | ||
82 | compatible = "ti,c6474-pll", "ti,c64x+pll"; | ||
83 | reg = <0x029a0000 0x200>; | ||
84 | ti,c64x+pll-bypass-delay = <120>; | ||
85 | ti,c64x+pll-reset-delay = <30000>; | ||
86 | ti,c64x+pll-lock-delay = <60000>; | ||
87 | }; | ||
88 | }; | ||
89 | }; | ||
diff --git a/arch/c6x/boot/linked_dtb.S b/arch/c6x/boot/linked_dtb.S new file mode 100644 index 000000000000..57a4454eaec3 --- /dev/null +++ b/arch/c6x/boot/linked_dtb.S | |||
@@ -0,0 +1,2 @@ | |||
1 | .section __fdt_blob,"a" | ||
2 | .incbin "arch/c6x/boot/builtin.dtb" | ||
diff --git a/arch/c6x/configs/dsk6455_defconfig b/arch/c6x/configs/dsk6455_defconfig new file mode 100644 index 000000000000..4663487c67a1 --- /dev/null +++ b/arch/c6x/configs/dsk6455_defconfig | |||
@@ -0,0 +1,44 @@ | |||
1 | CONFIG_SOC_TMS320C6455=y | ||
2 | CONFIG_EXPERIMENTAL=y | ||
3 | # CONFIG_LOCALVERSION_AUTO is not set | ||
4 | CONFIG_SYSVIPC=y | ||
5 | CONFIG_SPARSE_IRQ=y | ||
6 | CONFIG_LOG_BUF_SHIFT=14 | ||
7 | CONFIG_NAMESPACES=y | ||
8 | # CONFIG_UTS_NS is not set | ||
9 | # CONFIG_USER_NS is not set | ||
10 | # CONFIG_PID_NS is not set | ||
11 | CONFIG_BLK_DEV_INITRD=y | ||
12 | CONFIG_CC_OPTIMIZE_FOR_SIZE=y | ||
13 | CONFIG_EXPERT=y | ||
14 | # CONFIG_FUTEX is not set | ||
15 | # CONFIG_SLUB_DEBUG is not set | ||
16 | CONFIG_MODULES=y | ||
17 | CONFIG_MODULE_FORCE_LOAD=y | ||
18 | CONFIG_MODULE_UNLOAD=y | ||
19 | CONFIG_MODULE_FORCE_UNLOAD=y | ||
20 | CONFIG_CMDLINE_BOOL=y | ||
21 | CONFIG_CMDLINE="" | ||
22 | CONFIG_NO_HZ=y | ||
23 | CONFIG_HIGH_RES_TIMERS=y | ||
24 | CONFIG_BLK_DEV_LOOP=y | ||
25 | CONFIG_BLK_DEV_RAM=y | ||
26 | CONFIG_BLK_DEV_RAM_COUNT=2 | ||
27 | CONFIG_BLK_DEV_RAM_SIZE=17000 | ||
28 | CONFIG_MISC_DEVICES=y | ||
29 | # CONFIG_INPUT is not set | ||
30 | # CONFIG_SERIO is not set | ||
31 | # CONFIG_VT is not set | ||
32 | # CONFIG_HW_RANDOM is not set | ||
33 | # CONFIG_HWMON is not set | ||
34 | # CONFIG_USB_SUPPORT is not set | ||
35 | # CONFIG_IOMMU_SUPPORT is not set | ||
36 | # CONFIG_MISC_FILESYSTEMS is not set | ||
37 | CONFIG_CRC16=y | ||
38 | # CONFIG_ENABLE_MUST_CHECK is not set | ||
39 | # CONFIG_SCHED_DEBUG is not set | ||
40 | # CONFIG_DEBUG_BUGVERBOSE is not set | ||
41 | CONFIG_MTD=y | ||
42 | CONFIG_MTD_CFI=y | ||
43 | CONFIG_MTD_CFI_AMDSTD=y | ||
44 | CONFIG_MTD_PHYSMAP_OF=y | ||
diff --git a/arch/c6x/configs/evmc6457_defconfig b/arch/c6x/configs/evmc6457_defconfig new file mode 100644 index 000000000000..bba40e195ec4 --- /dev/null +++ b/arch/c6x/configs/evmc6457_defconfig | |||
@@ -0,0 +1,41 @@ | |||
1 | CONFIG_SOC_TMS320C6457=y | ||
2 | CONFIG_EXPERIMENTAL=y | ||
3 | # CONFIG_LOCALVERSION_AUTO is not set | ||
4 | CONFIG_SYSVIPC=y | ||
5 | CONFIG_SPARSE_IRQ=y | ||
6 | CONFIG_LOG_BUF_SHIFT=14 | ||
7 | CONFIG_NAMESPACES=y | ||
8 | # CONFIG_UTS_NS is not set | ||
9 | # CONFIG_USER_NS is not set | ||
10 | # CONFIG_PID_NS is not set | ||
11 | CONFIG_BLK_DEV_INITRD=y | ||
12 | CONFIG_CC_OPTIMIZE_FOR_SIZE=y | ||
13 | CONFIG_EXPERT=y | ||
14 | # CONFIG_FUTEX is not set | ||
15 | # CONFIG_SLUB_DEBUG is not set | ||
16 | CONFIG_MODULES=y | ||
17 | CONFIG_MODULE_FORCE_LOAD=y | ||
18 | CONFIG_MODULE_UNLOAD=y | ||
19 | CONFIG_MODULE_FORCE_UNLOAD=y | ||
20 | CONFIG_CMDLINE_BOOL=y | ||
21 | CONFIG_CMDLINE="" | ||
22 | CONFIG_BOARD_EVM6457=y | ||
23 | CONFIG_NO_HZ=y | ||
24 | CONFIG_HIGH_RES_TIMERS=y | ||
25 | CONFIG_BLK_DEV_LOOP=y | ||
26 | CONFIG_BLK_DEV_RAM=y | ||
27 | CONFIG_BLK_DEV_RAM_COUNT=2 | ||
28 | CONFIG_BLK_DEV_RAM_SIZE=17000 | ||
29 | CONFIG_MISC_DEVICES=y | ||
30 | # CONFIG_INPUT is not set | ||
31 | # CONFIG_SERIO is not set | ||
32 | # CONFIG_VT is not set | ||
33 | # CONFIG_HW_RANDOM is not set | ||
34 | # CONFIG_HWMON is not set | ||
35 | # CONFIG_USB_SUPPORT is not set | ||
36 | # CONFIG_IOMMU_SUPPORT is not set | ||
37 | # CONFIG_MISC_FILESYSTEMS is not set | ||
38 | CONFIG_CRC16=y | ||
39 | # CONFIG_ENABLE_MUST_CHECK is not set | ||
40 | # CONFIG_SCHED_DEBUG is not set | ||
41 | # CONFIG_DEBUG_BUGVERBOSE is not set | ||
diff --git a/arch/c6x/configs/evmc6472_defconfig b/arch/c6x/configs/evmc6472_defconfig new file mode 100644 index 000000000000..8c46155f6d31 --- /dev/null +++ b/arch/c6x/configs/evmc6472_defconfig | |||
@@ -0,0 +1,42 @@ | |||
1 | CONFIG_SOC_TMS320C6472=y | ||
2 | CONFIG_EXPERIMENTAL=y | ||
3 | # CONFIG_LOCALVERSION_AUTO is not set | ||
4 | CONFIG_SYSVIPC=y | ||
5 | CONFIG_SPARSE_IRQ=y | ||
6 | CONFIG_LOG_BUF_SHIFT=14 | ||
7 | CONFIG_NAMESPACES=y | ||
8 | # CONFIG_UTS_NS is not set | ||
9 | # CONFIG_USER_NS is not set | ||
10 | # CONFIG_PID_NS is not set | ||
11 | CONFIG_BLK_DEV_INITRD=y | ||
12 | CONFIG_CC_OPTIMIZE_FOR_SIZE=y | ||
13 | CONFIG_EXPERT=y | ||
14 | # CONFIG_FUTEX is not set | ||
15 | # CONFIG_SLUB_DEBUG is not set | ||
16 | CONFIG_MODULES=y | ||
17 | CONFIG_MODULE_FORCE_LOAD=y | ||
18 | CONFIG_MODULE_UNLOAD=y | ||
19 | CONFIG_MODULE_FORCE_UNLOAD=y | ||
20 | CONFIG_CMDLINE_BOOL=y | ||
21 | CONFIG_CMDLINE="" | ||
22 | # CONFIG_CMDLINE_FORCE is not set | ||
23 | CONFIG_BOARD_EVM6472=y | ||
24 | CONFIG_NO_HZ=y | ||
25 | CONFIG_HIGH_RES_TIMERS=y | ||
26 | CONFIG_BLK_DEV_LOOP=y | ||
27 | CONFIG_BLK_DEV_RAM=y | ||
28 | CONFIG_BLK_DEV_RAM_COUNT=2 | ||
29 | CONFIG_BLK_DEV_RAM_SIZE=17000 | ||
30 | CONFIG_MISC_DEVICES=y | ||
31 | # CONFIG_INPUT is not set | ||
32 | # CONFIG_SERIO is not set | ||
33 | # CONFIG_VT is not set | ||
34 | # CONFIG_HW_RANDOM is not set | ||
35 | # CONFIG_HWMON is not set | ||
36 | # CONFIG_USB_SUPPORT is not set | ||
37 | # CONFIG_IOMMU_SUPPORT is not set | ||
38 | # CONFIG_MISC_FILESYSTEMS is not set | ||
39 | CONFIG_CRC16=y | ||
40 | # CONFIG_ENABLE_MUST_CHECK is not set | ||
41 | # CONFIG_SCHED_DEBUG is not set | ||
42 | # CONFIG_DEBUG_BUGVERBOSE is not set | ||
diff --git a/arch/c6x/configs/evmc6474_defconfig b/arch/c6x/configs/evmc6474_defconfig new file mode 100644 index 000000000000..15533f632313 --- /dev/null +++ b/arch/c6x/configs/evmc6474_defconfig | |||
@@ -0,0 +1,42 @@ | |||
1 | CONFIG_SOC_TMS320C6474=y | ||
2 | CONFIG_EXPERIMENTAL=y | ||
3 | # CONFIG_LOCALVERSION_AUTO is not set | ||
4 | CONFIG_SYSVIPC=y | ||
5 | CONFIG_SPARSE_IRQ=y | ||
6 | CONFIG_LOG_BUF_SHIFT=14 | ||
7 | CONFIG_NAMESPACES=y | ||
8 | # CONFIG_UTS_NS is not set | ||
9 | # CONFIG_USER_NS is not set | ||
10 | # CONFIG_PID_NS is not set | ||
11 | CONFIG_BLK_DEV_INITRD=y | ||
12 | CONFIG_CC_OPTIMIZE_FOR_SIZE=y | ||
13 | CONFIG_EXPERT=y | ||
14 | # CONFIG_FUTEX is not set | ||
15 | # CONFIG_SLUB_DEBUG is not set | ||
16 | CONFIG_MODULES=y | ||
17 | CONFIG_MODULE_FORCE_LOAD=y | ||
18 | CONFIG_MODULE_UNLOAD=y | ||
19 | CONFIG_MODULE_FORCE_UNLOAD=y | ||
20 | CONFIG_CMDLINE_BOOL=y | ||
21 | CONFIG_CMDLINE="" | ||
22 | # CONFIG_CMDLINE_FORCE is not set | ||
23 | CONFIG_BOARD_EVM6474=y | ||
24 | CONFIG_NO_HZ=y | ||
25 | CONFIG_HIGH_RES_TIMERS=y | ||
26 | CONFIG_BLK_DEV_LOOP=y | ||
27 | CONFIG_BLK_DEV_RAM=y | ||
28 | CONFIG_BLK_DEV_RAM_COUNT=2 | ||
29 | CONFIG_BLK_DEV_RAM_SIZE=17000 | ||
30 | CONFIG_MISC_DEVICES=y | ||
31 | # CONFIG_INPUT is not set | ||
32 | # CONFIG_SERIO is not set | ||
33 | # CONFIG_VT is not set | ||
34 | # CONFIG_HW_RANDOM is not set | ||
35 | # CONFIG_HWMON is not set | ||
36 | # CONFIG_USB_SUPPORT is not set | ||
37 | # CONFIG_IOMMU_SUPPORT is not set | ||
38 | # CONFIG_MISC_FILESYSTEMS is not set | ||
39 | CONFIG_CRC16=y | ||
40 | # CONFIG_ENABLE_MUST_CHECK is not set | ||
41 | # CONFIG_SCHED_DEBUG is not set | ||
42 | # CONFIG_DEBUG_BUGVERBOSE is not set | ||
diff --git a/arch/c6x/include/asm/Kbuild b/arch/c6x/include/asm/Kbuild new file mode 100644 index 000000000000..13dcf78adf91 --- /dev/null +++ b/arch/c6x/include/asm/Kbuild | |||
@@ -0,0 +1,54 @@ | |||
1 | include include/asm-generic/Kbuild.asm | ||
2 | |||
3 | generic-y += atomic.h | ||
4 | generic-y += auxvec.h | ||
5 | generic-y += bitsperlong.h | ||
6 | generic-y += bug.h | ||
7 | generic-y += bugs.h | ||
8 | generic-y += cputime.h | ||
9 | generic-y += current.h | ||
10 | generic-y += device.h | ||
11 | generic-y += div64.h | ||
12 | generic-y += dma.h | ||
13 | generic-y += emergency-restart.h | ||
14 | generic-y += errno.h | ||
15 | generic-y += fb.h | ||
16 | generic-y += fcntl.h | ||
17 | generic-y += futex.h | ||
18 | generic-y += hw_irq.h | ||
19 | generic-y += io.h | ||
20 | generic-y += ioctl.h | ||
21 | generic-y += ioctls.h | ||
22 | generic-y += ipcbuf.h | ||
23 | generic-y += irq_regs.h | ||
24 | generic-y += kdebug.h | ||
25 | generic-y += kmap_types.h | ||
26 | generic-y += local.h | ||
27 | generic-y += mman.h | ||
28 | generic-y += mmu_context.h | ||
29 | generic-y += msgbuf.h | ||
30 | generic-y += param.h | ||
31 | generic-y += pci.h | ||
32 | generic-y += percpu.h | ||
33 | generic-y += pgalloc.h | ||
34 | generic-y += poll.h | ||
35 | generic-y += posix_types.h | ||
36 | generic-y += resource.h | ||
37 | generic-y += scatterlist.h | ||
38 | generic-y += segment.h | ||
39 | generic-y += sembuf.h | ||
40 | generic-y += shmbuf.h | ||
41 | generic-y += shmparam.h | ||
42 | generic-y += siginfo.h | ||
43 | generic-y += socket.h | ||
44 | generic-y += sockios.h | ||
45 | generic-y += stat.h | ||
46 | generic-y += statfs.h | ||
47 | generic-y += termbits.h | ||
48 | generic-y += termios.h | ||
49 | generic-y += tlbflush.h | ||
50 | generic-y += topology.h | ||
51 | generic-y += types.h | ||
52 | generic-y += ucontext.h | ||
53 | generic-y += user.h | ||
54 | generic-y += vga.h | ||
diff --git a/arch/c6x/include/asm/asm-offsets.h b/arch/c6x/include/asm/asm-offsets.h new file mode 100644 index 000000000000..d370ee36a182 --- /dev/null +++ b/arch/c6x/include/asm/asm-offsets.h | |||
@@ -0,0 +1 @@ | |||
#include <generated/asm-offsets.h> | |||
diff --git a/arch/c6x/include/asm/bitops.h b/arch/c6x/include/asm/bitops.h new file mode 100644 index 000000000000..39ab7e874d96 --- /dev/null +++ b/arch/c6x/include/asm/bitops.h | |||
@@ -0,0 +1,105 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_BITOPS_H | ||
12 | #define _ASM_C6X_BITOPS_H | ||
13 | |||
14 | #ifdef __KERNEL__ | ||
15 | |||
16 | #include <linux/bitops.h> | ||
17 | |||
18 | #include <asm/system.h> | ||
19 | #include <asm/byteorder.h> | ||
20 | |||
21 | /* | ||
22 | * clear_bit() doesn't provide any barrier for the compiler. | ||
23 | */ | ||
24 | #define smp_mb__before_clear_bit() barrier() | ||
25 | #define smp_mb__after_clear_bit() barrier() | ||
26 | |||
27 | /* | ||
28 | * We are lucky, DSP is perfect for bitops: do it in 3 cycles | ||
29 | */ | ||
30 | |||
31 | /** | ||
32 | * __ffs - find first bit in word. | ||
33 | * @word: The word to search | ||
34 | * | ||
35 | * Undefined if no bit exists, so code should check against 0 first. | ||
36 | * Note __ffs(0) = undef, __ffs(1) = 0, __ffs(0x80000000) = 31. | ||
37 | * | ||
38 | */ | ||
39 | static inline unsigned long __ffs(unsigned long x) | ||
40 | { | ||
41 | asm (" bitr .M1 %0,%0\n" | ||
42 | " nop\n" | ||
43 | " lmbd .L1 1,%0,%0\n" | ||
44 | : "+a"(x)); | ||
45 | |||
46 | return x; | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * ffz - find first zero in word. | ||
51 | * @word: The word to search | ||
52 | * | ||
53 | * Undefined if no zero exists, so code should check against ~0UL first. | ||
54 | */ | ||
55 | #define ffz(x) __ffs(~(x)) | ||
56 | |||
57 | /** | ||
58 | * fls - find last (most-significant) bit set | ||
59 | * @x: the word to search | ||
60 | * | ||
61 | * This is defined the same way as ffs. | ||
62 | * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. | ||
63 | */ | ||
64 | static inline int fls(int x) | ||
65 | { | ||
66 | if (!x) | ||
67 | return 0; | ||
68 | |||
69 | asm (" lmbd .L1 1,%0,%0\n" : "+a"(x)); | ||
70 | |||
71 | return 32 - x; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * ffs - find first bit set | ||
76 | * @x: the word to search | ||
77 | * | ||
78 | * This is defined the same way as | ||
79 | * the libc and compiler builtin ffs routines, therefore | ||
80 | * differs in spirit from the above ffz (man ffs). | ||
81 | * Note ffs(0) = 0, ffs(1) = 1, ffs(0x80000000) = 32. | ||
82 | */ | ||
83 | static inline int ffs(int x) | ||
84 | { | ||
85 | if (!x) | ||
86 | return 0; | ||
87 | |||
88 | return __ffs(x) + 1; | ||
89 | } | ||
90 | |||
91 | #include <asm-generic/bitops/__fls.h> | ||
92 | #include <asm-generic/bitops/fls64.h> | ||
93 | #include <asm-generic/bitops/find.h> | ||
94 | |||
95 | #include <asm-generic/bitops/sched.h> | ||
96 | #include <asm-generic/bitops/hweight.h> | ||
97 | #include <asm-generic/bitops/lock.h> | ||
98 | |||
99 | #include <asm-generic/bitops/atomic.h> | ||
100 | #include <asm-generic/bitops/non-atomic.h> | ||
101 | #include <asm-generic/bitops/le.h> | ||
102 | #include <asm-generic/bitops/ext2-atomic.h> | ||
103 | |||
104 | #endif /* __KERNEL__ */ | ||
105 | #endif /* _ASM_C6X_BITOPS_H */ | ||
diff --git a/arch/c6x/include/asm/byteorder.h b/arch/c6x/include/asm/byteorder.h new file mode 100644 index 000000000000..166038db342b --- /dev/null +++ b/arch/c6x/include/asm/byteorder.h | |||
@@ -0,0 +1,12 @@ | |||
1 | #ifndef _ASM_C6X_BYTEORDER_H | ||
2 | #define _ASM_C6X_BYTEORDER_H | ||
3 | |||
4 | #include <asm/types.h> | ||
5 | |||
6 | #ifdef _BIG_ENDIAN | ||
7 | #include <linux/byteorder/big_endian.h> | ||
8 | #else /* _BIG_ENDIAN */ | ||
9 | #include <linux/byteorder/little_endian.h> | ||
10 | #endif /* _BIG_ENDIAN */ | ||
11 | |||
12 | #endif /* _ASM_BYTEORDER_H */ | ||
diff --git a/arch/c6x/include/asm/cache.h b/arch/c6x/include/asm/cache.h new file mode 100644 index 000000000000..6d521d96d941 --- /dev/null +++ b/arch/c6x/include/asm/cache.h | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2005, 2006, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_CACHE_H | ||
12 | #define _ASM_C6X_CACHE_H | ||
13 | |||
14 | #include <linux/irqflags.h> | ||
15 | |||
16 | /* | ||
17 | * Cache line size | ||
18 | */ | ||
19 | #define L1D_CACHE_BYTES 64 | ||
20 | #define L1P_CACHE_BYTES 32 | ||
21 | #define L2_CACHE_BYTES 128 | ||
22 | |||
23 | /* | ||
24 | * L2 used as cache | ||
25 | */ | ||
26 | #define L2MODE_SIZE L2MODE_256K_CACHE | ||
27 | |||
28 | /* | ||
29 | * For practical reasons the L1_CACHE_BYTES defines should not be smaller than | ||
30 | * the L2 line size | ||
31 | */ | ||
32 | #define L1_CACHE_BYTES L2_CACHE_BYTES | ||
33 | |||
34 | #define L2_CACHE_ALIGN_LOW(x) \ | ||
35 | (((x) & ~(L2_CACHE_BYTES - 1))) | ||
36 | #define L2_CACHE_ALIGN_UP(x) \ | ||
37 | (((x) + (L2_CACHE_BYTES - 1)) & ~(L2_CACHE_BYTES - 1)) | ||
38 | #define L2_CACHE_ALIGN_CNT(x) \ | ||
39 | (((x) + (sizeof(int) - 1)) & ~(sizeof(int) - 1)) | ||
40 | |||
41 | #define ARCH_DMA_MINALIGN L1_CACHE_BYTES | ||
42 | #define ARCH_SLAB_MINALIGN L1_CACHE_BYTES | ||
43 | |||
44 | /* | ||
45 | * This is the granularity of hardware cacheability control. | ||
46 | */ | ||
47 | #define CACHEABILITY_ALIGN 0x01000000 | ||
48 | |||
49 | /* | ||
50 | * Align a physical address to MAR regions | ||
51 | */ | ||
52 | #define CACHE_REGION_START(v) \ | ||
53 | (((u32) (v)) & ~(CACHEABILITY_ALIGN - 1)) | ||
54 | #define CACHE_REGION_END(v) \ | ||
55 | (((u32) (v) + (CACHEABILITY_ALIGN - 1)) & ~(CACHEABILITY_ALIGN - 1)) | ||
56 | |||
57 | extern void __init c6x_cache_init(void); | ||
58 | |||
59 | extern void enable_caching(unsigned long start, unsigned long end); | ||
60 | extern void disable_caching(unsigned long start, unsigned long end); | ||
61 | |||
62 | extern void L1_cache_off(void); | ||
63 | extern void L1_cache_on(void); | ||
64 | |||
65 | extern void L1P_cache_global_invalidate(void); | ||
66 | extern void L1D_cache_global_invalidate(void); | ||
67 | extern void L1D_cache_global_writeback(void); | ||
68 | extern void L1D_cache_global_writeback_invalidate(void); | ||
69 | extern void L2_cache_set_mode(unsigned int mode); | ||
70 | extern void L2_cache_global_writeback_invalidate(void); | ||
71 | extern void L2_cache_global_writeback(void); | ||
72 | |||
73 | extern void L1P_cache_block_invalidate(unsigned int start, unsigned int end); | ||
74 | extern void L1D_cache_block_invalidate(unsigned int start, unsigned int end); | ||
75 | extern void L1D_cache_block_writeback_invalidate(unsigned int start, | ||
76 | unsigned int end); | ||
77 | extern void L1D_cache_block_writeback(unsigned int start, unsigned int end); | ||
78 | extern void L2_cache_block_invalidate(unsigned int start, unsigned int end); | ||
79 | extern void L2_cache_block_writeback(unsigned int start, unsigned int end); | ||
80 | extern void L2_cache_block_writeback_invalidate(unsigned int start, | ||
81 | unsigned int end); | ||
82 | extern void L2_cache_block_invalidate_nowait(unsigned int start, | ||
83 | unsigned int end); | ||
84 | extern void L2_cache_block_writeback_nowait(unsigned int start, | ||
85 | unsigned int end); | ||
86 | |||
87 | extern void L2_cache_block_writeback_invalidate_nowait(unsigned int start, | ||
88 | unsigned int end); | ||
89 | |||
90 | #endif /* _ASM_C6X_CACHE_H */ | ||
diff --git a/arch/c6x/include/asm/cacheflush.h b/arch/c6x/include/asm/cacheflush.h new file mode 100644 index 000000000000..df5db90dbe56 --- /dev/null +++ b/arch/c6x/include/asm/cacheflush.h | |||
@@ -0,0 +1,65 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_CACHEFLUSH_H | ||
12 | #define _ASM_C6X_CACHEFLUSH_H | ||
13 | |||
14 | #include <linux/spinlock.h> | ||
15 | |||
16 | #include <asm/setup.h> | ||
17 | #include <asm/cache.h> | ||
18 | #include <asm/mman.h> | ||
19 | #include <asm/page.h> | ||
20 | #include <asm/string.h> | ||
21 | |||
22 | /* | ||
23 | * virtually-indexed cache management (our cache is physically indexed) | ||
24 | */ | ||
25 | #define flush_cache_all() do {} while (0) | ||
26 | #define flush_cache_mm(mm) do {} while (0) | ||
27 | #define flush_cache_dup_mm(mm) do {} while (0) | ||
28 | #define flush_cache_range(mm, start, end) do {} while (0) | ||
29 | #define flush_cache_page(vma, vmaddr, pfn) do {} while (0) | ||
30 | #define flush_cache_vmap(start, end) do {} while (0) | ||
31 | #define flush_cache_vunmap(start, end) do {} while (0) | ||
32 | #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0 | ||
33 | #define flush_dcache_page(page) do {} while (0) | ||
34 | #define flush_dcache_mmap_lock(mapping) do {} while (0) | ||
35 | #define flush_dcache_mmap_unlock(mapping) do {} while (0) | ||
36 | |||
37 | /* | ||
38 | * physically-indexed cache management | ||
39 | */ | ||
40 | #define flush_icache_range(s, e) \ | ||
41 | do { \ | ||
42 | L1D_cache_block_writeback((s), (e)); \ | ||
43 | L1P_cache_block_invalidate((s), (e)); \ | ||
44 | } while (0) | ||
45 | |||
46 | #define flush_icache_page(vma, page) \ | ||
47 | do { \ | ||
48 | if ((vma)->vm_flags & PROT_EXEC) \ | ||
49 | L1D_cache_block_writeback_invalidate(page_address(page), \ | ||
50 | (unsigned long) page_address(page) + PAGE_SIZE)); \ | ||
51 | L1P_cache_block_invalidate(page_address(page), \ | ||
52 | (unsigned long) page_address(page) + PAGE_SIZE)); \ | ||
53 | } while (0) | ||
54 | |||
55 | |||
56 | #define copy_to_user_page(vma, page, vaddr, dst, src, len) \ | ||
57 | do { \ | ||
58 | memcpy(dst, src, len); \ | ||
59 | flush_icache_range((unsigned) (dst), (unsigned) (dst) + (len)); \ | ||
60 | } while (0) | ||
61 | |||
62 | #define copy_from_user_page(vma, page, vaddr, dst, src, len) \ | ||
63 | memcpy(dst, src, len) | ||
64 | |||
65 | #endif /* _ASM_C6X_CACHEFLUSH_H */ | ||
diff --git a/arch/c6x/include/asm/checksum.h b/arch/c6x/include/asm/checksum.h new file mode 100644 index 000000000000..7246816d6e4d --- /dev/null +++ b/arch/c6x/include/asm/checksum.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter <msalter@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #ifndef _ASM_C6X_CHECKSUM_H | ||
10 | #define _ASM_C6X_CHECKSUM_H | ||
11 | |||
12 | static inline __wsum | ||
13 | csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, | ||
14 | unsigned short proto, __wsum sum) | ||
15 | { | ||
16 | unsigned long long tmp; | ||
17 | |||
18 | asm ("add .d1 %1,%5,%1\n" | ||
19 | "|| addu .l1 %3,%4,%0\n" | ||
20 | "addu .l1 %2,%0,%0\n" | ||
21 | #ifndef CONFIG_CPU_BIG_ENDIAN | ||
22 | "|| shl .s1 %1,8,%1\n" | ||
23 | #endif | ||
24 | "addu .l1 %1,%0,%0\n" | ||
25 | "add .l1 %P0,%p0,%2\n" | ||
26 | : "=&a"(tmp), "+a"(len), "+a"(sum) | ||
27 | : "a" (saddr), "a" (daddr), "a" (proto)); | ||
28 | return sum; | ||
29 | } | ||
30 | #define csum_tcpudp_nofold csum_tcpudp_nofold | ||
31 | |||
32 | #include <asm-generic/checksum.h> | ||
33 | |||
34 | #endif /* _ASM_C6X_CHECKSUM_H */ | ||
diff --git a/arch/c6x/include/asm/clkdev.h b/arch/c6x/include/asm/clkdev.h new file mode 100644 index 000000000000..76a070b1c2e5 --- /dev/null +++ b/arch/c6x/include/asm/clkdev.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #ifndef _ASM_CLKDEV_H | ||
2 | #define _ASM_CLKDEV_H | ||
3 | |||
4 | #include <linux/slab.h> | ||
5 | |||
6 | struct clk; | ||
7 | |||
8 | static inline int __clk_get(struct clk *clk) | ||
9 | { | ||
10 | return 1; | ||
11 | } | ||
12 | |||
13 | static inline void __clk_put(struct clk *clk) | ||
14 | { | ||
15 | } | ||
16 | |||
17 | static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) | ||
18 | { | ||
19 | return kzalloc(size, GFP_KERNEL); | ||
20 | } | ||
21 | |||
22 | #endif /* _ASM_CLKDEV_H */ | ||
diff --git a/arch/c6x/include/asm/clock.h b/arch/c6x/include/asm/clock.h new file mode 100644 index 000000000000..bcf42b2b4b1e --- /dev/null +++ b/arch/c6x/include/asm/clock.h | |||
@@ -0,0 +1,148 @@ | |||
1 | /* | ||
2 | * TI C64X clock definitions | ||
3 | * | ||
4 | * Copyright (C) 2010, 2011 Texas Instruments. | ||
5 | * Contributed by: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * Copied heavily from arm/mach-davinci/clock.h, so: | ||
8 | * | ||
9 | * Copyright (C) 2006-2007 Texas Instruments. | ||
10 | * Copyright (C) 2008-2009 Deep Root Systems, LLC | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #ifndef _ASM_C6X_CLOCK_H | ||
18 | #define _ASM_C6X_CLOCK_H | ||
19 | |||
20 | #ifndef __ASSEMBLER__ | ||
21 | |||
22 | #include <linux/list.h> | ||
23 | |||
24 | /* PLL/Reset register offsets */ | ||
25 | #define PLLCTL 0x100 | ||
26 | #define PLLM 0x110 | ||
27 | #define PLLPRE 0x114 | ||
28 | #define PLLDIV1 0x118 | ||
29 | #define PLLDIV2 0x11c | ||
30 | #define PLLDIV3 0x120 | ||
31 | #define PLLPOST 0x128 | ||
32 | #define PLLCMD 0x138 | ||
33 | #define PLLSTAT 0x13c | ||
34 | #define PLLALNCTL 0x140 | ||
35 | #define PLLDCHANGE 0x144 | ||
36 | #define PLLCKEN 0x148 | ||
37 | #define PLLCKSTAT 0x14c | ||
38 | #define PLLSYSTAT 0x150 | ||
39 | #define PLLDIV4 0x160 | ||
40 | #define PLLDIV5 0x164 | ||
41 | #define PLLDIV6 0x168 | ||
42 | #define PLLDIV7 0x16c | ||
43 | #define PLLDIV8 0x170 | ||
44 | #define PLLDIV9 0x174 | ||
45 | #define PLLDIV10 0x178 | ||
46 | #define PLLDIV11 0x17c | ||
47 | #define PLLDIV12 0x180 | ||
48 | #define PLLDIV13 0x184 | ||
49 | #define PLLDIV14 0x188 | ||
50 | #define PLLDIV15 0x18c | ||
51 | #define PLLDIV16 0x190 | ||
52 | |||
53 | /* PLLM register bits */ | ||
54 | #define PLLM_PLLM_MASK 0xff | ||
55 | #define PLLM_VAL(x) ((x) - 1) | ||
56 | |||
57 | /* PREDIV register bits */ | ||
58 | #define PLLPREDIV_EN BIT(15) | ||
59 | #define PLLPREDIV_VAL(x) ((x) - 1) | ||
60 | |||
61 | /* PLLCTL register bits */ | ||
62 | #define PLLCTL_PLLEN BIT(0) | ||
63 | #define PLLCTL_PLLPWRDN BIT(1) | ||
64 | #define PLLCTL_PLLRST BIT(3) | ||
65 | #define PLLCTL_PLLDIS BIT(4) | ||
66 | #define PLLCTL_PLLENSRC BIT(5) | ||
67 | #define PLLCTL_CLKMODE BIT(8) | ||
68 | |||
69 | /* PLLCMD register bits */ | ||
70 | #define PLLCMD_GOSTAT BIT(0) | ||
71 | |||
72 | /* PLLSTAT register bits */ | ||
73 | #define PLLSTAT_GOSTAT BIT(0) | ||
74 | |||
75 | /* PLLDIV register bits */ | ||
76 | #define PLLDIV_EN BIT(15) | ||
77 | #define PLLDIV_RATIO_MASK 0x1f | ||
78 | #define PLLDIV_RATIO(x) ((x) - 1) | ||
79 | |||
80 | struct pll_data; | ||
81 | |||
82 | struct clk { | ||
83 | struct list_head node; | ||
84 | struct module *owner; | ||
85 | const char *name; | ||
86 | unsigned long rate; | ||
87 | int usecount; | ||
88 | u32 flags; | ||
89 | struct clk *parent; | ||
90 | struct list_head children; /* list of children */ | ||
91 | struct list_head childnode; /* parent's child list node */ | ||
92 | struct pll_data *pll_data; | ||
93 | u32 div; | ||
94 | unsigned long (*recalc) (struct clk *); | ||
95 | int (*set_rate) (struct clk *clk, unsigned long rate); | ||
96 | int (*round_rate) (struct clk *clk, unsigned long rate); | ||
97 | }; | ||
98 | |||
99 | /* Clock flags: SoC-specific flags start at BIT(16) */ | ||
100 | #define ALWAYS_ENABLED BIT(1) | ||
101 | #define CLK_PLL BIT(2) /* PLL-derived clock */ | ||
102 | #define PRE_PLL BIT(3) /* source is before PLL mult/div */ | ||
103 | #define FIXED_DIV_PLL BIT(4) /* fixed divisor from PLL */ | ||
104 | #define FIXED_RATE_PLL BIT(5) /* fixed ouput rate PLL */ | ||
105 | |||
106 | #define MAX_PLL_SYSCLKS 16 | ||
107 | |||
108 | struct pll_data { | ||
109 | void __iomem *base; | ||
110 | u32 num; | ||
111 | u32 flags; | ||
112 | u32 input_rate; | ||
113 | u32 bypass_delay; /* in loops */ | ||
114 | u32 reset_delay; /* in loops */ | ||
115 | u32 lock_delay; /* in loops */ | ||
116 | struct clk sysclks[MAX_PLL_SYSCLKS + 1]; | ||
117 | }; | ||
118 | |||
119 | /* pll_data flag bit */ | ||
120 | #define PLL_HAS_PRE BIT(0) | ||
121 | #define PLL_HAS_MUL BIT(1) | ||
122 | #define PLL_HAS_POST BIT(2) | ||
123 | |||
124 | #define CLK(dev, con, ck) \ | ||
125 | { \ | ||
126 | .dev_id = dev, \ | ||
127 | .con_id = con, \ | ||
128 | .clk = ck, \ | ||
129 | } \ | ||
130 | |||
131 | extern void c6x_clks_init(struct clk_lookup *clocks); | ||
132 | extern int clk_register(struct clk *clk); | ||
133 | extern void clk_unregister(struct clk *clk); | ||
134 | extern void c64x_setup_clocks(void); | ||
135 | |||
136 | extern struct pll_data c6x_soc_pll1; | ||
137 | |||
138 | extern struct clk clkin1; | ||
139 | extern struct clk c6x_core_clk; | ||
140 | extern struct clk c6x_i2c_clk; | ||
141 | extern struct clk c6x_watchdog_clk; | ||
142 | extern struct clk c6x_mcbsp1_clk; | ||
143 | extern struct clk c6x_mcbsp2_clk; | ||
144 | extern struct clk c6x_mdio_clk; | ||
145 | |||
146 | #endif | ||
147 | |||
148 | #endif /* _ASM_C6X_CLOCK_H */ | ||
diff --git a/arch/c6x/include/asm/delay.h b/arch/c6x/include/asm/delay.h new file mode 100644 index 000000000000..f314c2e9eb54 --- /dev/null +++ b/arch/c6x/include/asm/delay.h | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_DELAY_H | ||
12 | #define _ASM_C6X_DELAY_H | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | |||
16 | extern unsigned int ticks_per_ns_scaled; | ||
17 | |||
18 | static inline void __delay(unsigned long loops) | ||
19 | { | ||
20 | uint32_t tmp; | ||
21 | |||
22 | /* 6 cycles per loop */ | ||
23 | asm volatile (" mv .s1 %0,%1\n" | ||
24 | "0: [%1] b .s1 0b\n" | ||
25 | " add .l1 -6,%0,%0\n" | ||
26 | " cmplt .l1 1,%0,%1\n" | ||
27 | " nop 3\n" | ||
28 | : "+a"(loops), "=A"(tmp)); | ||
29 | } | ||
30 | |||
31 | static inline void _c6x_tickdelay(unsigned int x) | ||
32 | { | ||
33 | uint32_t cnt, endcnt; | ||
34 | |||
35 | asm volatile (" mvc .s2 TSCL,%0\n" | ||
36 | " add .s2x %0,%1,%2\n" | ||
37 | " || mvk .l2 1,B0\n" | ||
38 | "0: [B0] b .s2 0b\n" | ||
39 | " mvc .s2 TSCL,%0\n" | ||
40 | " sub .s2 %0,%2,%0\n" | ||
41 | " cmpgt .l2 0,%0,B0\n" | ||
42 | " nop 2\n" | ||
43 | : "=b"(cnt), "+a"(x), "=b"(endcnt) : : "B0"); | ||
44 | } | ||
45 | |||
46 | /* use scaled math to avoid slow division */ | ||
47 | #define C6X_NDELAY_SCALE 10 | ||
48 | |||
49 | static inline void _ndelay(unsigned int n) | ||
50 | { | ||
51 | _c6x_tickdelay((ticks_per_ns_scaled * n) >> C6X_NDELAY_SCALE); | ||
52 | } | ||
53 | |||
54 | static inline void _udelay(unsigned int n) | ||
55 | { | ||
56 | while (n >= 10) { | ||
57 | _ndelay(10000); | ||
58 | n -= 10; | ||
59 | } | ||
60 | while (n-- > 0) | ||
61 | _ndelay(1000); | ||
62 | } | ||
63 | |||
64 | #define udelay(x) _udelay((unsigned int)(x)) | ||
65 | #define ndelay(x) _ndelay((unsigned int)(x)) | ||
66 | |||
67 | #endif /* _ASM_C6X_DELAY_H */ | ||
diff --git a/arch/c6x/include/asm/dma-mapping.h b/arch/c6x/include/asm/dma-mapping.h new file mode 100644 index 000000000000..03579fd99dba --- /dev/null +++ b/arch/c6x/include/asm/dma-mapping.h | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot <aurelien.jacquiot@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | #ifndef _ASM_C6X_DMA_MAPPING_H | ||
13 | #define _ASM_C6X_DMA_MAPPING_H | ||
14 | |||
15 | #include <linux/dma-debug.h> | ||
16 | #include <asm-generic/dma-coherent.h> | ||
17 | |||
18 | #define dma_supported(d, m) 1 | ||
19 | |||
20 | static inline int dma_set_mask(struct device *dev, u64 dma_mask) | ||
21 | { | ||
22 | if (!dev->dma_mask || !dma_supported(dev, dma_mask)) | ||
23 | return -EIO; | ||
24 | |||
25 | *dev->dma_mask = dma_mask; | ||
26 | |||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | /* | ||
31 | * DMA errors are defined by all-bits-set in the DMA address. | ||
32 | */ | ||
33 | static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) | ||
34 | { | ||
35 | return dma_addr == ~0; | ||
36 | } | ||
37 | |||
38 | extern dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, | ||
39 | size_t size, enum dma_data_direction dir); | ||
40 | |||
41 | extern void dma_unmap_single(struct device *dev, dma_addr_t handle, | ||
42 | size_t size, enum dma_data_direction dir); | ||
43 | |||
44 | extern int dma_map_sg(struct device *dev, struct scatterlist *sglist, | ||
45 | int nents, enum dma_data_direction direction); | ||
46 | |||
47 | extern void dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | ||
48 | int nents, enum dma_data_direction direction); | ||
49 | |||
50 | static inline dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||
51 | unsigned long offset, size_t size, | ||
52 | enum dma_data_direction dir) | ||
53 | { | ||
54 | dma_addr_t handle; | ||
55 | |||
56 | handle = dma_map_single(dev, page_address(page) + offset, size, dir); | ||
57 | |||
58 | debug_dma_map_page(dev, page, offset, size, dir, handle, false); | ||
59 | |||
60 | return handle; | ||
61 | } | ||
62 | |||
63 | static inline void dma_unmap_page(struct device *dev, dma_addr_t handle, | ||
64 | size_t size, enum dma_data_direction dir) | ||
65 | { | ||
66 | dma_unmap_single(dev, handle, size, dir); | ||
67 | |||
68 | debug_dma_unmap_page(dev, handle, size, dir, false); | ||
69 | } | ||
70 | |||
71 | extern void dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, | ||
72 | size_t size, enum dma_data_direction dir); | ||
73 | |||
74 | extern void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, | ||
75 | size_t size, | ||
76 | enum dma_data_direction dir); | ||
77 | |||
78 | extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, | ||
79 | int nents, enum dma_data_direction dir); | ||
80 | |||
81 | extern void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | ||
82 | int nents, enum dma_data_direction dir); | ||
83 | |||
84 | extern void coherent_mem_init(u32 start, u32 size); | ||
85 | extern void *dma_alloc_coherent(struct device *, size_t, dma_addr_t *, gfp_t); | ||
86 | extern void dma_free_coherent(struct device *, size_t, void *, dma_addr_t); | ||
87 | |||
88 | #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent((d), (s), (h), (f)) | ||
89 | #define dma_free_noncoherent(d, s, v, h) dma_free_coherent((d), (s), (v), (h)) | ||
90 | |||
91 | #endif /* _ASM_C6X_DMA_MAPPING_H */ | ||
diff --git a/arch/c6x/include/asm/dscr.h b/arch/c6x/include/asm/dscr.h new file mode 100644 index 000000000000..561ba8332042 --- /dev/null +++ b/arch/c6x/include/asm/dscr.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter <msalter@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | */ | ||
10 | #ifndef _ASM_C6X_DSCR_H | ||
11 | #define _ASM_C6X_DSCR_H | ||
12 | |||
13 | enum dscr_devstate_t { | ||
14 | DSCR_DEVSTATE_ENABLED, | ||
15 | DSCR_DEVSTATE_DISABLED, | ||
16 | }; | ||
17 | |||
18 | /* | ||
19 | * Set the device state of the device with the given ID. | ||
20 | * | ||
21 | * Individual drivers should use this to enable or disable the | ||
22 | * hardware device. The devid used to identify the device being | ||
23 | * controlled should be a property in the device's tree node. | ||
24 | */ | ||
25 | extern void dscr_set_devstate(int devid, enum dscr_devstate_t state); | ||
26 | |||
27 | /* | ||
28 | * Assert or de-assert an RMII reset. | ||
29 | */ | ||
30 | extern void dscr_rmii_reset(int id, int assert); | ||
31 | |||
32 | extern void dscr_probe(void); | ||
33 | |||
34 | #endif /* _ASM_C6X_DSCR_H */ | ||
diff --git a/arch/c6x/include/asm/elf.h b/arch/c6x/include/asm/elf.h new file mode 100644 index 000000000000..d57865ba2c44 --- /dev/null +++ b/arch/c6x/include/asm/elf.h | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_ELF_H | ||
12 | #define _ASM_C6X_ELF_H | ||
13 | |||
14 | /* | ||
15 | * ELF register definitions.. | ||
16 | */ | ||
17 | #include <asm/ptrace.h> | ||
18 | |||
19 | typedef unsigned long elf_greg_t; | ||
20 | typedef unsigned long elf_fpreg_t; | ||
21 | |||
22 | #define ELF_NGREG 58 | ||
23 | #define ELF_NFPREG 1 | ||
24 | |||
25 | typedef elf_greg_t elf_gregset_t[ELF_NGREG]; | ||
26 | typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; | ||
27 | |||
28 | /* | ||
29 | * This is used to ensure we don't load something for the wrong architecture. | ||
30 | */ | ||
31 | #define elf_check_arch(x) ((x)->e_machine == EM_TI_C6000) | ||
32 | |||
33 | #define elf_check_const_displacement(x) (1) | ||
34 | |||
35 | /* | ||
36 | * These are used to set parameters in the core dumps. | ||
37 | */ | ||
38 | #ifdef __LITTLE_ENDIAN__ | ||
39 | #define ELF_DATA ELFDATA2LSB | ||
40 | #else | ||
41 | #define ELF_DATA ELFDATA2MSB | ||
42 | #endif | ||
43 | |||
44 | #define ELF_CLASS ELFCLASS32 | ||
45 | #define ELF_ARCH EM_TI_C6000 | ||
46 | |||
47 | /* Nothing for now. Need to setup DP... */ | ||
48 | #define ELF_PLAT_INIT(_r) | ||
49 | |||
50 | #define USE_ELF_CORE_DUMP | ||
51 | #define ELF_EXEC_PAGESIZE 4096 | ||
52 | |||
53 | #define ELF_CORE_COPY_REGS(_dest, _regs) \ | ||
54 | memcpy((char *) &_dest, (char *) _regs, \ | ||
55 | sizeof(struct pt_regs)); | ||
56 | |||
57 | /* This yields a mask that user programs can use to figure out what | ||
58 | instruction set this cpu supports. */ | ||
59 | |||
60 | #define ELF_HWCAP (0) | ||
61 | |||
62 | /* This yields a string that ld.so will use to load implementation | ||
63 | specific libraries for optimization. This is more specific in | ||
64 | intent than poking at uname or /proc/cpuinfo. */ | ||
65 | |||
66 | #define ELF_PLATFORM (NULL) | ||
67 | |||
68 | #define SET_PERSONALITY(ex) set_personality(PER_LINUX) | ||
69 | |||
70 | /* C6X specific section types */ | ||
71 | #define SHT_C6000_UNWIND 0x70000001 | ||
72 | #define SHT_C6000_PREEMPTMAP 0x70000002 | ||
73 | #define SHT_C6000_ATTRIBUTES 0x70000003 | ||
74 | |||
75 | /* C6X specific DT_ tags */ | ||
76 | #define DT_C6000_DSBT_BASE 0x70000000 | ||
77 | #define DT_C6000_DSBT_SIZE 0x70000001 | ||
78 | #define DT_C6000_PREEMPTMAP 0x70000002 | ||
79 | #define DT_C6000_DSBT_INDEX 0x70000003 | ||
80 | |||
81 | /* C6X specific relocs */ | ||
82 | #define R_C6000_NONE 0 | ||
83 | #define R_C6000_ABS32 1 | ||
84 | #define R_C6000_ABS16 2 | ||
85 | #define R_C6000_ABS8 3 | ||
86 | #define R_C6000_PCR_S21 4 | ||
87 | #define R_C6000_PCR_S12 5 | ||
88 | #define R_C6000_PCR_S10 6 | ||
89 | #define R_C6000_PCR_S7 7 | ||
90 | #define R_C6000_ABS_S16 8 | ||
91 | #define R_C6000_ABS_L16 9 | ||
92 | #define R_C6000_ABS_H16 10 | ||
93 | #define R_C6000_SBR_U15_B 11 | ||
94 | #define R_C6000_SBR_U15_H 12 | ||
95 | #define R_C6000_SBR_U15_W 13 | ||
96 | #define R_C6000_SBR_S16 14 | ||
97 | #define R_C6000_SBR_L16_B 15 | ||
98 | #define R_C6000_SBR_L16_H 16 | ||
99 | #define R_C6000_SBR_L16_W 17 | ||
100 | #define R_C6000_SBR_H16_B 18 | ||
101 | #define R_C6000_SBR_H16_H 19 | ||
102 | #define R_C6000_SBR_H16_W 20 | ||
103 | #define R_C6000_SBR_GOT_U15_W 21 | ||
104 | #define R_C6000_SBR_GOT_L16_W 22 | ||
105 | #define R_C6000_SBR_GOT_H16_W 23 | ||
106 | #define R_C6000_DSBT_INDEX 24 | ||
107 | #define R_C6000_PREL31 25 | ||
108 | #define R_C6000_COPY 26 | ||
109 | #define R_C6000_ALIGN 253 | ||
110 | #define R_C6000_FPHEAD 254 | ||
111 | #define R_C6000_NOCMP 255 | ||
112 | |||
113 | #endif /*_ASM_C6X_ELF_H */ | ||
diff --git a/arch/c6x/include/asm/ftrace.h b/arch/c6x/include/asm/ftrace.h new file mode 100644 index 000000000000..3701958d3d1c --- /dev/null +++ b/arch/c6x/include/asm/ftrace.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef _ASM_C6X_FTRACE_H | ||
2 | #define _ASM_C6X_FTRACE_H | ||
3 | |||
4 | /* empty */ | ||
5 | |||
6 | #endif /* _ASM_C6X_FTRACE_H */ | ||
diff --git a/arch/c6x/include/asm/hardirq.h b/arch/c6x/include/asm/hardirq.h new file mode 100644 index 000000000000..9621954f98f4 --- /dev/null +++ b/arch/c6x/include/asm/hardirq.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef _ASM_C6X_HARDIRQ_H | ||
13 | #define _ASM_C6X_HARDIRQ_H | ||
14 | |||
15 | extern void ack_bad_irq(int irq); | ||
16 | #define ack_bad_irq ack_bad_irq | ||
17 | |||
18 | #include <asm-generic/hardirq.h> | ||
19 | |||
20 | #endif /* _ASM_C6X_HARDIRQ_H */ | ||
diff --git a/arch/c6x/include/asm/irq.h b/arch/c6x/include/asm/irq.h new file mode 100644 index 000000000000..a6ae3c9d9c40 --- /dev/null +++ b/arch/c6x/include/asm/irq.h | |||
@@ -0,0 +1,302 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Large parts taken directly from powerpc. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef _ASM_C6X_IRQ_H | ||
14 | #define _ASM_C6X_IRQ_H | ||
15 | |||
16 | #include <linux/threads.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/radix-tree.h> | ||
19 | #include <asm/percpu.h> | ||
20 | |||
21 | #define irq_canonicalize(irq) (irq) | ||
22 | |||
23 | /* | ||
24 | * The C64X+ core has 16 IRQ vectors. One each is used by Reset and NMI. Two | ||
25 | * are reserved. The remaining 12 vectors are used to route SoC interrupts. | ||
26 | * These interrupt vectors are prioritized with IRQ 4 having the highest | ||
27 | * priority and IRQ 15 having the lowest. | ||
28 | * | ||
29 | * The C64x+ megamodule provides a PIC which combines SoC IRQ sources into a | ||
30 | * single core IRQ vector. There are four combined sources, each of which | ||
31 | * feed into one of the 12 general interrupt vectors. The remaining 8 vectors | ||
32 | * can each route a single SoC interrupt directly. | ||
33 | */ | ||
34 | #define NR_PRIORITY_IRQS 16 | ||
35 | |||
36 | #define NR_IRQS_LEGACY NR_PRIORITY_IRQS | ||
37 | |||
38 | /* Total number of virq in the platform */ | ||
39 | #define NR_IRQS 256 | ||
40 | |||
41 | /* This number is used when no interrupt has been assigned */ | ||
42 | #define NO_IRQ 0 | ||
43 | |||
44 | /* This type is the placeholder for a hardware interrupt number. It has to | ||
45 | * be big enough to enclose whatever representation is used by a given | ||
46 | * platform. | ||
47 | */ | ||
48 | typedef unsigned long irq_hw_number_t; | ||
49 | |||
50 | /* Interrupt controller "host" data structure. This could be defined as a | ||
51 | * irq domain controller. That is, it handles the mapping between hardware | ||
52 | * and virtual interrupt numbers for a given interrupt domain. The host | ||
53 | * structure is generally created by the PIC code for a given PIC instance | ||
54 | * (though a host can cover more than one PIC if they have a flat number | ||
55 | * model). It's the host callbacks that are responsible for setting the | ||
56 | * irq_chip on a given irq_desc after it's been mapped. | ||
57 | * | ||
58 | * The host code and data structures are fairly agnostic to the fact that | ||
59 | * we use an open firmware device-tree. We do have references to struct | ||
60 | * device_node in two places: in irq_find_host() to find the host matching | ||
61 | * a given interrupt controller node, and of course as an argument to its | ||
62 | * counterpart host->ops->match() callback. However, those are treated as | ||
63 | * generic pointers by the core and the fact that it's actually a device-node | ||
64 | * pointer is purely a convention between callers and implementation. This | ||
65 | * code could thus be used on other architectures by replacing those two | ||
66 | * by some sort of arch-specific void * "token" used to identify interrupt | ||
67 | * controllers. | ||
68 | */ | ||
69 | struct irq_host; | ||
70 | struct radix_tree_root; | ||
71 | struct device_node; | ||
72 | |||
73 | /* Functions below are provided by the host and called whenever a new mapping | ||
74 | * is created or an old mapping is disposed. The host can then proceed to | ||
75 | * whatever internal data structures management is required. It also needs | ||
76 | * to setup the irq_desc when returning from map(). | ||
77 | */ | ||
78 | struct irq_host_ops { | ||
79 | /* Match an interrupt controller device node to a host, returns | ||
80 | * 1 on a match | ||
81 | */ | ||
82 | int (*match)(struct irq_host *h, struct device_node *node); | ||
83 | |||
84 | /* Create or update a mapping between a virtual irq number and a hw | ||
85 | * irq number. This is called only once for a given mapping. | ||
86 | */ | ||
87 | int (*map)(struct irq_host *h, unsigned int virq, irq_hw_number_t hw); | ||
88 | |||
89 | /* Dispose of such a mapping */ | ||
90 | void (*unmap)(struct irq_host *h, unsigned int virq); | ||
91 | |||
92 | /* Translate device-tree interrupt specifier from raw format coming | ||
93 | * from the firmware to a irq_hw_number_t (interrupt line number) and | ||
94 | * type (sense) that can be passed to set_irq_type(). In the absence | ||
95 | * of this callback, irq_create_of_mapping() and irq_of_parse_and_map() | ||
96 | * will return the hw number in the first cell and IRQ_TYPE_NONE for | ||
97 | * the type (which amount to keeping whatever default value the | ||
98 | * interrupt controller has for that line) | ||
99 | */ | ||
100 | int (*xlate)(struct irq_host *h, struct device_node *ctrler, | ||
101 | const u32 *intspec, unsigned int intsize, | ||
102 | irq_hw_number_t *out_hwirq, unsigned int *out_type); | ||
103 | }; | ||
104 | |||
105 | struct irq_host { | ||
106 | struct list_head link; | ||
107 | |||
108 | /* type of reverse mapping technique */ | ||
109 | unsigned int revmap_type; | ||
110 | #define IRQ_HOST_MAP_PRIORITY 0 /* core priority irqs, get irqs 1..15 */ | ||
111 | #define IRQ_HOST_MAP_NOMAP 1 /* no fast reverse mapping */ | ||
112 | #define IRQ_HOST_MAP_LINEAR 2 /* linear map of interrupts */ | ||
113 | #define IRQ_HOST_MAP_TREE 3 /* radix tree */ | ||
114 | union { | ||
115 | struct { | ||
116 | unsigned int size; | ||
117 | unsigned int *revmap; | ||
118 | } linear; | ||
119 | struct radix_tree_root tree; | ||
120 | } revmap_data; | ||
121 | struct irq_host_ops *ops; | ||
122 | void *host_data; | ||
123 | irq_hw_number_t inval_irq; | ||
124 | |||
125 | /* Optional device node pointer */ | ||
126 | struct device_node *of_node; | ||
127 | }; | ||
128 | |||
129 | struct irq_data; | ||
130 | extern irq_hw_number_t irqd_to_hwirq(struct irq_data *d); | ||
131 | extern irq_hw_number_t virq_to_hw(unsigned int virq); | ||
132 | extern bool virq_is_host(unsigned int virq, struct irq_host *host); | ||
133 | |||
134 | /** | ||
135 | * irq_alloc_host - Allocate a new irq_host data structure | ||
136 | * @of_node: optional device-tree node of the interrupt controller | ||
137 | * @revmap_type: type of reverse mapping to use | ||
138 | * @revmap_arg: for IRQ_HOST_MAP_LINEAR linear only: size of the map | ||
139 | * @ops: map/unmap host callbacks | ||
140 | * @inval_irq: provide a hw number in that host space that is always invalid | ||
141 | * | ||
142 | * Allocates and initialize and irq_host structure. Note that in the case of | ||
143 | * IRQ_HOST_MAP_LEGACY, the map() callback will be called before this returns | ||
144 | * for all legacy interrupts except 0 (which is always the invalid irq for | ||
145 | * a legacy controller). For a IRQ_HOST_MAP_LINEAR, the map is allocated by | ||
146 | * this call as well. For a IRQ_HOST_MAP_TREE, the radix tree will be allocated | ||
147 | * later during boot automatically (the reverse mapping will use the slow path | ||
148 | * until that happens). | ||
149 | */ | ||
150 | extern struct irq_host *irq_alloc_host(struct device_node *of_node, | ||
151 | unsigned int revmap_type, | ||
152 | unsigned int revmap_arg, | ||
153 | struct irq_host_ops *ops, | ||
154 | irq_hw_number_t inval_irq); | ||
155 | |||
156 | |||
157 | /** | ||
158 | * irq_find_host - Locates a host for a given device node | ||
159 | * @node: device-tree node of the interrupt controller | ||
160 | */ | ||
161 | extern struct irq_host *irq_find_host(struct device_node *node); | ||
162 | |||
163 | |||
164 | /** | ||
165 | * irq_set_default_host - Set a "default" host | ||
166 | * @host: default host pointer | ||
167 | * | ||
168 | * For convenience, it's possible to set a "default" host that will be used | ||
169 | * whenever NULL is passed to irq_create_mapping(). It makes life easier for | ||
170 | * platforms that want to manipulate a few hard coded interrupt numbers that | ||
171 | * aren't properly represented in the device-tree. | ||
172 | */ | ||
173 | extern void irq_set_default_host(struct irq_host *host); | ||
174 | |||
175 | |||
176 | /** | ||
177 | * irq_set_virq_count - Set the maximum number of virt irqs | ||
178 | * @count: number of linux virtual irqs, capped with NR_IRQS | ||
179 | * | ||
180 | * This is mainly for use by platforms like iSeries who want to program | ||
181 | * the virtual irq number in the controller to avoid the reverse mapping | ||
182 | */ | ||
183 | extern void irq_set_virq_count(unsigned int count); | ||
184 | |||
185 | |||
186 | /** | ||
187 | * irq_create_mapping - Map a hardware interrupt into linux virq space | ||
188 | * @host: host owning this hardware interrupt or NULL for default host | ||
189 | * @hwirq: hardware irq number in that host space | ||
190 | * | ||
191 | * Only one mapping per hardware interrupt is permitted. Returns a linux | ||
192 | * virq number. | ||
193 | * If the sense/trigger is to be specified, set_irq_type() should be called | ||
194 | * on the number returned from that call. | ||
195 | */ | ||
196 | extern unsigned int irq_create_mapping(struct irq_host *host, | ||
197 | irq_hw_number_t hwirq); | ||
198 | |||
199 | |||
200 | /** | ||
201 | * irq_dispose_mapping - Unmap an interrupt | ||
202 | * @virq: linux virq number of the interrupt to unmap | ||
203 | */ | ||
204 | extern void irq_dispose_mapping(unsigned int virq); | ||
205 | |||
206 | /** | ||
207 | * irq_find_mapping - Find a linux virq from an hw irq number. | ||
208 | * @host: host owning this hardware interrupt | ||
209 | * @hwirq: hardware irq number in that host space | ||
210 | * | ||
211 | * This is a slow path, for use by generic code. It's expected that an | ||
212 | * irq controller implementation directly calls the appropriate low level | ||
213 | * mapping function. | ||
214 | */ | ||
215 | extern unsigned int irq_find_mapping(struct irq_host *host, | ||
216 | irq_hw_number_t hwirq); | ||
217 | |||
218 | /** | ||
219 | * irq_create_direct_mapping - Allocate a virq for direct mapping | ||
220 | * @host: host to allocate the virq for or NULL for default host | ||
221 | * | ||
222 | * This routine is used for irq controllers which can choose the hardware | ||
223 | * interrupt numbers they generate. In such a case it's simplest to use | ||
224 | * the linux virq as the hardware interrupt number. | ||
225 | */ | ||
226 | extern unsigned int irq_create_direct_mapping(struct irq_host *host); | ||
227 | |||
228 | /** | ||
229 | * irq_radix_revmap_insert - Insert a hw irq to linux virq number mapping. | ||
230 | * @host: host owning this hardware interrupt | ||
231 | * @virq: linux irq number | ||
232 | * @hwirq: hardware irq number in that host space | ||
233 | * | ||
234 | * This is for use by irq controllers that use a radix tree reverse | ||
235 | * mapping for fast lookup. | ||
236 | */ | ||
237 | extern void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq, | ||
238 | irq_hw_number_t hwirq); | ||
239 | |||
240 | /** | ||
241 | * irq_radix_revmap_lookup - Find a linux virq from a hw irq number. | ||
242 | * @host: host owning this hardware interrupt | ||
243 | * @hwirq: hardware irq number in that host space | ||
244 | * | ||
245 | * This is a fast path, for use by irq controller code that uses radix tree | ||
246 | * revmaps | ||
247 | */ | ||
248 | extern unsigned int irq_radix_revmap_lookup(struct irq_host *host, | ||
249 | irq_hw_number_t hwirq); | ||
250 | |||
251 | /** | ||
252 | * irq_linear_revmap - Find a linux virq from a hw irq number. | ||
253 | * @host: host owning this hardware interrupt | ||
254 | * @hwirq: hardware irq number in that host space | ||
255 | * | ||
256 | * This is a fast path, for use by irq controller code that uses linear | ||
257 | * revmaps. It does fallback to the slow path if the revmap doesn't exist | ||
258 | * yet and will create the revmap entry with appropriate locking | ||
259 | */ | ||
260 | |||
261 | extern unsigned int irq_linear_revmap(struct irq_host *host, | ||
262 | irq_hw_number_t hwirq); | ||
263 | |||
264 | |||
265 | |||
266 | /** | ||
267 | * irq_alloc_virt - Allocate virtual irq numbers | ||
268 | * @host: host owning these new virtual irqs | ||
269 | * @count: number of consecutive numbers to allocate | ||
270 | * @hint: pass a hint number, the allocator will try to use a 1:1 mapping | ||
271 | * | ||
272 | * This is a low level function that is used internally by irq_create_mapping() | ||
273 | * and that can be used by some irq controllers implementations for things | ||
274 | * like allocating ranges of numbers for MSIs. The revmaps are left untouched. | ||
275 | */ | ||
276 | extern unsigned int irq_alloc_virt(struct irq_host *host, | ||
277 | unsigned int count, | ||
278 | unsigned int hint); | ||
279 | |||
280 | /** | ||
281 | * irq_free_virt - Free virtual irq numbers | ||
282 | * @virq: virtual irq number of the first interrupt to free | ||
283 | * @count: number of interrupts to free | ||
284 | * | ||
285 | * This function is the opposite of irq_alloc_virt. It will not clear reverse | ||
286 | * maps, this should be done previously by unmap'ing the interrupt. In fact, | ||
287 | * all interrupts covered by the range being freed should have been unmapped | ||
288 | * prior to calling this. | ||
289 | */ | ||
290 | extern void irq_free_virt(unsigned int virq, unsigned int count); | ||
291 | |||
292 | extern void __init init_pic_c64xplus(void); | ||
293 | |||
294 | extern void init_IRQ(void); | ||
295 | |||
296 | struct pt_regs; | ||
297 | |||
298 | extern asmlinkage void c6x_do_IRQ(unsigned int prio, struct pt_regs *regs); | ||
299 | |||
300 | extern unsigned long irq_err_count; | ||
301 | |||
302 | #endif /* _ASM_C6X_IRQ_H */ | ||
diff --git a/arch/c6x/include/asm/irqflags.h b/arch/c6x/include/asm/irqflags.h new file mode 100644 index 000000000000..cf78e09e18c3 --- /dev/null +++ b/arch/c6x/include/asm/irqflags.h | |||
@@ -0,0 +1,72 @@ | |||
1 | /* | ||
2 | * C6X IRQ flag handling | ||
3 | * | ||
4 | * Copyright (C) 2010 Texas Instruments Incorporated | ||
5 | * Written by Mark Salter (msalter@redhat.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public Licence | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the Licence, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #ifndef _ASM_IRQFLAGS_H | ||
14 | #define _ASM_IRQFLAGS_H | ||
15 | |||
16 | #ifndef __ASSEMBLY__ | ||
17 | |||
18 | /* read interrupt enabled status */ | ||
19 | static inline unsigned long arch_local_save_flags(void) | ||
20 | { | ||
21 | unsigned long flags; | ||
22 | |||
23 | asm volatile (" mvc .s2 CSR,%0\n" : "=b"(flags)); | ||
24 | return flags; | ||
25 | } | ||
26 | |||
27 | /* set interrupt enabled status */ | ||
28 | static inline void arch_local_irq_restore(unsigned long flags) | ||
29 | { | ||
30 | asm volatile (" mvc .s2 %0,CSR\n" : : "b"(flags)); | ||
31 | } | ||
32 | |||
33 | /* unconditionally enable interrupts */ | ||
34 | static inline void arch_local_irq_enable(void) | ||
35 | { | ||
36 | unsigned long flags = arch_local_save_flags(); | ||
37 | flags |= 1; | ||
38 | arch_local_irq_restore(flags); | ||
39 | } | ||
40 | |||
41 | /* unconditionally disable interrupts */ | ||
42 | static inline void arch_local_irq_disable(void) | ||
43 | { | ||
44 | unsigned long flags = arch_local_save_flags(); | ||
45 | flags &= ~1; | ||
46 | arch_local_irq_restore(flags); | ||
47 | } | ||
48 | |||
49 | /* get status and disable interrupts */ | ||
50 | static inline unsigned long arch_local_irq_save(void) | ||
51 | { | ||
52 | unsigned long flags; | ||
53 | |||
54 | flags = arch_local_save_flags(); | ||
55 | arch_local_irq_restore(flags & ~1); | ||
56 | return flags; | ||
57 | } | ||
58 | |||
59 | /* test flags */ | ||
60 | static inline int arch_irqs_disabled_flags(unsigned long flags) | ||
61 | { | ||
62 | return (flags & 1) == 0; | ||
63 | } | ||
64 | |||
65 | /* test hardware interrupt enable bit */ | ||
66 | static inline int arch_irqs_disabled(void) | ||
67 | { | ||
68 | return arch_irqs_disabled_flags(arch_local_save_flags()); | ||
69 | } | ||
70 | |||
71 | #endif /* __ASSEMBLY__ */ | ||
72 | #endif /* __ASM_IRQFLAGS_H */ | ||
diff --git a/arch/c6x/include/asm/linkage.h b/arch/c6x/include/asm/linkage.h new file mode 100644 index 000000000000..376925c47d57 --- /dev/null +++ b/arch/c6x/include/asm/linkage.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #ifndef _ASM_C6X_LINKAGE_H | ||
2 | #define _ASM_C6X_LINKAGE_H | ||
3 | |||
4 | #ifdef __ASSEMBLER__ | ||
5 | |||
6 | #define __ALIGN .align 2 | ||
7 | #define __ALIGN_STR ".align 2" | ||
8 | |||
9 | #ifndef __DSBT__ | ||
10 | #define ENTRY(name) \ | ||
11 | .global name @ \ | ||
12 | __ALIGN @ \ | ||
13 | name: | ||
14 | #else | ||
15 | #define ENTRY(name) \ | ||
16 | .global name @ \ | ||
17 | .hidden name @ \ | ||
18 | __ALIGN @ \ | ||
19 | name: | ||
20 | #endif | ||
21 | |||
22 | #define ENDPROC(name) \ | ||
23 | .type name, @function @ \ | ||
24 | .size name, . - name | ||
25 | |||
26 | #endif | ||
27 | |||
28 | #include <asm-generic/linkage.h> | ||
29 | |||
30 | #endif /* _ASM_C6X_LINKAGE_H */ | ||
diff --git a/arch/c6x/include/asm/megamod-pic.h b/arch/c6x/include/asm/megamod-pic.h new file mode 100644 index 000000000000..eca0a8678034 --- /dev/null +++ b/arch/c6x/include/asm/megamod-pic.h | |||
@@ -0,0 +1,9 @@ | |||
1 | #ifndef _C6X_MEGAMOD_PIC_H | ||
2 | #define _C6X_MEGAMOD_PIC_H | ||
3 | |||
4 | #ifdef __KERNEL__ | ||
5 | |||
6 | extern void __init megamod_pic_init(void); | ||
7 | |||
8 | #endif /* __KERNEL__ */ | ||
9 | #endif /* _C6X_MEGAMOD_PIC_H */ | ||
diff --git a/arch/c6x/include/asm/mmu.h b/arch/c6x/include/asm/mmu.h new file mode 100644 index 000000000000..41592bf16067 --- /dev/null +++ b/arch/c6x/include/asm/mmu.h | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_MMU_H | ||
12 | #define _ASM_C6X_MMU_H | ||
13 | |||
14 | typedef struct { | ||
15 | unsigned long end_brk; | ||
16 | } mm_context_t; | ||
17 | |||
18 | #endif /* _ASM_C6X_MMU_H */ | ||
diff --git a/arch/c6x/include/asm/module.h b/arch/c6x/include/asm/module.h new file mode 100644 index 000000000000..a453f9744f42 --- /dev/null +++ b/arch/c6x/include/asm/module.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Updated for 2.6.34 by: Mark Salter (msalter@redhat.com) | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef _ASM_C6X_MODULE_H | ||
14 | #define _ASM_C6X_MODULE_H | ||
15 | |||
16 | #define Elf_Shdr Elf32_Shdr | ||
17 | #define Elf_Sym Elf32_Sym | ||
18 | #define Elf_Ehdr Elf32_Ehdr | ||
19 | #define Elf_Addr Elf32_Addr | ||
20 | #define Elf_Word Elf32_Word | ||
21 | |||
22 | /* | ||
23 | * This file contains the C6x architecture specific module code. | ||
24 | */ | ||
25 | struct mod_arch_specific { | ||
26 | }; | ||
27 | |||
28 | struct loaded_sections { | ||
29 | unsigned int new_vaddr; | ||
30 | unsigned int loaded; | ||
31 | }; | ||
32 | |||
33 | #endif /* _ASM_C6X_MODULE_H */ | ||
diff --git a/arch/c6x/include/asm/mutex.h b/arch/c6x/include/asm/mutex.h new file mode 100644 index 000000000000..7a7248e0462d --- /dev/null +++ b/arch/c6x/include/asm/mutex.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef _ASM_C6X_MUTEX_H | ||
2 | #define _ASM_C6X_MUTEX_H | ||
3 | |||
4 | #include <asm-generic/mutex-null.h> | ||
5 | |||
6 | #endif /* _ASM_C6X_MUTEX_H */ | ||
diff --git a/arch/c6x/include/asm/page.h b/arch/c6x/include/asm/page.h new file mode 100644 index 000000000000..d18e2b0c7aea --- /dev/null +++ b/arch/c6x/include/asm/page.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #ifndef _ASM_C6X_PAGE_H | ||
2 | #define _ASM_C6X_PAGE_H | ||
3 | |||
4 | #define VM_DATA_DEFAULT_FLAGS \ | ||
5 | (VM_READ | VM_WRITE | \ | ||
6 | ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) | \ | ||
7 | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) | ||
8 | |||
9 | #include <asm-generic/page.h> | ||
10 | |||
11 | #endif /* _ASM_C6X_PAGE_H */ | ||
diff --git a/arch/c6x/include/asm/pgtable.h b/arch/c6x/include/asm/pgtable.h new file mode 100644 index 000000000000..68c8af4f1f97 --- /dev/null +++ b/arch/c6x/include/asm/pgtable.h | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_PGTABLE_H | ||
12 | #define _ASM_C6X_PGTABLE_H | ||
13 | |||
14 | #include <asm-generic/4level-fixup.h> | ||
15 | |||
16 | #include <asm/setup.h> | ||
17 | #include <asm/page.h> | ||
18 | |||
19 | /* | ||
20 | * All 32bit addresses are effectively valid for vmalloc... | ||
21 | * Sort of meaningless for non-VM targets. | ||
22 | */ | ||
23 | #define VMALLOC_START 0 | ||
24 | #define VMALLOC_END 0xffffffff | ||
25 | |||
26 | #define pgd_present(pgd) (1) | ||
27 | #define pgd_none(pgd) (0) | ||
28 | #define pgd_bad(pgd) (0) | ||
29 | #define pgd_clear(pgdp) | ||
30 | #define kern_addr_valid(addr) (1) | ||
31 | |||
32 | #define pmd_offset(a, b) ((void *)0) | ||
33 | #define pmd_none(x) (!pmd_val(x)) | ||
34 | #define pmd_present(x) (pmd_val(x)) | ||
35 | #define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) | ||
36 | #define pmd_bad(x) (pmd_val(x) & ~PAGE_MASK) | ||
37 | |||
38 | #define PAGE_NONE __pgprot(0) /* these mean nothing to NO_MM */ | ||
39 | #define PAGE_SHARED __pgprot(0) /* these mean nothing to NO_MM */ | ||
40 | #define PAGE_COPY __pgprot(0) /* these mean nothing to NO_MM */ | ||
41 | #define PAGE_READONLY __pgprot(0) /* these mean nothing to NO_MM */ | ||
42 | #define PAGE_KERNEL __pgprot(0) /* these mean nothing to NO_MM */ | ||
43 | #define pgprot_noncached(prot) (prot) | ||
44 | |||
45 | extern void paging_init(void); | ||
46 | |||
47 | #define __swp_type(x) (0) | ||
48 | #define __swp_offset(x) (0) | ||
49 | #define __swp_entry(typ, off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) | ||
50 | #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) | ||
51 | #define __swp_entry_to_pte(x) ((pte_t) { (x).val }) | ||
52 | |||
53 | static inline int pte_file(pte_t pte) | ||
54 | { | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | #define set_pte(pteptr, pteval) (*(pteptr) = pteval) | ||
59 | #define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval) | ||
60 | |||
61 | /* | ||
62 | * ZERO_PAGE is a global shared page that is always zero: used | ||
63 | * for zero-mapped memory areas etc.. | ||
64 | */ | ||
65 | #define ZERO_PAGE(vaddr) virt_to_page(empty_zero_page) | ||
66 | extern unsigned long empty_zero_page; | ||
67 | |||
68 | #define swapper_pg_dir ((pgd_t *) 0) | ||
69 | |||
70 | /* | ||
71 | * No page table caches to initialise | ||
72 | */ | ||
73 | #define pgtable_cache_init() do { } while (0) | ||
74 | #define io_remap_pfn_range remap_pfn_range | ||
75 | |||
76 | #define io_remap_page_range(vma, vaddr, paddr, size, prot) \ | ||
77 | remap_pfn_range(vma, vaddr, (paddr) >> PAGE_SHIFT, size, prot) | ||
78 | |||
79 | #include <asm-generic/pgtable.h> | ||
80 | |||
81 | #endif /* _ASM_C6X_PGTABLE_H */ | ||
diff --git a/arch/c6x/include/asm/processor.h b/arch/c6x/include/asm/processor.h new file mode 100644 index 000000000000..8154c4ee8c9c --- /dev/null +++ b/arch/c6x/include/asm/processor.h | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Updated for 2.6.34: Mark Salter <msalter@redhat.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef _ASM_C6X_PROCESSOR_H | ||
14 | #define _ASM_C6X_PROCESSOR_H | ||
15 | |||
16 | #include <asm/ptrace.h> | ||
17 | #include <asm/page.h> | ||
18 | #include <asm/current.h> | ||
19 | |||
20 | /* | ||
21 | * Default implementation of macro that returns current | ||
22 | * instruction pointer ("program counter"). | ||
23 | */ | ||
24 | #define current_text_addr() \ | ||
25 | ({ \ | ||
26 | void *__pc; \ | ||
27 | asm("mvc .S2 pce1,%0\n" : "=b"(__pc)); \ | ||
28 | __pc; \ | ||
29 | }) | ||
30 | |||
31 | /* | ||
32 | * User space process size. This is mostly meaningless for NOMMU | ||
33 | * but some C6X processors may have RAM addresses up to 0xFFFFFFFF. | ||
34 | * Since calls like mmap() can return an address or an error, we | ||
35 | * have to allow room for error returns when code does something | ||
36 | * like: | ||
37 | * | ||
38 | * addr = do_mmap(...) | ||
39 | * if ((unsigned long)addr >= TASK_SIZE) | ||
40 | * ... its an error code, not an address ... | ||
41 | * | ||
42 | * Here, we allow for 4096 error codes which means we really can't | ||
43 | * use the last 4K page on systems with RAM extending all the way | ||
44 | * to the end of the 32-bit address space. | ||
45 | */ | ||
46 | #define TASK_SIZE 0xFFFFF000 | ||
47 | |||
48 | /* | ||
49 | * This decides where the kernel will search for a free chunk of vm | ||
50 | * space during mmap's. We won't be using it | ||
51 | */ | ||
52 | #define TASK_UNMAPPED_BASE 0 | ||
53 | |||
54 | struct thread_struct { | ||
55 | unsigned long long b15_14; | ||
56 | unsigned long long a15_14; | ||
57 | unsigned long long b13_12; | ||
58 | unsigned long long a13_12; | ||
59 | unsigned long long b11_10; | ||
60 | unsigned long long a11_10; | ||
61 | unsigned long long ricl_icl; | ||
62 | unsigned long usp; /* user stack pointer */ | ||
63 | unsigned long pc; /* kernel pc */ | ||
64 | unsigned long wchan; | ||
65 | }; | ||
66 | |||
67 | #define INIT_THREAD \ | ||
68 | { \ | ||
69 | .usp = 0, \ | ||
70 | .wchan = 0, \ | ||
71 | } | ||
72 | |||
73 | #define INIT_MMAP { \ | ||
74 | &init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, \ | ||
75 | NULL, NULL } | ||
76 | |||
77 | #define task_pt_regs(task) \ | ||
78 | ((struct pt_regs *)(THREAD_START_SP + task_stack_page(task)) - 1) | ||
79 | |||
80 | #define alloc_kernel_stack() __get_free_page(GFP_KERNEL) | ||
81 | #define free_kernel_stack(page) free_page((page)) | ||
82 | |||
83 | |||
84 | /* Forward declaration, a strange C thing */ | ||
85 | struct task_struct; | ||
86 | |||
87 | extern void start_thread(struct pt_regs *regs, unsigned int pc, | ||
88 | unsigned long usp); | ||
89 | |||
90 | /* Free all resources held by a thread. */ | ||
91 | static inline void release_thread(struct task_struct *dead_task) | ||
92 | { | ||
93 | } | ||
94 | |||
95 | /* Prepare to copy thread state - unlazy all lazy status */ | ||
96 | #define prepare_to_copy(tsk) do { } while (0) | ||
97 | |||
98 | extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); | ||
99 | |||
100 | #define copy_segments(tsk, mm) do { } while (0) | ||
101 | #define release_segments(mm) do { } while (0) | ||
102 | |||
103 | /* | ||
104 | * saved PC of a blocked thread. | ||
105 | */ | ||
106 | #define thread_saved_pc(tsk) (task_pt_regs(tsk)->pc) | ||
107 | |||
108 | /* | ||
109 | * saved kernel SP and DP of a blocked thread. | ||
110 | */ | ||
111 | #ifdef _BIG_ENDIAN | ||
112 | #define thread_saved_ksp(tsk) \ | ||
113 | (*(unsigned long *)&(tsk)->thread.b15_14) | ||
114 | #define thread_saved_dp(tsk) \ | ||
115 | (*(((unsigned long *)&(tsk)->thread.b15_14) + 1)) | ||
116 | #else | ||
117 | #define thread_saved_ksp(tsk) \ | ||
118 | (*(((unsigned long *)&(tsk)->thread.b15_14) + 1)) | ||
119 | #define thread_saved_dp(tsk) \ | ||
120 | (*(unsigned long *)&(tsk)->thread.b15_14) | ||
121 | #endif | ||
122 | |||
123 | extern unsigned long get_wchan(struct task_struct *p); | ||
124 | |||
125 | #define KSTK_EIP(tsk) (task_pt_regs(task)->pc) | ||
126 | #define KSTK_ESP(tsk) (task_pt_regs(task)->sp) | ||
127 | |||
128 | #define cpu_relax() do { } while (0) | ||
129 | |||
130 | extern const struct seq_operations cpuinfo_op; | ||
131 | |||
132 | #endif /* ASM_C6X_PROCESSOR_H */ | ||
diff --git a/arch/c6x/include/asm/procinfo.h b/arch/c6x/include/asm/procinfo.h new file mode 100644 index 000000000000..c139d1e71f87 --- /dev/null +++ b/arch/c6x/include/asm/procinfo.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter (msalter@redhat.com) | ||
4 | * | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #ifndef _ASM_C6X_PROCINFO_H | ||
11 | #define _ASM_C6X_PROCINFO_H | ||
12 | |||
13 | #ifdef __KERNEL__ | ||
14 | |||
15 | struct proc_info_list { | ||
16 | unsigned int cpu_val; | ||
17 | unsigned int cpu_mask; | ||
18 | const char *arch_name; | ||
19 | const char *elf_name; | ||
20 | unsigned int elf_hwcap; | ||
21 | }; | ||
22 | |||
23 | #else /* __KERNEL__ */ | ||
24 | #include <asm/elf.h> | ||
25 | #warning "Please include asm/elf.h instead" | ||
26 | #endif /* __KERNEL__ */ | ||
27 | |||
28 | #endif /* _ASM_C6X_PROCINFO_H */ | ||
diff --git a/arch/c6x/include/asm/prom.h b/arch/c6x/include/asm/prom.h new file mode 100644 index 000000000000..b4ec95f07518 --- /dev/null +++ b/arch/c6x/include/asm/prom.h | |||
@@ -0,0 +1 @@ | |||
/* dummy prom.h; here to make linux/of.h's #includes happy */ | |||
diff --git a/arch/c6x/include/asm/ptrace.h b/arch/c6x/include/asm/ptrace.h new file mode 100644 index 000000000000..21e8d7931fe7 --- /dev/null +++ b/arch/c6x/include/asm/ptrace.h | |||
@@ -0,0 +1,174 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, 2006, 2009, 2010 Texas Instruments Incorporated | ||
3 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
4 | * | ||
5 | * Updated for 2.6.34: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_PTRACE_H | ||
12 | #define _ASM_C6X_PTRACE_H | ||
13 | |||
14 | #define BKPT_OPCODE 0x56454314 /* illegal opcode */ | ||
15 | |||
16 | #ifdef _BIG_ENDIAN | ||
17 | #define PT_LO(odd, even) odd | ||
18 | #define PT_HI(odd, even) even | ||
19 | #else | ||
20 | #define PT_LO(odd, even) even | ||
21 | #define PT_HI(odd, even) odd | ||
22 | #endif | ||
23 | |||
24 | #define PT_A4_ORG PT_LO(1, 0) | ||
25 | #define PT_TSR PT_HI(1, 0) | ||
26 | #define PT_ILC PT_LO(3, 2) | ||
27 | #define PT_RILC PT_HI(3, 2) | ||
28 | #define PT_CSR PT_LO(5, 4) | ||
29 | #define PT_PC PT_HI(5, 4) | ||
30 | #define PT_B16 PT_LO(7, 6) | ||
31 | #define PT_B17 PT_HI(7, 6) | ||
32 | #define PT_B18 PT_LO(9, 8) | ||
33 | #define PT_B19 PT_HI(9, 8) | ||
34 | #define PT_B20 PT_LO(11, 10) | ||
35 | #define PT_B21 PT_HI(11, 10) | ||
36 | #define PT_B22 PT_LO(13, 12) | ||
37 | #define PT_B23 PT_HI(13, 12) | ||
38 | #define PT_B24 PT_LO(15, 14) | ||
39 | #define PT_B25 PT_HI(15, 14) | ||
40 | #define PT_B26 PT_LO(17, 16) | ||
41 | #define PT_B27 PT_HI(17, 16) | ||
42 | #define PT_B28 PT_LO(19, 18) | ||
43 | #define PT_B29 PT_HI(19, 18) | ||
44 | #define PT_B30 PT_LO(21, 20) | ||
45 | #define PT_B31 PT_HI(21, 20) | ||
46 | #define PT_B0 PT_LO(23, 22) | ||
47 | #define PT_B1 PT_HI(23, 22) | ||
48 | #define PT_B2 PT_LO(25, 24) | ||
49 | #define PT_B3 PT_HI(25, 24) | ||
50 | #define PT_B4 PT_LO(27, 26) | ||
51 | #define PT_B5 PT_HI(27, 26) | ||
52 | #define PT_B6 PT_LO(29, 28) | ||
53 | #define PT_B7 PT_HI(29, 28) | ||
54 | #define PT_B8 PT_LO(31, 30) | ||
55 | #define PT_B9 PT_HI(31, 30) | ||
56 | #define PT_B10 PT_LO(33, 32) | ||
57 | #define PT_B11 PT_HI(33, 32) | ||
58 | #define PT_B12 PT_LO(35, 34) | ||
59 | #define PT_B13 PT_HI(35, 34) | ||
60 | #define PT_A16 PT_LO(37, 36) | ||
61 | #define PT_A17 PT_HI(37, 36) | ||
62 | #define PT_A18 PT_LO(39, 38) | ||
63 | #define PT_A19 PT_HI(39, 38) | ||
64 | #define PT_A20 PT_LO(41, 40) | ||
65 | #define PT_A21 PT_HI(41, 40) | ||
66 | #define PT_A22 PT_LO(43, 42) | ||
67 | #define PT_A23 PT_HI(43, 42) | ||
68 | #define PT_A24 PT_LO(45, 44) | ||
69 | #define PT_A25 PT_HI(45, 44) | ||
70 | #define PT_A26 PT_LO(47, 46) | ||
71 | #define PT_A27 PT_HI(47, 46) | ||
72 | #define PT_A28 PT_LO(49, 48) | ||
73 | #define PT_A29 PT_HI(49, 48) | ||
74 | #define PT_A30 PT_LO(51, 50) | ||
75 | #define PT_A31 PT_HI(51, 50) | ||
76 | #define PT_A0 PT_LO(53, 52) | ||
77 | #define PT_A1 PT_HI(53, 52) | ||
78 | #define PT_A2 PT_LO(55, 54) | ||
79 | #define PT_A3 PT_HI(55, 54) | ||
80 | #define PT_A4 PT_LO(57, 56) | ||
81 | #define PT_A5 PT_HI(57, 56) | ||
82 | #define PT_A6 PT_LO(59, 58) | ||
83 | #define PT_A7 PT_HI(59, 58) | ||
84 | #define PT_A8 PT_LO(61, 60) | ||
85 | #define PT_A9 PT_HI(61, 60) | ||
86 | #define PT_A10 PT_LO(63, 62) | ||
87 | #define PT_A11 PT_HI(63, 62) | ||
88 | #define PT_A12 PT_LO(65, 64) | ||
89 | #define PT_A13 PT_HI(65, 64) | ||
90 | #define PT_A14 PT_LO(67, 66) | ||
91 | #define PT_A15 PT_HI(67, 66) | ||
92 | #define PT_B14 PT_LO(69, 68) | ||
93 | #define PT_B15 PT_HI(69, 68) | ||
94 | |||
95 | #define NR_PTREGS 70 | ||
96 | |||
97 | #define PT_DP PT_B14 /* Data Segment Pointer (B14) */ | ||
98 | #define PT_SP PT_B15 /* Stack Pointer (B15) */ | ||
99 | |||
100 | #ifndef __ASSEMBLY__ | ||
101 | |||
102 | #ifdef _BIG_ENDIAN | ||
103 | #define REG_PAIR(odd, even) unsigned long odd; unsigned long even | ||
104 | #else | ||
105 | #define REG_PAIR(odd, even) unsigned long even; unsigned long odd | ||
106 | #endif | ||
107 | |||
108 | /* | ||
109 | * this struct defines the way the registers are stored on the | ||
110 | * stack during a system call. fields defined with REG_PAIR | ||
111 | * are saved and restored using double-word memory operations | ||
112 | * which means the word ordering of the pair depends on endianess. | ||
113 | */ | ||
114 | struct pt_regs { | ||
115 | REG_PAIR(tsr, orig_a4); | ||
116 | REG_PAIR(rilc, ilc); | ||
117 | REG_PAIR(pc, csr); | ||
118 | |||
119 | REG_PAIR(b17, b16); | ||
120 | REG_PAIR(b19, b18); | ||
121 | REG_PAIR(b21, b20); | ||
122 | REG_PAIR(b23, b22); | ||
123 | REG_PAIR(b25, b24); | ||
124 | REG_PAIR(b27, b26); | ||
125 | REG_PAIR(b29, b28); | ||
126 | REG_PAIR(b31, b30); | ||
127 | |||
128 | REG_PAIR(b1, b0); | ||
129 | REG_PAIR(b3, b2); | ||
130 | REG_PAIR(b5, b4); | ||
131 | REG_PAIR(b7, b6); | ||
132 | REG_PAIR(b9, b8); | ||
133 | REG_PAIR(b11, b10); | ||
134 | REG_PAIR(b13, b12); | ||
135 | |||
136 | REG_PAIR(a17, a16); | ||
137 | REG_PAIR(a19, a18); | ||
138 | REG_PAIR(a21, a20); | ||
139 | REG_PAIR(a23, a22); | ||
140 | REG_PAIR(a25, a24); | ||
141 | REG_PAIR(a27, a26); | ||
142 | REG_PAIR(a29, a28); | ||
143 | REG_PAIR(a31, a30); | ||
144 | |||
145 | REG_PAIR(a1, a0); | ||
146 | REG_PAIR(a3, a2); | ||
147 | REG_PAIR(a5, a4); | ||
148 | REG_PAIR(a7, a6); | ||
149 | REG_PAIR(a9, a8); | ||
150 | REG_PAIR(a11, a10); | ||
151 | REG_PAIR(a13, a12); | ||
152 | |||
153 | REG_PAIR(a15, a14); | ||
154 | REG_PAIR(sp, dp); | ||
155 | }; | ||
156 | |||
157 | #ifdef __KERNEL__ | ||
158 | |||
159 | #include <linux/linkage.h> | ||
160 | |||
161 | #define user_mode(regs) ((((regs)->tsr) & 0x40) != 0) | ||
162 | |||
163 | #define instruction_pointer(regs) ((regs)->pc) | ||
164 | #define profile_pc(regs) instruction_pointer(regs) | ||
165 | #define user_stack_pointer(regs) ((regs)->sp) | ||
166 | |||
167 | extern void show_regs(struct pt_regs *); | ||
168 | |||
169 | extern asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs); | ||
170 | extern asmlinkage void syscall_trace_exit(struct pt_regs *regs); | ||
171 | |||
172 | #endif /* __KERNEL__ */ | ||
173 | #endif /* __ASSEMBLY__ */ | ||
174 | #endif /* _ASM_C6X_PTRACE_H */ | ||
diff --git a/arch/c6x/include/asm/sections.h b/arch/c6x/include/asm/sections.h new file mode 100644 index 000000000000..f703989d837a --- /dev/null +++ b/arch/c6x/include/asm/sections.h | |||
@@ -0,0 +1,12 @@ | |||
1 | #ifndef _ASM_C6X_SECTIONS_H | ||
2 | #define _ASM_C6X_SECTIONS_H | ||
3 | |||
4 | #include <asm-generic/sections.h> | ||
5 | |||
6 | extern char _vectors_start[]; | ||
7 | extern char _vectors_end[]; | ||
8 | |||
9 | extern char _data_lma[]; | ||
10 | extern char _fdt_start[], _fdt_end[]; | ||
11 | |||
12 | #endif /* _ASM_C6X_SECTIONS_H */ | ||
diff --git a/arch/c6x/include/asm/setup.h b/arch/c6x/include/asm/setup.h new file mode 100644 index 000000000000..1808f279f82e --- /dev/null +++ b/arch/c6x/include/asm/setup.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_SETUP_H | ||
12 | #define _ASM_C6X_SETUP_H | ||
13 | |||
14 | #define COMMAND_LINE_SIZE 1024 | ||
15 | |||
16 | #ifndef __ASSEMBLY__ | ||
17 | extern char c6x_command_line[COMMAND_LINE_SIZE]; | ||
18 | |||
19 | extern int c6x_add_memory(phys_addr_t start, unsigned long size); | ||
20 | |||
21 | extern unsigned long ram_start; | ||
22 | extern unsigned long ram_end; | ||
23 | |||
24 | extern int c6x_num_cores; | ||
25 | extern unsigned int c6x_silicon_rev; | ||
26 | extern unsigned int c6x_devstat; | ||
27 | extern unsigned char c6x_fuse_mac[6]; | ||
28 | |||
29 | extern void machine_init(unsigned long dt_ptr); | ||
30 | |||
31 | #endif /* !__ASSEMBLY__ */ | ||
32 | #endif /* _ASM_C6X_SETUP_H */ | ||
diff --git a/arch/c6x/include/asm/sigcontext.h b/arch/c6x/include/asm/sigcontext.h new file mode 100644 index 000000000000..eb702f39cde7 --- /dev/null +++ b/arch/c6x/include/asm/sigcontext.h | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_SIGCONTEXT_H | ||
12 | #define _ASM_C6X_SIGCONTEXT_H | ||
13 | |||
14 | |||
15 | struct sigcontext { | ||
16 | unsigned long sc_mask; /* old sigmask */ | ||
17 | unsigned long sc_sp; /* old user stack pointer */ | ||
18 | |||
19 | unsigned long sc_a4; | ||
20 | unsigned long sc_b4; | ||
21 | unsigned long sc_a6; | ||
22 | unsigned long sc_b6; | ||
23 | unsigned long sc_a8; | ||
24 | unsigned long sc_b8; | ||
25 | |||
26 | unsigned long sc_a0; | ||
27 | unsigned long sc_a1; | ||
28 | unsigned long sc_a2; | ||
29 | unsigned long sc_a3; | ||
30 | unsigned long sc_a5; | ||
31 | unsigned long sc_a7; | ||
32 | unsigned long sc_a9; | ||
33 | |||
34 | unsigned long sc_b0; | ||
35 | unsigned long sc_b1; | ||
36 | unsigned long sc_b2; | ||
37 | unsigned long sc_b3; | ||
38 | unsigned long sc_b5; | ||
39 | unsigned long sc_b7; | ||
40 | unsigned long sc_b9; | ||
41 | |||
42 | unsigned long sc_a16; | ||
43 | unsigned long sc_a17; | ||
44 | unsigned long sc_a18; | ||
45 | unsigned long sc_a19; | ||
46 | unsigned long sc_a20; | ||
47 | unsigned long sc_a21; | ||
48 | unsigned long sc_a22; | ||
49 | unsigned long sc_a23; | ||
50 | unsigned long sc_a24; | ||
51 | unsigned long sc_a25; | ||
52 | unsigned long sc_a26; | ||
53 | unsigned long sc_a27; | ||
54 | unsigned long sc_a28; | ||
55 | unsigned long sc_a29; | ||
56 | unsigned long sc_a30; | ||
57 | unsigned long sc_a31; | ||
58 | |||
59 | unsigned long sc_b16; | ||
60 | unsigned long sc_b17; | ||
61 | unsigned long sc_b18; | ||
62 | unsigned long sc_b19; | ||
63 | unsigned long sc_b20; | ||
64 | unsigned long sc_b21; | ||
65 | unsigned long sc_b22; | ||
66 | unsigned long sc_b23; | ||
67 | unsigned long sc_b24; | ||
68 | unsigned long sc_b25; | ||
69 | unsigned long sc_b26; | ||
70 | unsigned long sc_b27; | ||
71 | unsigned long sc_b28; | ||
72 | unsigned long sc_b29; | ||
73 | unsigned long sc_b30; | ||
74 | unsigned long sc_b31; | ||
75 | |||
76 | unsigned long sc_csr; | ||
77 | unsigned long sc_pc; | ||
78 | }; | ||
79 | |||
80 | #endif /* _ASM_C6X_SIGCONTEXT_H */ | ||
diff --git a/arch/c6x/include/asm/signal.h b/arch/c6x/include/asm/signal.h new file mode 100644 index 000000000000..f1cd870596a3 --- /dev/null +++ b/arch/c6x/include/asm/signal.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef _ASM_C6X_SIGNAL_H | ||
2 | #define _ASM_C6X_SIGNAL_H | ||
3 | |||
4 | #include <asm-generic/signal.h> | ||
5 | |||
6 | #ifndef __ASSEMBLY__ | ||
7 | #include <linux/linkage.h> | ||
8 | |||
9 | struct pt_regs; | ||
10 | |||
11 | extern asmlinkage int do_rt_sigreturn(struct pt_regs *regs); | ||
12 | extern asmlinkage void do_notify_resume(struct pt_regs *regs, | ||
13 | u32 thread_info_flags, | ||
14 | int syscall); | ||
15 | #endif | ||
16 | |||
17 | #endif /* _ASM_C6X_SIGNAL_H */ | ||
diff --git a/arch/c6x/include/asm/soc.h b/arch/c6x/include/asm/soc.h new file mode 100644 index 000000000000..43f50159e59b --- /dev/null +++ b/arch/c6x/include/asm/soc.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * Miscellaneous SoC-specific hooks. | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
5 | * | ||
6 | * Author: Mark Salter <msalter@redhat.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | #ifndef _ASM_C6X_SOC_H | ||
13 | #define _ASM_C6X_SOC_H | ||
14 | |||
15 | struct soc_ops { | ||
16 | /* Return active exception event or -1 if none */ | ||
17 | int (*get_exception)(void); | ||
18 | |||
19 | /* Assert an event */ | ||
20 | void (*assert_event)(unsigned int evt); | ||
21 | }; | ||
22 | |||
23 | extern struct soc_ops soc_ops; | ||
24 | |||
25 | extern int soc_get_exception(void); | ||
26 | extern void soc_assert_event(unsigned int event); | ||
27 | extern int soc_mac_addr(unsigned int index, u8 *addr); | ||
28 | |||
29 | /* | ||
30 | * for mmio on SoC devices. regs are always same byte order as cpu. | ||
31 | */ | ||
32 | #define soc_readl(addr) __raw_readl(addr) | ||
33 | #define soc_writel(b, addr) __raw_writel((b), (addr)) | ||
34 | |||
35 | #endif /* _ASM_C6X_SOC_H */ | ||
diff --git a/arch/c6x/include/asm/string.h b/arch/c6x/include/asm/string.h new file mode 100644 index 000000000000..b21517c80a17 --- /dev/null +++ b/arch/c6x/include/asm/string.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_STRING_H | ||
12 | #define _ASM_C6X_STRING_H | ||
13 | |||
14 | #include <asm/page.h> | ||
15 | #include <linux/linkage.h> | ||
16 | |||
17 | asmlinkage extern void *memcpy(void *to, const void *from, size_t n); | ||
18 | |||
19 | #define __HAVE_ARCH_MEMCPY | ||
20 | |||
21 | #endif /* _ASM_C6X_STRING_H */ | ||
diff --git a/arch/c6x/include/asm/swab.h b/arch/c6x/include/asm/swab.h new file mode 100644 index 000000000000..fd4bb0520e5e --- /dev/null +++ b/arch/c6x/include/asm/swab.h | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter <msalter@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #ifndef _ASM_C6X_SWAB_H | ||
10 | #define _ASM_C6X_SWAB_H | ||
11 | |||
12 | static inline __attribute_const__ __u16 __c6x_swab16(__u16 val) | ||
13 | { | ||
14 | asm("swap4 .l1 %0,%0\n" : "+a"(val)); | ||
15 | return val; | ||
16 | } | ||
17 | |||
18 | static inline __attribute_const__ __u32 __c6x_swab32(__u32 val) | ||
19 | { | ||
20 | asm("swap4 .l1 %0,%0\n" | ||
21 | "swap2 .l1 %0,%0\n" | ||
22 | : "+a"(val)); | ||
23 | return val; | ||
24 | } | ||
25 | |||
26 | static inline __attribute_const__ __u64 __c6x_swab64(__u64 val) | ||
27 | { | ||
28 | asm(" swap2 .s1 %p0,%P0\n" | ||
29 | "|| swap2 .l1 %P0,%p0\n" | ||
30 | " swap4 .l1 %p0,%p0\n" | ||
31 | " swap4 .l1 %P0,%P0\n" | ||
32 | : "+a"(val)); | ||
33 | return val; | ||
34 | } | ||
35 | |||
36 | static inline __attribute_const__ __u32 __c6x_swahw32(__u32 val) | ||
37 | { | ||
38 | asm("swap2 .l1 %0,%0\n" : "+a"(val)); | ||
39 | return val; | ||
40 | } | ||
41 | |||
42 | static inline __attribute_const__ __u32 __c6x_swahb32(__u32 val) | ||
43 | { | ||
44 | asm("swap4 .l1 %0,%0\n" : "+a"(val)); | ||
45 | return val; | ||
46 | } | ||
47 | |||
48 | #define __arch_swab16 __c6x_swab16 | ||
49 | #define __arch_swab32 __c6x_swab32 | ||
50 | #define __arch_swab64 __c6x_swab64 | ||
51 | #define __arch_swahw32 __c6x_swahw32 | ||
52 | #define __arch_swahb32 __c6x_swahb32 | ||
53 | |||
54 | #endif /* _ASM_C6X_SWAB_H */ | ||
diff --git a/arch/c6x/include/asm/syscall.h b/arch/c6x/include/asm/syscall.h new file mode 100644 index 000000000000..ae2be315ee9c --- /dev/null +++ b/arch/c6x/include/asm/syscall.h | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter <msalter@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #ifndef __ASM_C6X_SYSCALL_H | ||
12 | #define __ASM_C6X_SYSCALL_H | ||
13 | |||
14 | #include <linux/err.h> | ||
15 | #include <linux/sched.h> | ||
16 | |||
17 | static inline int syscall_get_nr(struct task_struct *task, | ||
18 | struct pt_regs *regs) | ||
19 | { | ||
20 | return regs->b0; | ||
21 | } | ||
22 | |||
23 | static inline void syscall_rollback(struct task_struct *task, | ||
24 | struct pt_regs *regs) | ||
25 | { | ||
26 | /* do nothing */ | ||
27 | } | ||
28 | |||
29 | static inline long syscall_get_error(struct task_struct *task, | ||
30 | struct pt_regs *regs) | ||
31 | { | ||
32 | return IS_ERR_VALUE(regs->a4) ? regs->a4 : 0; | ||
33 | } | ||
34 | |||
35 | static inline long syscall_get_return_value(struct task_struct *task, | ||
36 | struct pt_regs *regs) | ||
37 | { | ||
38 | return regs->a4; | ||
39 | } | ||
40 | |||
41 | static inline void syscall_set_return_value(struct task_struct *task, | ||
42 | struct pt_regs *regs, | ||
43 | int error, long val) | ||
44 | { | ||
45 | regs->a4 = error ?: val; | ||
46 | } | ||
47 | |||
48 | static inline void syscall_get_arguments(struct task_struct *task, | ||
49 | struct pt_regs *regs, unsigned int i, | ||
50 | unsigned int n, unsigned long *args) | ||
51 | { | ||
52 | switch (i) { | ||
53 | case 0: | ||
54 | if (!n--) | ||
55 | break; | ||
56 | *args++ = regs->a4; | ||
57 | case 1: | ||
58 | if (!n--) | ||
59 | break; | ||
60 | *args++ = regs->b4; | ||
61 | case 2: | ||
62 | if (!n--) | ||
63 | break; | ||
64 | *args++ = regs->a6; | ||
65 | case 3: | ||
66 | if (!n--) | ||
67 | break; | ||
68 | *args++ = regs->b6; | ||
69 | case 4: | ||
70 | if (!n--) | ||
71 | break; | ||
72 | *args++ = regs->a8; | ||
73 | case 5: | ||
74 | if (!n--) | ||
75 | break; | ||
76 | *args++ = regs->b8; | ||
77 | case 6: | ||
78 | if (!n--) | ||
79 | break; | ||
80 | default: | ||
81 | BUG(); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | static inline void syscall_set_arguments(struct task_struct *task, | ||
86 | struct pt_regs *regs, | ||
87 | unsigned int i, unsigned int n, | ||
88 | const unsigned long *args) | ||
89 | { | ||
90 | switch (i) { | ||
91 | case 0: | ||
92 | if (!n--) | ||
93 | break; | ||
94 | regs->a4 = *args++; | ||
95 | case 1: | ||
96 | if (!n--) | ||
97 | break; | ||
98 | regs->b4 = *args++; | ||
99 | case 2: | ||
100 | if (!n--) | ||
101 | break; | ||
102 | regs->a6 = *args++; | ||
103 | case 3: | ||
104 | if (!n--) | ||
105 | break; | ||
106 | regs->b6 = *args++; | ||
107 | case 4: | ||
108 | if (!n--) | ||
109 | break; | ||
110 | regs->a8 = *args++; | ||
111 | case 5: | ||
112 | if (!n--) | ||
113 | break; | ||
114 | regs->a9 = *args++; | ||
115 | case 6: | ||
116 | if (!n) | ||
117 | break; | ||
118 | default: | ||
119 | BUG(); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | #endif /* __ASM_C6X_SYSCALLS_H */ | ||
diff --git a/arch/c6x/include/asm/syscalls.h b/arch/c6x/include/asm/syscalls.h new file mode 100644 index 000000000000..aed53da703c9 --- /dev/null +++ b/arch/c6x/include/asm/syscalls.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter <msalter@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation, version 2. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
12 | * NON INFRINGEMENT. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #ifndef __ASM_C6X_SYSCALLS_H | ||
17 | #define __ASM_C6X_SYSCALLS_H | ||
18 | |||
19 | #include <linux/compiler.h> | ||
20 | #include <linux/linkage.h> | ||
21 | #include <linux/types.h> | ||
22 | |||
23 | /* The array of function pointers for syscalls. */ | ||
24 | extern void *sys_call_table[]; | ||
25 | |||
26 | /* The following are trampolines in entry.S to handle 64-bit arguments */ | ||
27 | extern long sys_pread_c6x(unsigned int fd, char __user *buf, | ||
28 | size_t count, off_t pos_low, off_t pos_high); | ||
29 | extern long sys_pwrite_c6x(unsigned int fd, const char __user *buf, | ||
30 | size_t count, off_t pos_low, off_t pos_high); | ||
31 | extern long sys_truncate64_c6x(const char __user *path, | ||
32 | off_t length_low, off_t length_high); | ||
33 | extern long sys_ftruncate64_c6x(unsigned int fd, | ||
34 | off_t length_low, off_t length_high); | ||
35 | extern long sys_fadvise64_c6x(int fd, u32 offset_lo, u32 offset_hi, | ||
36 | u32 len, int advice); | ||
37 | extern long sys_fadvise64_64_c6x(int fd, u32 offset_lo, u32 offset_hi, | ||
38 | u32 len_lo, u32 len_hi, int advice); | ||
39 | extern long sys_fallocate_c6x(int fd, int mode, | ||
40 | u32 offset_lo, u32 offset_hi, | ||
41 | u32 len_lo, u32 len_hi); | ||
42 | extern int sys_cache_sync(unsigned long s, unsigned long e); | ||
43 | |||
44 | struct pt_regs; | ||
45 | |||
46 | extern asmlinkage long sys_c6x_clone(struct pt_regs *regs); | ||
47 | extern asmlinkage long sys_c6x_execve(const char __user *name, | ||
48 | const char __user *const __user *argv, | ||
49 | const char __user *const __user *envp, | ||
50 | struct pt_regs *regs); | ||
51 | |||
52 | |||
53 | #include <asm-generic/syscalls.h> | ||
54 | |||
55 | #endif /* __ASM_C6X_SYSCALLS_H */ | ||
diff --git a/arch/c6x/include/asm/system.h b/arch/c6x/include/asm/system.h new file mode 100644 index 000000000000..e076dc0eacc8 --- /dev/null +++ b/arch/c6x/include/asm/system.h | |||
@@ -0,0 +1,168 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_SYSTEM_H | ||
12 | #define _ASM_C6X_SYSTEM_H | ||
13 | |||
14 | #include <linux/linkage.h> | ||
15 | #include <linux/irqflags.h> | ||
16 | |||
17 | #define prepare_to_switch() do { } while (0) | ||
18 | |||
19 | struct task_struct; | ||
20 | struct thread_struct; | ||
21 | asmlinkage void *__switch_to(struct thread_struct *prev, | ||
22 | struct thread_struct *next, | ||
23 | struct task_struct *tsk); | ||
24 | |||
25 | #define switch_to(prev, next, last) \ | ||
26 | do { \ | ||
27 | current->thread.wchan = (u_long) __builtin_return_address(0); \ | ||
28 | (last) = __switch_to(&(prev)->thread, \ | ||
29 | &(next)->thread, (prev)); \ | ||
30 | mb(); \ | ||
31 | current->thread.wchan = 0; \ | ||
32 | } while (0) | ||
33 | |||
34 | /* Reset the board */ | ||
35 | #define HARD_RESET_NOW() | ||
36 | |||
37 | #define get_creg(reg) \ | ||
38 | ({ unsigned int __x; \ | ||
39 | asm volatile ("mvc .s2 " #reg ",%0\n" : "=b"(__x)); __x; }) | ||
40 | |||
41 | #define set_creg(reg, v) \ | ||
42 | do { unsigned int __x = (unsigned int)(v); \ | ||
43 | asm volatile ("mvc .s2 %0," #reg "\n" : : "b"(__x)); \ | ||
44 | } while (0) | ||
45 | |||
46 | #define or_creg(reg, n) \ | ||
47 | do { unsigned __x, __n = (unsigned)(n); \ | ||
48 | asm volatile ("mvc .s2 " #reg ",%0\n" \ | ||
49 | "or .l2 %1,%0,%0\n" \ | ||
50 | "mvc .s2 %0," #reg "\n" \ | ||
51 | "nop\n" \ | ||
52 | : "=&b"(__x) : "b"(__n)); \ | ||
53 | } while (0) | ||
54 | |||
55 | #define and_creg(reg, n) \ | ||
56 | do { unsigned __x, __n = (unsigned)(n); \ | ||
57 | asm volatile ("mvc .s2 " #reg ",%0\n" \ | ||
58 | "and .l2 %1,%0,%0\n" \ | ||
59 | "mvc .s2 %0," #reg "\n" \ | ||
60 | "nop\n" \ | ||
61 | : "=&b"(__x) : "b"(__n)); \ | ||
62 | } while (0) | ||
63 | |||
64 | #define get_coreid() (get_creg(DNUM) & 0xff) | ||
65 | |||
66 | /* Set/get IST */ | ||
67 | #define set_ist(x) set_creg(ISTP, x) | ||
68 | #define get_ist() get_creg(ISTP) | ||
69 | |||
70 | /* | ||
71 | * Exception management | ||
72 | */ | ||
73 | asmlinkage void enable_exception(void); | ||
74 | #define disable_exception() | ||
75 | #define get_except_type() get_creg(EFR) | ||
76 | #define ack_exception(type) set_creg(ECR, 1 << (type)) | ||
77 | #define get_iexcept() get_creg(IERR) | ||
78 | #define set_iexcept(mask) set_creg(IERR, (mask)) | ||
79 | |||
80 | /* | ||
81 | * Misc. functions | ||
82 | */ | ||
83 | #define nop() asm("NOP\n"); | ||
84 | #define mb() barrier() | ||
85 | #define rmb() barrier() | ||
86 | #define wmb() barrier() | ||
87 | #define set_mb(var, value) do { var = value; mb(); } while (0) | ||
88 | #define set_wmb(var, value) do { var = value; wmb(); } while (0) | ||
89 | |||
90 | #define smp_mb() barrier() | ||
91 | #define smp_rmb() barrier() | ||
92 | #define smp_wmb() barrier() | ||
93 | #define smp_read_barrier_depends() do { } while (0) | ||
94 | |||
95 | #define xchg(ptr, x) \ | ||
96 | ((__typeof__(*(ptr)))__xchg((unsigned int)(x), (void *) (ptr), \ | ||
97 | sizeof(*(ptr)))) | ||
98 | #define tas(ptr) xchg((ptr), 1) | ||
99 | |||
100 | unsigned int _lmbd(unsigned int, unsigned int); | ||
101 | unsigned int _bitr(unsigned int); | ||
102 | |||
103 | struct __xchg_dummy { unsigned int a[100]; }; | ||
104 | #define __xg(x) ((volatile struct __xchg_dummy *)(x)) | ||
105 | |||
106 | static inline unsigned int __xchg(unsigned int x, volatile void *ptr, int size) | ||
107 | { | ||
108 | unsigned int tmp; | ||
109 | unsigned long flags; | ||
110 | |||
111 | local_irq_save(flags); | ||
112 | |||
113 | switch (size) { | ||
114 | case 1: | ||
115 | tmp = 0; | ||
116 | tmp = *((unsigned char *) ptr); | ||
117 | *((unsigned char *) ptr) = (unsigned char) x; | ||
118 | break; | ||
119 | case 2: | ||
120 | tmp = 0; | ||
121 | tmp = *((unsigned short *) ptr); | ||
122 | *((unsigned short *) ptr) = x; | ||
123 | break; | ||
124 | case 4: | ||
125 | tmp = 0; | ||
126 | tmp = *((unsigned int *) ptr); | ||
127 | *((unsigned int *) ptr) = x; | ||
128 | break; | ||
129 | } | ||
130 | local_irq_restore(flags); | ||
131 | return tmp; | ||
132 | } | ||
133 | |||
134 | #include <asm-generic/cmpxchg-local.h> | ||
135 | |||
136 | /* | ||
137 | * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make | ||
138 | * them available. | ||
139 | */ | ||
140 | #define cmpxchg_local(ptr, o, n) \ | ||
141 | ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), \ | ||
142 | (unsigned long)(o), \ | ||
143 | (unsigned long)(n), \ | ||
144 | sizeof(*(ptr)))) | ||
145 | #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) | ||
146 | |||
147 | #include <asm-generic/cmpxchg.h> | ||
148 | |||
149 | #define _extu(x, s, e) \ | ||
150 | ({ unsigned int __x; \ | ||
151 | asm volatile ("extu .S2 %3,%1,%2,%0\n" : \ | ||
152 | "=b"(__x) : "n"(s), "n"(e), "b"(x)); \ | ||
153 | __x; }) | ||
154 | |||
155 | |||
156 | extern unsigned int c6x_core_freq; | ||
157 | |||
158 | struct pt_regs; | ||
159 | |||
160 | extern void die(char *str, struct pt_regs *fp, int nr); | ||
161 | extern asmlinkage int process_exception(struct pt_regs *regs); | ||
162 | extern void time_init(void); | ||
163 | extern void free_initmem(void); | ||
164 | |||
165 | extern void (*c6x_restart)(void); | ||
166 | extern void (*c6x_halt)(void); | ||
167 | |||
168 | #endif /* _ASM_C6X_SYSTEM_H */ | ||
diff --git a/arch/c6x/include/asm/thread_info.h b/arch/c6x/include/asm/thread_info.h new file mode 100644 index 000000000000..fd99148cda9d --- /dev/null +++ b/arch/c6x/include/asm/thread_info.h | |||
@@ -0,0 +1,121 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Updated for 2.6.3x: Mark Salter <msalter@redhat.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef _ASM_C6X_THREAD_INFO_H | ||
14 | #define _ASM_C6X_THREAD_INFO_H | ||
15 | |||
16 | #ifdef __KERNEL__ | ||
17 | |||
18 | #include <asm/page.h> | ||
19 | |||
20 | #ifdef CONFIG_4KSTACKS | ||
21 | #define THREAD_SIZE 4096 | ||
22 | #define THREAD_SHIFT 12 | ||
23 | #define THREAD_ORDER 0 | ||
24 | #else | ||
25 | #define THREAD_SIZE 8192 | ||
26 | #define THREAD_SHIFT 13 | ||
27 | #define THREAD_ORDER 1 | ||
28 | #endif | ||
29 | |||
30 | #define THREAD_START_SP (THREAD_SIZE - 8) | ||
31 | |||
32 | #ifndef __ASSEMBLY__ | ||
33 | |||
34 | typedef struct { | ||
35 | unsigned long seg; | ||
36 | } mm_segment_t; | ||
37 | |||
38 | /* | ||
39 | * low level task data. | ||
40 | */ | ||
41 | struct thread_info { | ||
42 | struct task_struct *task; /* main task structure */ | ||
43 | struct exec_domain *exec_domain; /* execution domain */ | ||
44 | unsigned long flags; /* low level flags */ | ||
45 | int cpu; /* cpu we're on */ | ||
46 | int preempt_count; /* 0 = preemptable, <0 = BUG */ | ||
47 | mm_segment_t addr_limit; /* thread address space */ | ||
48 | struct restart_block restart_block; | ||
49 | }; | ||
50 | |||
51 | /* | ||
52 | * macros/functions for gaining access to the thread information structure | ||
53 | * | ||
54 | * preempt_count needs to be 1 initially, until the scheduler is functional. | ||
55 | */ | ||
56 | #define INIT_THREAD_INFO(tsk) \ | ||
57 | { \ | ||
58 | .task = &tsk, \ | ||
59 | .exec_domain = &default_exec_domain, \ | ||
60 | .flags = 0, \ | ||
61 | .cpu = 0, \ | ||
62 | .preempt_count = INIT_PREEMPT_COUNT, \ | ||
63 | .addr_limit = KERNEL_DS, \ | ||
64 | .restart_block = { \ | ||
65 | .fn = do_no_restart_syscall, \ | ||
66 | }, \ | ||
67 | } | ||
68 | |||
69 | #define init_thread_info (init_thread_union.thread_info) | ||
70 | #define init_stack (init_thread_union.stack) | ||
71 | |||
72 | /* get the thread information struct of current task */ | ||
73 | static inline __attribute__((const)) | ||
74 | struct thread_info *current_thread_info(void) | ||
75 | { | ||
76 | struct thread_info *ti; | ||
77 | asm volatile (" clr .s2 B15,0,%1,%0\n" | ||
78 | : "=b" (ti) | ||
79 | : "Iu5" (THREAD_SHIFT - 1)); | ||
80 | return ti; | ||
81 | } | ||
82 | |||
83 | #define __HAVE_ARCH_THREAD_INFO_ALLOCATOR | ||
84 | |||
85 | /* thread information allocation */ | ||
86 | #ifdef CONFIG_DEBUG_STACK_USAGE | ||
87 | #define THREAD_FLAGS (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO) | ||
88 | #else | ||
89 | #define THREAD_FLAGS (GFP_KERNEL | __GFP_NOTRACK) | ||
90 | #endif | ||
91 | |||
92 | #define alloc_thread_info_node(tsk, node) \ | ||
93 | ((struct thread_info *)__get_free_pages(THREAD_FLAGS, THREAD_ORDER)) | ||
94 | |||
95 | #define free_thread_info(ti) free_pages((unsigned long) (ti), THREAD_ORDER) | ||
96 | #define get_thread_info(ti) get_task_struct((ti)->task) | ||
97 | #define put_thread_info(ti) put_task_struct((ti)->task) | ||
98 | #endif /* __ASSEMBLY__ */ | ||
99 | |||
100 | #define PREEMPT_ACTIVE 0x10000000 | ||
101 | |||
102 | /* | ||
103 | * thread information flag bit numbers | ||
104 | * - pending work-to-be-done flags are in LSW | ||
105 | * - other flags in MSW | ||
106 | */ | ||
107 | #define TIF_SYSCALL_TRACE 0 /* syscall trace active */ | ||
108 | #define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ | ||
109 | #define TIF_SIGPENDING 2 /* signal pending */ | ||
110 | #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ | ||
111 | #define TIF_RESTORE_SIGMASK 4 /* restore signal mask in do_signal() */ | ||
112 | |||
113 | #define TIF_POLLING_NRFLAG 16 /* true if polling TIF_NEED_RESCHED */ | ||
114 | #define TIF_MEMDIE 17 /* OOM killer killed process */ | ||
115 | |||
116 | #define TIF_WORK_MASK 0x00007FFE /* work on irq/exception return */ | ||
117 | #define TIF_ALLWORK_MASK 0x00007FFF /* work on any return to u-space */ | ||
118 | |||
119 | #endif /* __KERNEL__ */ | ||
120 | |||
121 | #endif /* _ASM_C6X_THREAD_INFO_H */ | ||
diff --git a/arch/c6x/include/asm/timer64.h b/arch/c6x/include/asm/timer64.h new file mode 100644 index 000000000000..bbe27bb9887e --- /dev/null +++ b/arch/c6x/include/asm/timer64.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef _C6X_TIMER64_H | ||
2 | #define _C6X_TIMER64_H | ||
3 | |||
4 | extern void __init timer64_init(void); | ||
5 | |||
6 | #endif /* _C6X_TIMER64_H */ | ||
diff --git a/arch/c6x/include/asm/timex.h b/arch/c6x/include/asm/timex.h new file mode 100644 index 000000000000..508c3ec971f9 --- /dev/null +++ b/arch/c6x/include/asm/timex.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Modified for 2.6.34: Mark Salter <msalter@redhat.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifndef _ASM_C6X_TIMEX_H | ||
14 | #define _ASM_C6X_TIMEX_H | ||
15 | |||
16 | #define CLOCK_TICK_RATE ((1000 * 1000000UL) / 6) | ||
17 | |||
18 | /* 64-bit timestamp */ | ||
19 | typedef unsigned long long cycles_t; | ||
20 | |||
21 | static inline cycles_t get_cycles(void) | ||
22 | { | ||
23 | unsigned l, h; | ||
24 | |||
25 | asm volatile (" dint\n" | ||
26 | " mvc .s2 TSCL,%0\n" | ||
27 | " mvc .s2 TSCH,%1\n" | ||
28 | " rint\n" | ||
29 | : "=b"(l), "=b"(h)); | ||
30 | return ((cycles_t)h << 32) | l; | ||
31 | } | ||
32 | |||
33 | #endif /* _ASM_C6X_TIMEX_H */ | ||
diff --git a/arch/c6x/include/asm/tlb.h b/arch/c6x/include/asm/tlb.h new file mode 100644 index 000000000000..8709e5e29d2d --- /dev/null +++ b/arch/c6x/include/asm/tlb.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _ASM_C6X_TLB_H | ||
2 | #define _ASM_C6X_TLB_H | ||
3 | |||
4 | #define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) | ||
5 | |||
6 | #include <asm-generic/tlb.h> | ||
7 | |||
8 | #endif /* _ASM_C6X_TLB_H */ | ||
diff --git a/arch/c6x/include/asm/traps.h b/arch/c6x/include/asm/traps.h new file mode 100644 index 000000000000..62124d7b1b5f --- /dev/null +++ b/arch/c6x/include/asm/traps.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef _ASM_C6X_TRAPS_H | ||
12 | #define _ASM_C6X_TRAPS_H | ||
13 | |||
14 | #define EXCEPT_TYPE_NXF 31 /* NMI */ | ||
15 | #define EXCEPT_TYPE_EXC 30 /* external exception */ | ||
16 | #define EXCEPT_TYPE_IXF 1 /* internal exception */ | ||
17 | #define EXCEPT_TYPE_SXF 0 /* software exception */ | ||
18 | |||
19 | #define EXCEPT_CAUSE_LBX (1 << 7) /* loop buffer exception */ | ||
20 | #define EXCEPT_CAUSE_PRX (1 << 6) /* privilege exception */ | ||
21 | #define EXCEPT_CAUSE_RAX (1 << 5) /* resource access exception */ | ||
22 | #define EXCEPT_CAUSE_RCX (1 << 4) /* resource conflict exception */ | ||
23 | #define EXCEPT_CAUSE_OPX (1 << 3) /* opcode exception */ | ||
24 | #define EXCEPT_CAUSE_EPX (1 << 2) /* execute packet exception */ | ||
25 | #define EXCEPT_CAUSE_FPX (1 << 1) /* fetch packet exception */ | ||
26 | #define EXCEPT_CAUSE_IFX (1 << 0) /* instruction fetch exception */ | ||
27 | |||
28 | struct exception_info { | ||
29 | char *kernel_str; | ||
30 | int signo; | ||
31 | int code; | ||
32 | }; | ||
33 | |||
34 | extern int (*c6x_nmi_handler)(struct pt_regs *regs); | ||
35 | |||
36 | #endif /* _ASM_C6X_TRAPS_H */ | ||
diff --git a/arch/c6x/include/asm/uaccess.h b/arch/c6x/include/asm/uaccess.h new file mode 100644 index 000000000000..453dd263bee3 --- /dev/null +++ b/arch/c6x/include/asm/uaccess.h | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter <msalter@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #ifndef _ASM_C6X_UACCESS_H | ||
10 | #define _ASM_C6X_UACCESS_H | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/compiler.h> | ||
14 | #include <linux/string.h> | ||
15 | |||
16 | #ifdef CONFIG_ACCESS_CHECK | ||
17 | #define __access_ok _access_ok | ||
18 | #endif | ||
19 | |||
20 | /* | ||
21 | * __copy_from_user/copy_to_user are based on ones in asm-generic/uaccess.h | ||
22 | * | ||
23 | * C6X supports unaligned 32 and 64 bit loads and stores. | ||
24 | */ | ||
25 | static inline __must_check long __copy_from_user(void *to, | ||
26 | const void __user *from, unsigned long n) | ||
27 | { | ||
28 | u32 tmp32; | ||
29 | u64 tmp64; | ||
30 | |||
31 | if (__builtin_constant_p(n)) { | ||
32 | switch (n) { | ||
33 | case 1: | ||
34 | *(u8 *)to = *(u8 __force *)from; | ||
35 | return 0; | ||
36 | case 4: | ||
37 | asm volatile ("ldnw .d1t1 *%2,%0\n" | ||
38 | "nop 4\n" | ||
39 | "stnw .d1t1 %0,*%1\n" | ||
40 | : "=&a"(tmp32) | ||
41 | : "A"(to), "a"(from) | ||
42 | : "memory"); | ||
43 | return 0; | ||
44 | case 8: | ||
45 | asm volatile ("ldndw .d1t1 *%2,%0\n" | ||
46 | "nop 4\n" | ||
47 | "stndw .d1t1 %0,*%1\n" | ||
48 | : "=&a"(tmp64) | ||
49 | : "a"(to), "a"(from) | ||
50 | : "memory"); | ||
51 | return 0; | ||
52 | default: | ||
53 | break; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | memcpy(to, (const void __force *)from, n); | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static inline __must_check long __copy_to_user(void __user *to, | ||
62 | const void *from, unsigned long n) | ||
63 | { | ||
64 | u32 tmp32; | ||
65 | u64 tmp64; | ||
66 | |||
67 | if (__builtin_constant_p(n)) { | ||
68 | switch (n) { | ||
69 | case 1: | ||
70 | *(u8 __force *)to = *(u8 *)from; | ||
71 | return 0; | ||
72 | case 4: | ||
73 | asm volatile ("ldnw .d1t1 *%2,%0\n" | ||
74 | "nop 4\n" | ||
75 | "stnw .d1t1 %0,*%1\n" | ||
76 | : "=&a"(tmp32) | ||
77 | : "a"(to), "a"(from) | ||
78 | : "memory"); | ||
79 | return 0; | ||
80 | case 8: | ||
81 | asm volatile ("ldndw .d1t1 *%2,%0\n" | ||
82 | "nop 4\n" | ||
83 | "stndw .d1t1 %0,*%1\n" | ||
84 | : "=&a"(tmp64) | ||
85 | : "a"(to), "a"(from) | ||
86 | : "memory"); | ||
87 | return 0; | ||
88 | default: | ||
89 | break; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | memcpy((void __force *)to, from, n); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | #define __copy_to_user __copy_to_user | ||
98 | #define __copy_from_user __copy_from_user | ||
99 | |||
100 | extern int _access_ok(unsigned long addr, unsigned long size); | ||
101 | #ifdef CONFIG_ACCESS_CHECK | ||
102 | #define __access_ok _access_ok | ||
103 | #endif | ||
104 | |||
105 | #include <asm-generic/uaccess.h> | ||
106 | |||
107 | #endif /* _ASM_C6X_UACCESS_H */ | ||
diff --git a/arch/c6x/include/asm/unaligned.h b/arch/c6x/include/asm/unaligned.h new file mode 100644 index 000000000000..b976cb740eaa --- /dev/null +++ b/arch/c6x/include/asm/unaligned.h | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * Rewritten for 2.6.3x: Mark Salter <msalter@redhat.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #ifndef _ASM_C6X_UNALIGNED_H | ||
13 | #define _ASM_C6X_UNALIGNED_H | ||
14 | |||
15 | #include <linux/swab.h> | ||
16 | |||
17 | /* | ||
18 | * The C64x+ can do unaligned word and dword accesses in hardware | ||
19 | * using special load/store instructions. | ||
20 | */ | ||
21 | |||
22 | static inline u16 get_unaligned_le16(const void *p) | ||
23 | { | ||
24 | const u8 *_p = p; | ||
25 | return _p[0] | _p[1] << 8; | ||
26 | } | ||
27 | |||
28 | static inline u16 get_unaligned_be16(const void *p) | ||
29 | { | ||
30 | const u8 *_p = p; | ||
31 | return _p[0] << 8 | _p[1]; | ||
32 | } | ||
33 | |||
34 | static inline void put_unaligned_le16(u16 val, void *p) | ||
35 | { | ||
36 | u8 *_p = p; | ||
37 | _p[0] = val; | ||
38 | _p[1] = val >> 8; | ||
39 | } | ||
40 | |||
41 | static inline void put_unaligned_be16(u16 val, void *p) | ||
42 | { | ||
43 | u8 *_p = p; | ||
44 | _p[0] = val >> 8; | ||
45 | _p[1] = val; | ||
46 | } | ||
47 | |||
48 | static inline u32 get_unaligned32(const void *p) | ||
49 | { | ||
50 | u32 val = (u32) p; | ||
51 | asm (" ldnw .d1t1 *%0,%0\n" | ||
52 | " nop 4\n" | ||
53 | : "+a"(val)); | ||
54 | return val; | ||
55 | } | ||
56 | |||
57 | static inline void put_unaligned32(u32 val, void *p) | ||
58 | { | ||
59 | asm volatile (" stnw .d2t1 %0,*%1\n" | ||
60 | : : "a"(val), "b"(p) : "memory"); | ||
61 | } | ||
62 | |||
63 | static inline u64 get_unaligned64(const void *p) | ||
64 | { | ||
65 | u64 val; | ||
66 | asm volatile (" ldndw .d1t1 *%1,%0\n" | ||
67 | " nop 4\n" | ||
68 | : "=a"(val) : "a"(p)); | ||
69 | return val; | ||
70 | } | ||
71 | |||
72 | static inline void put_unaligned64(u64 val, const void *p) | ||
73 | { | ||
74 | asm volatile (" stndw .d2t1 %0,*%1\n" | ||
75 | : : "a"(val), "b"(p) : "memory"); | ||
76 | } | ||
77 | |||
78 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
79 | |||
80 | #define get_unaligned_le32(p) __swab32(get_unaligned32(p)) | ||
81 | #define get_unaligned_le64(p) __swab64(get_unaligned64(p)) | ||
82 | #define get_unaligned_be32(p) get_unaligned32(p) | ||
83 | #define get_unaligned_be64(p) get_unaligned64(p) | ||
84 | #define put_unaligned_le32(v, p) put_unaligned32(__swab32(v), (p)) | ||
85 | #define put_unaligned_le64(v, p) put_unaligned64(__swab64(v), (p)) | ||
86 | #define put_unaligned_be32(v, p) put_unaligned32((v), (p)) | ||
87 | #define put_unaligned_be64(v, p) put_unaligned64((v), (p)) | ||
88 | #define get_unaligned __get_unaligned_be | ||
89 | #define put_unaligned __put_unaligned_be | ||
90 | |||
91 | #else | ||
92 | |||
93 | #define get_unaligned_le32(p) get_unaligned32(p) | ||
94 | #define get_unaligned_le64(p) get_unaligned64(p) | ||
95 | #define get_unaligned_be32(p) __swab32(get_unaligned32(p)) | ||
96 | #define get_unaligned_be64(p) __swab64(get_unaligned64(p)) | ||
97 | #define put_unaligned_le32(v, p) put_unaligned32((v), (p)) | ||
98 | #define put_unaligned_le64(v, p) put_unaligned64((v), (p)) | ||
99 | #define put_unaligned_be32(v, p) put_unaligned32(__swab32(v), (p)) | ||
100 | #define put_unaligned_be64(v, p) put_unaligned64(__swab64(v), (p)) | ||
101 | #define get_unaligned __get_unaligned_le | ||
102 | #define put_unaligned __put_unaligned_le | ||
103 | |||
104 | #endif | ||
105 | |||
106 | /* | ||
107 | * Cause a link-time error if we try an unaligned access other than | ||
108 | * 1,2,4 or 8 bytes long | ||
109 | */ | ||
110 | extern int __bad_unaligned_access_size(void); | ||
111 | |||
112 | #define __get_unaligned_le(ptr) (typeof(*(ptr)))({ \ | ||
113 | sizeof(*(ptr)) == 1 ? *(ptr) : \ | ||
114 | (sizeof(*(ptr)) == 2 ? get_unaligned_le16((ptr)) : \ | ||
115 | (sizeof(*(ptr)) == 4 ? get_unaligned_le32((ptr)) : \ | ||
116 | (sizeof(*(ptr)) == 8 ? get_unaligned_le64((ptr)) : \ | ||
117 | __bad_unaligned_access_size()))); \ | ||
118 | }) | ||
119 | |||
120 | #define __get_unaligned_be(ptr) (__force typeof(*(ptr)))({ \ | ||
121 | sizeof(*(ptr)) == 1 ? *(ptr) : \ | ||
122 | (sizeof(*(ptr)) == 2 ? get_unaligned_be16((ptr)) : \ | ||
123 | (sizeof(*(ptr)) == 4 ? get_unaligned_be32((ptr)) : \ | ||
124 | (sizeof(*(ptr)) == 8 ? get_unaligned_be64((ptr)) : \ | ||
125 | __bad_unaligned_access_size()))); \ | ||
126 | }) | ||
127 | |||
128 | #define __put_unaligned_le(val, ptr) ({ \ | ||
129 | void *__gu_p = (ptr); \ | ||
130 | switch (sizeof(*(ptr))) { \ | ||
131 | case 1: \ | ||
132 | *(u8 *)__gu_p = (__force u8)(val); \ | ||
133 | break; \ | ||
134 | case 2: \ | ||
135 | put_unaligned_le16((__force u16)(val), __gu_p); \ | ||
136 | break; \ | ||
137 | case 4: \ | ||
138 | put_unaligned_le32((__force u32)(val), __gu_p); \ | ||
139 | break; \ | ||
140 | case 8: \ | ||
141 | put_unaligned_le64((__force u64)(val), __gu_p); \ | ||
142 | break; \ | ||
143 | default: \ | ||
144 | __bad_unaligned_access_size(); \ | ||
145 | break; \ | ||
146 | } \ | ||
147 | (void)0; }) | ||
148 | |||
149 | #define __put_unaligned_be(val, ptr) ({ \ | ||
150 | void *__gu_p = (ptr); \ | ||
151 | switch (sizeof(*(ptr))) { \ | ||
152 | case 1: \ | ||
153 | *(u8 *)__gu_p = (__force u8)(val); \ | ||
154 | break; \ | ||
155 | case 2: \ | ||
156 | put_unaligned_be16((__force u16)(val), __gu_p); \ | ||
157 | break; \ | ||
158 | case 4: \ | ||
159 | put_unaligned_be32((__force u32)(val), __gu_p); \ | ||
160 | break; \ | ||
161 | case 8: \ | ||
162 | put_unaligned_be64((__force u64)(val), __gu_p); \ | ||
163 | break; \ | ||
164 | default: \ | ||
165 | __bad_unaligned_access_size(); \ | ||
166 | break; \ | ||
167 | } \ | ||
168 | (void)0; }) | ||
169 | |||
170 | #endif /* _ASM_C6X_UNALIGNED_H */ | ||
diff --git a/arch/c6x/include/asm/unistd.h b/arch/c6x/include/asm/unistd.h new file mode 100644 index 000000000000..6d54ea4262eb --- /dev/null +++ b/arch/c6x/include/asm/unistd.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * | ||
4 | * Based on arch/tile version. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation, version 2. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
13 | * NON INFRINGEMENT. See the GNU General Public License for | ||
14 | * more details. | ||
15 | */ | ||
16 | #if !defined(_ASM_C6X_UNISTD_H) || defined(__SYSCALL) | ||
17 | #define _ASM_C6X_UNISTD_H | ||
18 | |||
19 | /* Use the standard ABI for syscalls. */ | ||
20 | #include <asm-generic/unistd.h> | ||
21 | |||
22 | /* C6X-specific syscalls. */ | ||
23 | #define __NR_cache_sync (__NR_arch_specific_syscall + 0) | ||
24 | __SYSCALL(__NR_cache_sync, sys_cache_sync) | ||
25 | |||
26 | #endif /* _ASM_C6X_UNISTD_H */ | ||
diff --git a/arch/c6x/kernel/Makefile b/arch/c6x/kernel/Makefile new file mode 100644 index 000000000000..580a515a9443 --- /dev/null +++ b/arch/c6x/kernel/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # Makefile for arch/c6x/kernel/ | ||
3 | # | ||
4 | |||
5 | extra-y := head.o vmlinux.lds | ||
6 | |||
7 | obj-y := process.o traps.o irq.o signal.o ptrace.o | ||
8 | obj-y += setup.o sys_c6x.o time.o devicetree.o | ||
9 | obj-y += switch_to.o entry.o vectors.o c6x_ksyms.o | ||
10 | obj-y += soc.o dma.o | ||
11 | |||
12 | obj-$(CONFIG_MODULES) += module.o | ||
diff --git a/arch/c6x/kernel/asm-offsets.c b/arch/c6x/kernel/asm-offsets.c new file mode 100644 index 000000000000..759ad6d207b6 --- /dev/null +++ b/arch/c6x/kernel/asm-offsets.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * Generate definitions needed by assembly language modules. | ||
3 | * This code generates raw asm output which is post-processed | ||
4 | * to extract and format the required data. | ||
5 | */ | ||
6 | |||
7 | #include <linux/sched.h> | ||
8 | #include <linux/thread_info.h> | ||
9 | #include <asm/procinfo.h> | ||
10 | #include <linux/kbuild.h> | ||
11 | #include <linux/unistd.h> | ||
12 | |||
13 | void foo(void) | ||
14 | { | ||
15 | OFFSET(REGS_A16, pt_regs, a16); | ||
16 | OFFSET(REGS_A17, pt_regs, a17); | ||
17 | OFFSET(REGS_A18, pt_regs, a18); | ||
18 | OFFSET(REGS_A19, pt_regs, a19); | ||
19 | OFFSET(REGS_A20, pt_regs, a20); | ||
20 | OFFSET(REGS_A21, pt_regs, a21); | ||
21 | OFFSET(REGS_A22, pt_regs, a22); | ||
22 | OFFSET(REGS_A23, pt_regs, a23); | ||
23 | OFFSET(REGS_A24, pt_regs, a24); | ||
24 | OFFSET(REGS_A25, pt_regs, a25); | ||
25 | OFFSET(REGS_A26, pt_regs, a26); | ||
26 | OFFSET(REGS_A27, pt_regs, a27); | ||
27 | OFFSET(REGS_A28, pt_regs, a28); | ||
28 | OFFSET(REGS_A29, pt_regs, a29); | ||
29 | OFFSET(REGS_A30, pt_regs, a30); | ||
30 | OFFSET(REGS_A31, pt_regs, a31); | ||
31 | |||
32 | OFFSET(REGS_B16, pt_regs, b16); | ||
33 | OFFSET(REGS_B17, pt_regs, b17); | ||
34 | OFFSET(REGS_B18, pt_regs, b18); | ||
35 | OFFSET(REGS_B19, pt_regs, b19); | ||
36 | OFFSET(REGS_B20, pt_regs, b20); | ||
37 | OFFSET(REGS_B21, pt_regs, b21); | ||
38 | OFFSET(REGS_B22, pt_regs, b22); | ||
39 | OFFSET(REGS_B23, pt_regs, b23); | ||
40 | OFFSET(REGS_B24, pt_regs, b24); | ||
41 | OFFSET(REGS_B25, pt_regs, b25); | ||
42 | OFFSET(REGS_B26, pt_regs, b26); | ||
43 | OFFSET(REGS_B27, pt_regs, b27); | ||
44 | OFFSET(REGS_B28, pt_regs, b28); | ||
45 | OFFSET(REGS_B29, pt_regs, b29); | ||
46 | OFFSET(REGS_B30, pt_regs, b30); | ||
47 | OFFSET(REGS_B31, pt_regs, b31); | ||
48 | |||
49 | OFFSET(REGS_A0, pt_regs, a0); | ||
50 | OFFSET(REGS_A1, pt_regs, a1); | ||
51 | OFFSET(REGS_A2, pt_regs, a2); | ||
52 | OFFSET(REGS_A3, pt_regs, a3); | ||
53 | OFFSET(REGS_A4, pt_regs, a4); | ||
54 | OFFSET(REGS_A5, pt_regs, a5); | ||
55 | OFFSET(REGS_A6, pt_regs, a6); | ||
56 | OFFSET(REGS_A7, pt_regs, a7); | ||
57 | OFFSET(REGS_A8, pt_regs, a8); | ||
58 | OFFSET(REGS_A9, pt_regs, a9); | ||
59 | OFFSET(REGS_A10, pt_regs, a10); | ||
60 | OFFSET(REGS_A11, pt_regs, a11); | ||
61 | OFFSET(REGS_A12, pt_regs, a12); | ||
62 | OFFSET(REGS_A13, pt_regs, a13); | ||
63 | OFFSET(REGS_A14, pt_regs, a14); | ||
64 | OFFSET(REGS_A15, pt_regs, a15); | ||
65 | |||
66 | OFFSET(REGS_B0, pt_regs, b0); | ||
67 | OFFSET(REGS_B1, pt_regs, b1); | ||
68 | OFFSET(REGS_B2, pt_regs, b2); | ||
69 | OFFSET(REGS_B3, pt_regs, b3); | ||
70 | OFFSET(REGS_B4, pt_regs, b4); | ||
71 | OFFSET(REGS_B5, pt_regs, b5); | ||
72 | OFFSET(REGS_B6, pt_regs, b6); | ||
73 | OFFSET(REGS_B7, pt_regs, b7); | ||
74 | OFFSET(REGS_B8, pt_regs, b8); | ||
75 | OFFSET(REGS_B9, pt_regs, b9); | ||
76 | OFFSET(REGS_B10, pt_regs, b10); | ||
77 | OFFSET(REGS_B11, pt_regs, b11); | ||
78 | OFFSET(REGS_B12, pt_regs, b12); | ||
79 | OFFSET(REGS_B13, pt_regs, b13); | ||
80 | OFFSET(REGS_DP, pt_regs, dp); | ||
81 | OFFSET(REGS_SP, pt_regs, sp); | ||
82 | |||
83 | OFFSET(REGS_TSR, pt_regs, tsr); | ||
84 | OFFSET(REGS_ORIG_A4, pt_regs, orig_a4); | ||
85 | |||
86 | DEFINE(REGS__END, sizeof(struct pt_regs)); | ||
87 | BLANK(); | ||
88 | |||
89 | OFFSET(THREAD_PC, thread_struct, pc); | ||
90 | OFFSET(THREAD_B15_14, thread_struct, b15_14); | ||
91 | OFFSET(THREAD_A15_14, thread_struct, a15_14); | ||
92 | OFFSET(THREAD_B13_12, thread_struct, b13_12); | ||
93 | OFFSET(THREAD_A13_12, thread_struct, a13_12); | ||
94 | OFFSET(THREAD_B11_10, thread_struct, b11_10); | ||
95 | OFFSET(THREAD_A11_10, thread_struct, a11_10); | ||
96 | OFFSET(THREAD_RICL_ICL, thread_struct, ricl_icl); | ||
97 | BLANK(); | ||
98 | |||
99 | OFFSET(TASK_STATE, task_struct, state); | ||
100 | BLANK(); | ||
101 | |||
102 | OFFSET(THREAD_INFO_FLAGS, thread_info, flags); | ||
103 | OFFSET(THREAD_INFO_PREEMPT_COUNT, thread_info, preempt_count); | ||
104 | BLANK(); | ||
105 | |||
106 | /* These would be unneccessary if we ran asm files | ||
107 | * through the preprocessor. | ||
108 | */ | ||
109 | DEFINE(KTHREAD_SIZE, THREAD_SIZE); | ||
110 | DEFINE(KTHREAD_SHIFT, THREAD_SHIFT); | ||
111 | DEFINE(KTHREAD_START_SP, THREAD_START_SP); | ||
112 | DEFINE(ENOSYS_, ENOSYS); | ||
113 | DEFINE(NR_SYSCALLS_, __NR_syscalls); | ||
114 | |||
115 | DEFINE(_TIF_SYSCALL_TRACE, (1<<TIF_SYSCALL_TRACE)); | ||
116 | DEFINE(_TIF_NOTIFY_RESUME, (1<<TIF_NOTIFY_RESUME)); | ||
117 | DEFINE(_TIF_SIGPENDING, (1<<TIF_SIGPENDING)); | ||
118 | DEFINE(_TIF_NEED_RESCHED, (1<<TIF_NEED_RESCHED)); | ||
119 | DEFINE(_TIF_POLLING_NRFLAG, (1<<TIF_POLLING_NRFLAG)); | ||
120 | |||
121 | DEFINE(_TIF_ALLWORK_MASK, TIF_ALLWORK_MASK); | ||
122 | DEFINE(_TIF_WORK_MASK, TIF_WORK_MASK); | ||
123 | } | ||
diff --git a/arch/c6x/kernel/c6x_ksyms.c b/arch/c6x/kernel/c6x_ksyms.c new file mode 100644 index 000000000000..0ba3e0bba3e1 --- /dev/null +++ b/arch/c6x/kernel/c6x_ksyms.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | #include <linux/module.h> | ||
13 | #include <asm/checksum.h> | ||
14 | #include <linux/io.h> | ||
15 | |||
16 | /* | ||
17 | * libgcc functions - used internally by the compiler... | ||
18 | */ | ||
19 | extern int __c6xabi_divi(int dividend, int divisor); | ||
20 | EXPORT_SYMBOL(__c6xabi_divi); | ||
21 | |||
22 | extern unsigned __c6xabi_divu(unsigned dividend, unsigned divisor); | ||
23 | EXPORT_SYMBOL(__c6xabi_divu); | ||
24 | |||
25 | extern int __c6xabi_remi(int dividend, int divisor); | ||
26 | EXPORT_SYMBOL(__c6xabi_remi); | ||
27 | |||
28 | extern unsigned __c6xabi_remu(unsigned dividend, unsigned divisor); | ||
29 | EXPORT_SYMBOL(__c6xabi_remu); | ||
30 | |||
31 | extern int __c6xabi_divremi(int dividend, int divisor); | ||
32 | EXPORT_SYMBOL(__c6xabi_divremi); | ||
33 | |||
34 | extern unsigned __c6xabi_divremu(unsigned dividend, unsigned divisor); | ||
35 | EXPORT_SYMBOL(__c6xabi_divremu); | ||
36 | |||
37 | extern unsigned long long __c6xabi_mpyll(unsigned long long src1, | ||
38 | unsigned long long src2); | ||
39 | EXPORT_SYMBOL(__c6xabi_mpyll); | ||
40 | |||
41 | extern long long __c6xabi_negll(long long src); | ||
42 | EXPORT_SYMBOL(__c6xabi_negll); | ||
43 | |||
44 | extern unsigned long long __c6xabi_llshl(unsigned long long src1, uint src2); | ||
45 | EXPORT_SYMBOL(__c6xabi_llshl); | ||
46 | |||
47 | extern long long __c6xabi_llshr(long long src1, uint src2); | ||
48 | EXPORT_SYMBOL(__c6xabi_llshr); | ||
49 | |||
50 | extern unsigned long long __c6xabi_llshru(unsigned long long src1, uint src2); | ||
51 | EXPORT_SYMBOL(__c6xabi_llshru); | ||
52 | |||
53 | extern void __c6xabi_strasgi(int *dst, const int *src, unsigned cnt); | ||
54 | EXPORT_SYMBOL(__c6xabi_strasgi); | ||
55 | |||
56 | extern void __c6xabi_push_rts(void); | ||
57 | EXPORT_SYMBOL(__c6xabi_push_rts); | ||
58 | |||
59 | extern void __c6xabi_pop_rts(void); | ||
60 | EXPORT_SYMBOL(__c6xabi_pop_rts); | ||
61 | |||
62 | extern void __c6xabi_strasgi_64plus(int *dst, const int *src, unsigned cnt); | ||
63 | EXPORT_SYMBOL(__c6xabi_strasgi_64plus); | ||
64 | |||
65 | /* lib functions */ | ||
66 | EXPORT_SYMBOL(memcpy); | ||
diff --git a/arch/c6x/kernel/devicetree.c b/arch/c6x/kernel/devicetree.c new file mode 100644 index 000000000000..bdb56f09d0ac --- /dev/null +++ b/arch/c6x/kernel/devicetree.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Architecture specific OF callbacks. | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
5 | * Author: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/of_fdt.h> | ||
15 | #include <linux/initrd.h> | ||
16 | #include <linux/memblock.h> | ||
17 | |||
18 | void __init early_init_devtree(void *params) | ||
19 | { | ||
20 | /* Setup flat device-tree pointer */ | ||
21 | initial_boot_params = params; | ||
22 | |||
23 | /* Retrieve various informations from the /chosen node of the | ||
24 | * device-tree, including the platform type, initrd location and | ||
25 | * size and more ... | ||
26 | */ | ||
27 | of_scan_flat_dt(early_init_dt_scan_chosen, c6x_command_line); | ||
28 | |||
29 | /* Scan memory nodes and rebuild MEMBLOCKs */ | ||
30 | of_scan_flat_dt(early_init_dt_scan_root, NULL); | ||
31 | of_scan_flat_dt(early_init_dt_scan_memory, NULL); | ||
32 | } | ||
33 | |||
34 | |||
35 | #ifdef CONFIG_BLK_DEV_INITRD | ||
36 | void __init early_init_dt_setup_initrd_arch(unsigned long start, | ||
37 | unsigned long end) | ||
38 | { | ||
39 | initrd_start = (unsigned long)__va(start); | ||
40 | initrd_end = (unsigned long)__va(end); | ||
41 | initrd_below_start_ok = 1; | ||
42 | } | ||
43 | #endif | ||
44 | |||
45 | void __init early_init_dt_add_memory_arch(u64 base, u64 size) | ||
46 | { | ||
47 | c6x_add_memory(base, size); | ||
48 | } | ||
49 | |||
50 | void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) | ||
51 | { | ||
52 | return __va(memblock_alloc(size, align)); | ||
53 | } | ||
diff --git a/arch/c6x/kernel/dma.c b/arch/c6x/kernel/dma.c new file mode 100644 index 000000000000..ab7b12de144d --- /dev/null +++ b/arch/c6x/kernel/dma.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter <msalter@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/dma-mapping.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/mm_types.h> | ||
13 | #include <linux/scatterlist.h> | ||
14 | |||
15 | #include <asm/cacheflush.h> | ||
16 | |||
17 | static void c6x_dma_sync(dma_addr_t handle, size_t size, | ||
18 | enum dma_data_direction dir) | ||
19 | { | ||
20 | unsigned long paddr = handle; | ||
21 | |||
22 | BUG_ON(!valid_dma_direction(dir)); | ||
23 | |||
24 | switch (dir) { | ||
25 | case DMA_FROM_DEVICE: | ||
26 | L2_cache_block_invalidate(paddr, paddr + size); | ||
27 | break; | ||
28 | case DMA_TO_DEVICE: | ||
29 | L2_cache_block_writeback(paddr, paddr + size); | ||
30 | break; | ||
31 | case DMA_BIDIRECTIONAL: | ||
32 | L2_cache_block_writeback_invalidate(paddr, paddr + size); | ||
33 | break; | ||
34 | default: | ||
35 | break; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, | ||
40 | enum dma_data_direction dir) | ||
41 | { | ||
42 | dma_addr_t addr = virt_to_phys(ptr); | ||
43 | |||
44 | c6x_dma_sync(addr, size, dir); | ||
45 | |||
46 | debug_dma_map_page(dev, virt_to_page(ptr), | ||
47 | (unsigned long)ptr & ~PAGE_MASK, size, | ||
48 | dir, addr, true); | ||
49 | return addr; | ||
50 | } | ||
51 | EXPORT_SYMBOL(dma_map_single); | ||
52 | |||
53 | |||
54 | void dma_unmap_single(struct device *dev, dma_addr_t handle, | ||
55 | size_t size, enum dma_data_direction dir) | ||
56 | { | ||
57 | c6x_dma_sync(handle, size, dir); | ||
58 | |||
59 | debug_dma_unmap_page(dev, handle, size, dir, true); | ||
60 | } | ||
61 | EXPORT_SYMBOL(dma_unmap_single); | ||
62 | |||
63 | |||
64 | int dma_map_sg(struct device *dev, struct scatterlist *sglist, | ||
65 | int nents, enum dma_data_direction dir) | ||
66 | { | ||
67 | struct scatterlist *sg; | ||
68 | int i; | ||
69 | |||
70 | for_each_sg(sglist, sg, nents, i) | ||
71 | sg->dma_address = dma_map_single(dev, sg_virt(sg), sg->length, | ||
72 | dir); | ||
73 | |||
74 | debug_dma_map_sg(dev, sglist, nents, nents, dir); | ||
75 | |||
76 | return nents; | ||
77 | } | ||
78 | EXPORT_SYMBOL(dma_map_sg); | ||
79 | |||
80 | |||
81 | void dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | ||
82 | int nents, enum dma_data_direction dir) | ||
83 | { | ||
84 | struct scatterlist *sg; | ||
85 | int i; | ||
86 | |||
87 | for_each_sg(sglist, sg, nents, i) | ||
88 | dma_unmap_single(dev, sg_dma_address(sg), sg->length, dir); | ||
89 | |||
90 | debug_dma_unmap_sg(dev, sglist, nents, dir); | ||
91 | } | ||
92 | EXPORT_SYMBOL(dma_unmap_sg); | ||
93 | |||
94 | void dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, | ||
95 | size_t size, enum dma_data_direction dir) | ||
96 | { | ||
97 | c6x_dma_sync(handle, size, dir); | ||
98 | |||
99 | debug_dma_sync_single_for_cpu(dev, handle, size, dir); | ||
100 | } | ||
101 | EXPORT_SYMBOL(dma_sync_single_for_cpu); | ||
102 | |||
103 | |||
104 | void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, | ||
105 | size_t size, enum dma_data_direction dir) | ||
106 | { | ||
107 | c6x_dma_sync(handle, size, dir); | ||
108 | |||
109 | debug_dma_sync_single_for_device(dev, handle, size, dir); | ||
110 | } | ||
111 | EXPORT_SYMBOL(dma_sync_single_for_device); | ||
112 | |||
113 | |||
114 | void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sglist, | ||
115 | int nents, enum dma_data_direction dir) | ||
116 | { | ||
117 | struct scatterlist *sg; | ||
118 | int i; | ||
119 | |||
120 | for_each_sg(sglist, sg, nents, i) | ||
121 | dma_sync_single_for_cpu(dev, sg_dma_address(sg), | ||
122 | sg->length, dir); | ||
123 | |||
124 | debug_dma_sync_sg_for_cpu(dev, sglist, nents, dir); | ||
125 | } | ||
126 | EXPORT_SYMBOL(dma_sync_sg_for_cpu); | ||
127 | |||
128 | |||
129 | void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sglist, | ||
130 | int nents, enum dma_data_direction dir) | ||
131 | { | ||
132 | struct scatterlist *sg; | ||
133 | int i; | ||
134 | |||
135 | for_each_sg(sglist, sg, nents, i) | ||
136 | dma_sync_single_for_device(dev, sg_dma_address(sg), | ||
137 | sg->length, dir); | ||
138 | |||
139 | debug_dma_sync_sg_for_device(dev, sglist, nents, dir); | ||
140 | } | ||
141 | EXPORT_SYMBOL(dma_sync_sg_for_device); | ||
142 | |||
143 | |||
144 | /* Number of entries preallocated for DMA-API debugging */ | ||
145 | #define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16) | ||
146 | |||
147 | static int __init dma_init(void) | ||
148 | { | ||
149 | dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | fs_initcall(dma_init); | ||
diff --git a/arch/c6x/kernel/entry.S b/arch/c6x/kernel/entry.S new file mode 100644 index 000000000000..3e977ccda827 --- /dev/null +++ b/arch/c6x/kernel/entry.S | |||
@@ -0,0 +1,803 @@ | |||
1 | ; | ||
2 | ; Port on Texas Instruments TMS320C6x architecture | ||
3 | ; | ||
4 | ; Copyright (C) 2004-2011 Texas Instruments Incorporated | ||
5 | ; Author: Aurelien Jacquiot (aurelien.jacquiot@virtuallogix.com) | ||
6 | ; Updated for 2.6.34: Mark Salter <msalter@redhat.com> | ||
7 | ; | ||
8 | ; This program is free software; you can redistribute it and/or modify | ||
9 | ; it under the terms of the GNU General Public License version 2 as | ||
10 | ; published by the Free Software Foundation. | ||
11 | ; | ||
12 | |||
13 | #include <linux/sys.h> | ||
14 | #include <linux/linkage.h> | ||
15 | #include <asm/thread_info.h> | ||
16 | #include <asm/asm-offsets.h> | ||
17 | #include <asm/unistd.h> | ||
18 | #include <asm/errno.h> | ||
19 | |||
20 | ; Registers naming | ||
21 | #define DP B14 | ||
22 | #define SP B15 | ||
23 | |||
24 | #ifndef CONFIG_PREEMPT | ||
25 | #define resume_kernel restore_all | ||
26 | #endif | ||
27 | |||
28 | .altmacro | ||
29 | |||
30 | .macro MASK_INT reg | ||
31 | MVC .S2 CSR,reg | ||
32 | CLR .S2 reg,0,0,reg | ||
33 | MVC .S2 reg,CSR | ||
34 | .endm | ||
35 | |||
36 | .macro UNMASK_INT reg | ||
37 | MVC .S2 CSR,reg | ||
38 | SET .S2 reg,0,0,reg | ||
39 | MVC .S2 reg,CSR | ||
40 | .endm | ||
41 | |||
42 | .macro GET_THREAD_INFO reg | ||
43 | SHR .S1X SP,THREAD_SHIFT,reg | ||
44 | SHL .S1 reg,THREAD_SHIFT,reg | ||
45 | .endm | ||
46 | |||
47 | ;; | ||
48 | ;; This defines the normal kernel pt_regs layout. | ||
49 | ;; | ||
50 | .macro SAVE_ALL __rp __tsr | ||
51 | STW .D2T2 B0,*SP--[2] ; save original B0 | ||
52 | MVKL .S2 current_ksp,B0 | ||
53 | MVKH .S2 current_ksp,B0 | ||
54 | LDW .D2T2 *B0,B1 ; KSP | ||
55 | |||
56 | NOP 3 | ||
57 | STW .D2T2 B1,*+SP[1] ; save original B1 | ||
58 | XOR .D2 SP,B1,B0 ; (SP ^ KSP) | ||
59 | LDW .D2T2 *+SP[1],B1 ; restore B0/B1 | ||
60 | LDW .D2T2 *++SP[2],B0 | ||
61 | SHR .S2 B0,THREAD_SHIFT,B0 ; 0 if already using kstack | ||
62 | [B0] STDW .D2T2 SP:DP,*--B1[1] ; user: save user sp/dp kstack | ||
63 | [B0] MV .S2 B1,SP ; and switch to kstack | ||
64 | ||[!B0] STDW .D2T2 SP:DP,*--SP[1] ; kernel: save on current stack | ||
65 | |||
66 | SUBAW .D2 SP,2,SP | ||
67 | |||
68 | ADD .D1X SP,-8,A15 | ||
69 | || STDW .D2T1 A15:A14,*SP--[16] ; save A15:A14 | ||
70 | |||
71 | STDW .D2T2 B13:B12,*SP--[1] | ||
72 | || STDW .D1T1 A13:A12,*A15--[1] | ||
73 | || MVC .S2 __rp,B13 | ||
74 | |||
75 | STDW .D2T2 B11:B10,*SP--[1] | ||
76 | || STDW .D1T1 A11:A10,*A15--[1] | ||
77 | || MVC .S2 CSR,B12 | ||
78 | |||
79 | STDW .D2T2 B9:B8,*SP--[1] | ||
80 | || STDW .D1T1 A9:A8,*A15--[1] | ||
81 | || MVC .S2 RILC,B11 | ||
82 | STDW .D2T2 B7:B6,*SP--[1] | ||
83 | || STDW .D1T1 A7:A6,*A15--[1] | ||
84 | || MVC .S2 ILC,B10 | ||
85 | |||
86 | STDW .D2T2 B5:B4,*SP--[1] | ||
87 | || STDW .D1T1 A5:A4,*A15--[1] | ||
88 | |||
89 | STDW .D2T2 B3:B2,*SP--[1] | ||
90 | || STDW .D1T1 A3:A2,*A15--[1] | ||
91 | || MVC .S2 __tsr,B5 | ||
92 | |||
93 | STDW .D2T2 B1:B0,*SP--[1] | ||
94 | || STDW .D1T1 A1:A0,*A15--[1] | ||
95 | || MV .S1X B5,A5 | ||
96 | |||
97 | STDW .D2T2 B31:B30,*SP--[1] | ||
98 | || STDW .D1T1 A31:A30,*A15--[1] | ||
99 | STDW .D2T2 B29:B28,*SP--[1] | ||
100 | || STDW .D1T1 A29:A28,*A15--[1] | ||
101 | STDW .D2T2 B27:B26,*SP--[1] | ||
102 | || STDW .D1T1 A27:A26,*A15--[1] | ||
103 | STDW .D2T2 B25:B24,*SP--[1] | ||
104 | || STDW .D1T1 A25:A24,*A15--[1] | ||
105 | STDW .D2T2 B23:B22,*SP--[1] | ||
106 | || STDW .D1T1 A23:A22,*A15--[1] | ||
107 | STDW .D2T2 B21:B20,*SP--[1] | ||
108 | || STDW .D1T1 A21:A20,*A15--[1] | ||
109 | STDW .D2T2 B19:B18,*SP--[1] | ||
110 | || STDW .D1T1 A19:A18,*A15--[1] | ||
111 | STDW .D2T2 B17:B16,*SP--[1] | ||
112 | || STDW .D1T1 A17:A16,*A15--[1] | ||
113 | |||
114 | STDW .D2T2 B13:B12,*SP--[1] ; save PC and CSR | ||
115 | |||
116 | STDW .D2T2 B11:B10,*SP--[1] ; save RILC and ILC | ||
117 | STDW .D2T1 A5:A4,*SP--[1] ; save TSR and orig A4 | ||
118 | |||
119 | ;; We left an unused word on the stack just above pt_regs. | ||
120 | ;; It is used to save whether or not this frame is due to | ||
121 | ;; a syscall. It is cleared here, but the syscall handler | ||
122 | ;; sets it to a non-zero value. | ||
123 | MVK .L2 0,B1 | ||
124 | STW .D2T2 B1,*+SP(REGS__END+8) ; clear syscall flag | ||
125 | .endm | ||
126 | |||
127 | .macro RESTORE_ALL __rp __tsr | ||
128 | LDDW .D2T2 *++SP[1],B9:B8 ; get TSR (B9) | ||
129 | LDDW .D2T2 *++SP[1],B11:B10 ; get RILC (B11) and ILC (B10) | ||
130 | LDDW .D2T2 *++SP[1],B13:B12 ; get PC (B13) and CSR (B12) | ||
131 | |||
132 | ADDAW .D1X SP,30,A15 | ||
133 | |||
134 | LDDW .D1T1 *++A15[1],A17:A16 | ||
135 | || LDDW .D2T2 *++SP[1],B17:B16 | ||
136 | LDDW .D1T1 *++A15[1],A19:A18 | ||
137 | || LDDW .D2T2 *++SP[1],B19:B18 | ||
138 | LDDW .D1T1 *++A15[1],A21:A20 | ||
139 | || LDDW .D2T2 *++SP[1],B21:B20 | ||
140 | LDDW .D1T1 *++A15[1],A23:A22 | ||
141 | || LDDW .D2T2 *++SP[1],B23:B22 | ||
142 | LDDW .D1T1 *++A15[1],A25:A24 | ||
143 | || LDDW .D2T2 *++SP[1],B25:B24 | ||
144 | LDDW .D1T1 *++A15[1],A27:A26 | ||
145 | || LDDW .D2T2 *++SP[1],B27:B26 | ||
146 | LDDW .D1T1 *++A15[1],A29:A28 | ||
147 | || LDDW .D2T2 *++SP[1],B29:B28 | ||
148 | LDDW .D1T1 *++A15[1],A31:A30 | ||
149 | || LDDW .D2T2 *++SP[1],B31:B30 | ||
150 | |||
151 | LDDW .D1T1 *++A15[1],A1:A0 | ||
152 | || LDDW .D2T2 *++SP[1],B1:B0 | ||
153 | |||
154 | LDDW .D1T1 *++A15[1],A3:A2 | ||
155 | || LDDW .D2T2 *++SP[1],B3:B2 | ||
156 | || MVC .S2 B9,__tsr | ||
157 | LDDW .D1T1 *++A15[1],A5:A4 | ||
158 | || LDDW .D2T2 *++SP[1],B5:B4 | ||
159 | || MVC .S2 B11,RILC | ||
160 | LDDW .D1T1 *++A15[1],A7:A6 | ||
161 | || LDDW .D2T2 *++SP[1],B7:B6 | ||
162 | || MVC .S2 B10,ILC | ||
163 | |||
164 | LDDW .D1T1 *++A15[1],A9:A8 | ||
165 | || LDDW .D2T2 *++SP[1],B9:B8 | ||
166 | || MVC .S2 B13,__rp | ||
167 | |||
168 | LDDW .D1T1 *++A15[1],A11:A10 | ||
169 | || LDDW .D2T2 *++SP[1],B11:B10 | ||
170 | || MVC .S2 B12,CSR | ||
171 | |||
172 | LDDW .D1T1 *++A15[1],A13:A12 | ||
173 | || LDDW .D2T2 *++SP[1],B13:B12 | ||
174 | |||
175 | MV .D2X A15,SP | ||
176 | || MVKL .S1 current_ksp,A15 | ||
177 | MVKH .S1 current_ksp,A15 | ||
178 | || ADDAW .D1X SP,6,A14 | ||
179 | STW .D1T1 A14,*A15 ; save kernel stack pointer | ||
180 | |||
181 | LDDW .D2T1 *++SP[1],A15:A14 | ||
182 | |||
183 | B .S2 __rp ; return from interruption | ||
184 | LDDW .D2T2 *+SP[1],SP:DP | ||
185 | NOP 4 | ||
186 | .endm | ||
187 | |||
188 | .section .text | ||
189 | |||
190 | ;; | ||
191 | ;; Jump to schedule() then return to ret_from_exception | ||
192 | ;; | ||
193 | _reschedule: | ||
194 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
195 | MVKL .S1 schedule,A0 | ||
196 | MVKH .S1 schedule,A0 | ||
197 | B .S2X A0 | ||
198 | #else | ||
199 | B .S1 schedule | ||
200 | #endif | ||
201 | ADDKPC .S2 ret_from_exception,B3,4 | ||
202 | |||
203 | ;; | ||
204 | ;; Called before syscall handler when process is being debugged | ||
205 | ;; | ||
206 | tracesys_on: | ||
207 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
208 | MVKL .S1 syscall_trace_entry,A0 | ||
209 | MVKH .S1 syscall_trace_entry,A0 | ||
210 | B .S2X A0 | ||
211 | #else | ||
212 | B .S1 syscall_trace_entry | ||
213 | #endif | ||
214 | ADDKPC .S2 ret_from_syscall_trace,B3,3 | ||
215 | ADD .S1X 8,SP,A4 | ||
216 | |||
217 | ret_from_syscall_trace: | ||
218 | ;; tracing returns (possibly new) syscall number | ||
219 | MV .D2X A4,B0 | ||
220 | || MVK .S2 __NR_syscalls,B1 | ||
221 | CMPLTU .L2 B0,B1,B1 | ||
222 | |||
223 | [!B1] BNOP .S2 ret_from_syscall_function,5 | ||
224 | || MVK .S1 -ENOSYS,A4 | ||
225 | |||
226 | ;; reload syscall args from (possibly modified) stack frame | ||
227 | ;; and get syscall handler addr from sys_call_table: | ||
228 | LDW .D2T2 *+SP(REGS_B4+8),B4 | ||
229 | || MVKL .S2 sys_call_table,B1 | ||
230 | LDW .D2T1 *+SP(REGS_A6+8),A6 | ||
231 | || MVKH .S2 sys_call_table,B1 | ||
232 | LDW .D2T2 *+B1[B0],B0 | ||
233 | || MVKL .S2 ret_from_syscall_function,B3 | ||
234 | LDW .D2T2 *+SP(REGS_B6+8),B6 | ||
235 | || MVKH .S2 ret_from_syscall_function,B3 | ||
236 | LDW .D2T1 *+SP(REGS_A8+8),A8 | ||
237 | LDW .D2T2 *+SP(REGS_B8+8),B8 | ||
238 | NOP | ||
239 | ; B0 = sys_call_table[__NR_*] | ||
240 | BNOP .S2 B0,5 ; branch to syscall handler | ||
241 | || LDW .D2T1 *+SP(REGS_ORIG_A4+8),A4 | ||
242 | |||
243 | syscall_exit_work: | ||
244 | AND .D1 _TIF_SYSCALL_TRACE,A2,A0 | ||
245 | [!A0] BNOP .S1 work_pending,5 | ||
246 | [A0] B .S2 syscall_trace_exit | ||
247 | ADDKPC .S2 resume_userspace,B3,1 | ||
248 | MVC .S2 CSR,B1 | ||
249 | SET .S2 B1,0,0,B1 | ||
250 | MVC .S2 B1,CSR ; enable ints | ||
251 | |||
252 | work_pending: | ||
253 | AND .D1 _TIF_NEED_RESCHED,A2,A0 | ||
254 | [!A0] BNOP .S1 work_notifysig,5 | ||
255 | |||
256 | work_resched: | ||
257 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
258 | MVKL .S1 schedule,A1 | ||
259 | MVKH .S1 schedule,A1 | ||
260 | B .S2X A1 | ||
261 | #else | ||
262 | B .S2 schedule | ||
263 | #endif | ||
264 | ADDKPC .S2 work_rescheduled,B3,4 | ||
265 | work_rescheduled: | ||
266 | ;; make sure we don't miss an interrupt setting need_resched or | ||
267 | ;; sigpending between sampling and the rti | ||
268 | MASK_INT B2 | ||
269 | GET_THREAD_INFO A12 | ||
270 | LDW .D1T1 *+A12(THREAD_INFO_FLAGS),A2 | ||
271 | MVK .S1 _TIF_WORK_MASK,A1 | ||
272 | MVK .S1 _TIF_NEED_RESCHED,A3 | ||
273 | NOP 2 | ||
274 | AND .D1 A1,A2,A0 | ||
275 | || AND .S1 A3,A2,A1 | ||
276 | [!A0] BNOP .S1 restore_all,5 | ||
277 | [A1] BNOP .S1 work_resched,5 | ||
278 | |||
279 | work_notifysig: | ||
280 | B .S2 do_notify_resume | ||
281 | LDW .D2T1 *+SP(REGS__END+8),A6 ; syscall flag | ||
282 | ADDKPC .S2 resume_userspace,B3,1 | ||
283 | ADD .S1X 8,SP,A4 ; pt_regs pointer is first arg | ||
284 | MV .D2X A2,B4 ; thread_info flags is second arg | ||
285 | |||
286 | ;; | ||
287 | ;; On C64x+, the return way from exception and interrupt | ||
288 | ;; is a little bit different | ||
289 | ;; | ||
290 | ENTRY(ret_from_exception) | ||
291 | #ifdef CONFIG_PREEMPT | ||
292 | MASK_INT B2 | ||
293 | #endif | ||
294 | |||
295 | ENTRY(ret_from_interrupt) | ||
296 | ;; | ||
297 | ;; Check if we are comming from user mode. | ||
298 | ;; | ||
299 | LDW .D2T2 *+SP(REGS_TSR+8),B0 | ||
300 | MVK .S2 0x40,B1 | ||
301 | NOP 3 | ||
302 | AND .D2 B0,B1,B0 | ||
303 | [!B0] BNOP .S2 resume_kernel,5 | ||
304 | |||
305 | resume_userspace: | ||
306 | ;; make sure we don't miss an interrupt setting need_resched or | ||
307 | ;; sigpending between sampling and the rti | ||
308 | MASK_INT B2 | ||
309 | GET_THREAD_INFO A12 | ||
310 | LDW .D1T1 *+A12(THREAD_INFO_FLAGS),A2 | ||
311 | MVK .S1 _TIF_WORK_MASK,A1 | ||
312 | MVK .S1 _TIF_NEED_RESCHED,A3 | ||
313 | NOP 2 | ||
314 | AND .D1 A1,A2,A0 | ||
315 | [A0] BNOP .S1 work_pending,5 | ||
316 | BNOP .S1 restore_all,5 | ||
317 | |||
318 | ;; | ||
319 | ;; System call handling | ||
320 | ;; B0 = syscall number (in sys_call_table) | ||
321 | ;; A4,B4,A6,B6,A8,B8 = arguments of the syscall function | ||
322 | ;; A4 is the return value register | ||
323 | ;; | ||
324 | system_call_saved: | ||
325 | MVK .L2 1,B2 | ||
326 | STW .D2T2 B2,*+SP(REGS__END+8) ; set syscall flag | ||
327 | MVC .S2 B2,ECR ; ack the software exception | ||
328 | |||
329 | UNMASK_INT B2 ; re-enable global IT | ||
330 | |||
331 | system_call_saved_noack: | ||
332 | ;; Check system call number | ||
333 | MVK .S2 __NR_syscalls,B1 | ||
334 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
335 | || MVKL .S1 sys_ni_syscall,A0 | ||
336 | #endif | ||
337 | CMPLTU .L2 B0,B1,B1 | ||
338 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
339 | || MVKH .S1 sys_ni_syscall,A0 | ||
340 | #endif | ||
341 | |||
342 | ;; Check for ptrace | ||
343 | GET_THREAD_INFO A12 | ||
344 | |||
345 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
346 | [!B1] B .S2X A0 | ||
347 | #else | ||
348 | [!B1] B .S2 sys_ni_syscall | ||
349 | #endif | ||
350 | [!B1] ADDKPC .S2 ret_from_syscall_function,B3,4 | ||
351 | |||
352 | ;; Get syscall handler addr from sys_call_table | ||
353 | ;; call tracesys_on or call syscall handler | ||
354 | LDW .D1T1 *+A12(THREAD_INFO_FLAGS),A2 | ||
355 | || MVKL .S2 sys_call_table,B1 | ||
356 | MVKH .S2 sys_call_table,B1 | ||
357 | LDW .D2T2 *+B1[B0],B0 | ||
358 | NOP 2 | ||
359 | ; A2 = thread_info flags | ||
360 | AND .D1 _TIF_SYSCALL_TRACE,A2,A2 | ||
361 | [A2] BNOP .S1 tracesys_on,5 | ||
362 | ;; B0 = _sys_call_table[__NR_*] | ||
363 | B .S2 B0 | ||
364 | ADDKPC .S2 ret_from_syscall_function,B3,4 | ||
365 | |||
366 | ret_from_syscall_function: | ||
367 | STW .D2T1 A4,*+SP(REGS_A4+8) ; save return value in A4 | ||
368 | ; original A4 is in orig_A4 | ||
369 | syscall_exit: | ||
370 | ;; make sure we don't miss an interrupt setting need_resched or | ||
371 | ;; sigpending between sampling and the rti | ||
372 | MASK_INT B2 | ||
373 | LDW .D1T1 *+A12(THREAD_INFO_FLAGS),A2 | ||
374 | MVK .S1 _TIF_ALLWORK_MASK,A1 | ||
375 | NOP 3 | ||
376 | AND .D1 A1,A2,A2 ; check for work to do | ||
377 | [A2] BNOP .S1 syscall_exit_work,5 | ||
378 | |||
379 | restore_all: | ||
380 | RESTORE_ALL NRP,NTSR | ||
381 | |||
382 | ;; | ||
383 | ;; After a fork we jump here directly from resume, | ||
384 | ;; so that A4 contains the previous task structure. | ||
385 | ;; | ||
386 | ENTRY(ret_from_fork) | ||
387 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
388 | MVKL .S1 schedule_tail,A0 | ||
389 | MVKH .S1 schedule_tail,A0 | ||
390 | B .S2X A0 | ||
391 | #else | ||
392 | B .S2 schedule_tail | ||
393 | #endif | ||
394 | ADDKPC .S2 ret_from_fork_2,B3,4 | ||
395 | ret_from_fork_2: | ||
396 | ;; return 0 in A4 for child process | ||
397 | GET_THREAD_INFO A12 | ||
398 | BNOP .S2 syscall_exit,3 | ||
399 | MVK .L2 0,B0 | ||
400 | STW .D2T2 B0,*+SP(REGS_A4+8) | ||
401 | ENDPROC(ret_from_fork) | ||
402 | |||
403 | ;; | ||
404 | ;; These are the interrupt handlers, responsible for calling __do_IRQ() | ||
405 | ;; int6 is used for syscalls (see _system_call entry) | ||
406 | ;; | ||
407 | .macro SAVE_ALL_INT | ||
408 | SAVE_ALL IRP,ITSR | ||
409 | .endm | ||
410 | |||
411 | .macro CALL_INT int | ||
412 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
413 | MVKL .S1 c6x_do_IRQ,A0 | ||
414 | MVKH .S1 c6x_do_IRQ,A0 | ||
415 | BNOP .S2X A0,1 | ||
416 | MVK .S1 int,A4 | ||
417 | ADDAW .D2 SP,2,B4 | ||
418 | MVKL .S2 ret_from_interrupt,B3 | ||
419 | MVKH .S2 ret_from_interrupt,B3 | ||
420 | #else | ||
421 | CALLP .S2 c6x_do_IRQ,B3 | ||
422 | || MVK .S1 int,A4 | ||
423 | || ADDAW .D2 SP,2,B4 | ||
424 | B .S1 ret_from_interrupt | ||
425 | NOP 5 | ||
426 | #endif | ||
427 | .endm | ||
428 | |||
429 | ENTRY(_int4_handler) | ||
430 | SAVE_ALL_INT | ||
431 | CALL_INT 4 | ||
432 | ENDPROC(_int4_handler) | ||
433 | |||
434 | ENTRY(_int5_handler) | ||
435 | SAVE_ALL_INT | ||
436 | CALL_INT 5 | ||
437 | ENDPROC(_int5_handler) | ||
438 | |||
439 | ENTRY(_int6_handler) | ||
440 | SAVE_ALL_INT | ||
441 | CALL_INT 6 | ||
442 | ENDPROC(_int6_handler) | ||
443 | |||
444 | ENTRY(_int7_handler) | ||
445 | SAVE_ALL_INT | ||
446 | CALL_INT 7 | ||
447 | ENDPROC(_int7_handler) | ||
448 | |||
449 | ENTRY(_int8_handler) | ||
450 | SAVE_ALL_INT | ||
451 | CALL_INT 8 | ||
452 | ENDPROC(_int8_handler) | ||
453 | |||
454 | ENTRY(_int9_handler) | ||
455 | SAVE_ALL_INT | ||
456 | CALL_INT 9 | ||
457 | ENDPROC(_int9_handler) | ||
458 | |||
459 | ENTRY(_int10_handler) | ||
460 | SAVE_ALL_INT | ||
461 | CALL_INT 10 | ||
462 | ENDPROC(_int10_handler) | ||
463 | |||
464 | ENTRY(_int11_handler) | ||
465 | SAVE_ALL_INT | ||
466 | CALL_INT 11 | ||
467 | ENDPROC(_int11_handler) | ||
468 | |||
469 | ENTRY(_int12_handler) | ||
470 | SAVE_ALL_INT | ||
471 | CALL_INT 12 | ||
472 | ENDPROC(_int12_handler) | ||
473 | |||
474 | ENTRY(_int13_handler) | ||
475 | SAVE_ALL_INT | ||
476 | CALL_INT 13 | ||
477 | ENDPROC(_int13_handler) | ||
478 | |||
479 | ENTRY(_int14_handler) | ||
480 | SAVE_ALL_INT | ||
481 | CALL_INT 14 | ||
482 | ENDPROC(_int14_handler) | ||
483 | |||
484 | ENTRY(_int15_handler) | ||
485 | SAVE_ALL_INT | ||
486 | CALL_INT 15 | ||
487 | ENDPROC(_int15_handler) | ||
488 | |||
489 | ;; | ||
490 | ;; Handler for uninitialized and spurious interrupts | ||
491 | ;; | ||
492 | ENTRY(_bad_interrupt) | ||
493 | B .S2 IRP | ||
494 | NOP 5 | ||
495 | ENDPROC(_bad_interrupt) | ||
496 | |||
497 | ;; | ||
498 | ;; Entry for NMI/exceptions/syscall | ||
499 | ;; | ||
500 | ENTRY(_nmi_handler) | ||
501 | SAVE_ALL NRP,NTSR | ||
502 | |||
503 | MVC .S2 EFR,B2 | ||
504 | CMPEQ .L2 1,B2,B2 | ||
505 | || MVC .S2 TSR,B1 | ||
506 | CLR .S2 B1,10,10,B1 | ||
507 | MVC .S2 B1,TSR | ||
508 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
509 | [!B2] MVKL .S1 process_exception,A0 | ||
510 | [!B2] MVKH .S1 process_exception,A0 | ||
511 | [!B2] B .S2X A0 | ||
512 | #else | ||
513 | [!B2] B .S2 process_exception | ||
514 | #endif | ||
515 | [B2] B .S2 system_call_saved | ||
516 | [!B2] ADDAW .D2 SP,2,B1 | ||
517 | [!B2] MV .D1X B1,A4 | ||
518 | ADDKPC .S2 ret_from_trap,B3,2 | ||
519 | |||
520 | ret_from_trap: | ||
521 | MV .D2X A4,B0 | ||
522 | [!B0] BNOP .S2 ret_from_exception,5 | ||
523 | |||
524 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
525 | MVKL .S2 system_call_saved_noack,B3 | ||
526 | MVKH .S2 system_call_saved_noack,B3 | ||
527 | #endif | ||
528 | LDW .D2T2 *+SP(REGS_B0+8),B0 | ||
529 | LDW .D2T1 *+SP(REGS_A4+8),A4 | ||
530 | LDW .D2T2 *+SP(REGS_B4+8),B4 | ||
531 | LDW .D2T1 *+SP(REGS_A6+8),A6 | ||
532 | LDW .D2T2 *+SP(REGS_B6+8),B6 | ||
533 | LDW .D2T1 *+SP(REGS_A8+8),A8 | ||
534 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
535 | || B .S2 B3 | ||
536 | #else | ||
537 | || B .S2 system_call_saved_noack | ||
538 | #endif | ||
539 | LDW .D2T2 *+SP(REGS_B8+8),B8 | ||
540 | NOP 4 | ||
541 | ENDPROC(_nmi_handler) | ||
542 | |||
543 | ;; | ||
544 | ;; Jump to schedule() then return to ret_from_isr | ||
545 | ;; | ||
546 | #ifdef CONFIG_PREEMPT | ||
547 | resume_kernel: | ||
548 | GET_THREAD_INFO A12 | ||
549 | LDW .D1T1 *+A12(THREAD_INFO_PREEMPT_COUNT),A1 | ||
550 | NOP 4 | ||
551 | [A1] BNOP .S2 restore_all,5 | ||
552 | |||
553 | preempt_schedule: | ||
554 | GET_THREAD_INFO A2 | ||
555 | LDW .D1T1 *+A2(THREAD_INFO_FLAGS),A1 | ||
556 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
557 | MVKL .S2 preempt_schedule_irq,B0 | ||
558 | MVKH .S2 preempt_schedule_irq,B0 | ||
559 | NOP 2 | ||
560 | #else | ||
561 | NOP 4 | ||
562 | #endif | ||
563 | AND .D1 _TIF_NEED_RESCHED,A1,A1 | ||
564 | [!A1] BNOP .S2 restore_all,5 | ||
565 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
566 | B .S2 B0 | ||
567 | #else | ||
568 | B .S2 preempt_schedule_irq | ||
569 | #endif | ||
570 | ADDKPC .S2 preempt_schedule,B3,4 | ||
571 | #endif /* CONFIG_PREEMPT */ | ||
572 | |||
573 | ENTRY(enable_exception) | ||
574 | DINT | ||
575 | MVC .S2 TSR,B0 | ||
576 | MVC .S2 B3,NRP | ||
577 | MVK .L2 0xc,B1 | ||
578 | OR .D2 B0,B1,B0 | ||
579 | MVC .S2 B0,TSR ; Set GEE and XEN in TSR | ||
580 | B .S2 NRP | ||
581 | NOP 5 | ||
582 | ENDPROC(enable_exception) | ||
583 | |||
584 | ENTRY(sys_sigaltstack) | ||
585 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
586 | MVKL .S1 do_sigaltstack,A0 ; branch to do_sigaltstack | ||
587 | MVKH .S1 do_sigaltstack,A0 | ||
588 | B .S2X A0 | ||
589 | #else | ||
590 | B .S2 do_sigaltstack | ||
591 | #endif | ||
592 | LDW .D2T1 *+SP(REGS_SP+8),A6 | ||
593 | NOP 4 | ||
594 | ENDPROC(sys_sigaltstack) | ||
595 | |||
596 | ;; kernel_execve | ||
597 | ENTRY(kernel_execve) | ||
598 | MVK .S2 __NR_execve,B0 | ||
599 | SWE | ||
600 | BNOP .S2 B3,5 | ||
601 | ENDPROC(kernel_execve) | ||
602 | |||
603 | ;; | ||
604 | ;; Special system calls | ||
605 | ;; return address is in B3 | ||
606 | ;; | ||
607 | ENTRY(sys_clone) | ||
608 | ADD .D1X SP,8,A4 | ||
609 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
610 | || MVKL .S1 sys_c6x_clone,A0 | ||
611 | MVKH .S1 sys_c6x_clone,A0 | ||
612 | BNOP .S2X A0,5 | ||
613 | #else | ||
614 | || B .S2 sys_c6x_clone | ||
615 | NOP 5 | ||
616 | #endif | ||
617 | ENDPROC(sys_clone) | ||
618 | |||
619 | ENTRY(sys_rt_sigreturn) | ||
620 | ADD .D1X SP,8,A4 | ||
621 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
622 | || MVKL .S1 do_rt_sigreturn,A0 | ||
623 | MVKH .S1 do_rt_sigreturn,A0 | ||
624 | BNOP .S2X A0,5 | ||
625 | #else | ||
626 | || B .S2 do_rt_sigreturn | ||
627 | NOP 5 | ||
628 | #endif | ||
629 | ENDPROC(sys_rt_sigreturn) | ||
630 | |||
631 | ENTRY(sys_execve) | ||
632 | ADDAW .D2 SP,2,B6 ; put regs addr in 4th parameter | ||
633 | ; & adjust regs stack addr | ||
634 | LDW .D2T2 *+SP(REGS_B4+8),B4 | ||
635 | |||
636 | ;; c6x_execve(char *name, char **argv, | ||
637 | ;; char **envp, struct pt_regs *regs) | ||
638 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
639 | || MVKL .S1 sys_c6x_execve,A0 | ||
640 | MVKH .S1 sys_c6x_execve,A0 | ||
641 | B .S2X A0 | ||
642 | #else | ||
643 | || B .S2 sys_c6x_execve | ||
644 | #endif | ||
645 | STW .D2T2 B3,*SP--[2] | ||
646 | ADDKPC .S2 ret_from_c6x_execve,B3,3 | ||
647 | |||
648 | ret_from_c6x_execve: | ||
649 | LDW .D2T2 *++SP[2],B3 | ||
650 | NOP 4 | ||
651 | BNOP .S2 B3,5 | ||
652 | ENDPROC(sys_execve) | ||
653 | |||
654 | ENTRY(sys_pread_c6x) | ||
655 | MV .D2X A8,B7 | ||
656 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
657 | || MVKL .S1 sys_pread64,A0 | ||
658 | MVKH .S1 sys_pread64,A0 | ||
659 | BNOP .S2X A0,5 | ||
660 | #else | ||
661 | || B .S2 sys_pread64 | ||
662 | NOP 5 | ||
663 | #endif | ||
664 | ENDPROC(sys_pread_c6x) | ||
665 | |||
666 | ENTRY(sys_pwrite_c6x) | ||
667 | MV .D2X A8,B7 | ||
668 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
669 | || MVKL .S1 sys_pwrite64,A0 | ||
670 | MVKH .S1 sys_pwrite64,A0 | ||
671 | BNOP .S2X A0,5 | ||
672 | #else | ||
673 | || B .S2 sys_pwrite64 | ||
674 | NOP 5 | ||
675 | #endif | ||
676 | ENDPROC(sys_pwrite_c6x) | ||
677 | |||
678 | ;; On Entry | ||
679 | ;; A4 - path | ||
680 | ;; B4 - offset_lo (LE), offset_hi (BE) | ||
681 | ;; A6 - offset_lo (BE), offset_hi (LE) | ||
682 | ENTRY(sys_truncate64_c6x) | ||
683 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
684 | MV .S2 B4,B5 | ||
685 | MV .D2X A6,B4 | ||
686 | #else | ||
687 | MV .D2X A6,B5 | ||
688 | #endif | ||
689 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
690 | || MVKL .S1 sys_truncate64,A0 | ||
691 | MVKH .S1 sys_truncate64,A0 | ||
692 | BNOP .S2X A0,5 | ||
693 | #else | ||
694 | || B .S2 sys_truncate64 | ||
695 | NOP 5 | ||
696 | #endif | ||
697 | ENDPROC(sys_truncate64_c6x) | ||
698 | |||
699 | ;; On Entry | ||
700 | ;; A4 - fd | ||
701 | ;; B4 - offset_lo (LE), offset_hi (BE) | ||
702 | ;; A6 - offset_lo (BE), offset_hi (LE) | ||
703 | ENTRY(sys_ftruncate64_c6x) | ||
704 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
705 | MV .S2 B4,B5 | ||
706 | MV .D2X A6,B4 | ||
707 | #else | ||
708 | MV .D2X A6,B5 | ||
709 | #endif | ||
710 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
711 | || MVKL .S1 sys_ftruncate64,A0 | ||
712 | MVKH .S1 sys_ftruncate64,A0 | ||
713 | BNOP .S2X A0,5 | ||
714 | #else | ||
715 | || B .S2 sys_ftruncate64 | ||
716 | NOP 5 | ||
717 | #endif | ||
718 | ENDPROC(sys_ftruncate64_c6x) | ||
719 | |||
720 | #ifdef __ARCH_WANT_SYSCALL_OFF_T | ||
721 | ;; On Entry | ||
722 | ;; A4 - fd | ||
723 | ;; B4 - offset_lo (LE), offset_hi (BE) | ||
724 | ;; A6 - offset_lo (BE), offset_hi (LE) | ||
725 | ;; B6 - len | ||
726 | ;; A8 - advice | ||
727 | ENTRY(sys_fadvise64_c6x) | ||
728 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
729 | MVKL .S1 sys_fadvise64,A0 | ||
730 | MVKH .S1 sys_fadvise64,A0 | ||
731 | BNOP .S2X A0,2 | ||
732 | #else | ||
733 | B .S2 sys_fadvise64 | ||
734 | NOP 2 | ||
735 | #endif | ||
736 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
737 | MV .L2 B4,B5 | ||
738 | || MV .D2X A6,B4 | ||
739 | #else | ||
740 | MV .D2X A6,B5 | ||
741 | #endif | ||
742 | MV .D1X B6,A6 | ||
743 | MV .D2X A8,B6 | ||
744 | #endif | ||
745 | ENDPROC(sys_fadvise64_c6x) | ||
746 | |||
747 | ;; On Entry | ||
748 | ;; A4 - fd | ||
749 | ;; B4 - offset_lo (LE), offset_hi (BE) | ||
750 | ;; A6 - offset_lo (BE), offset_hi (LE) | ||
751 | ;; B6 - len_lo (LE), len_hi (BE) | ||
752 | ;; A8 - len_lo (BE), len_hi (LE) | ||
753 | ;; B8 - advice | ||
754 | ENTRY(sys_fadvise64_64_c6x) | ||
755 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
756 | MVKL .S1 sys_fadvise64_64,A0 | ||
757 | MVKH .S1 sys_fadvise64_64,A0 | ||
758 | BNOP .S2X A0,2 | ||
759 | #else | ||
760 | B .S2 sys_fadvise64_64 | ||
761 | NOP 2 | ||
762 | #endif | ||
763 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
764 | MV .L2 B4,B5 | ||
765 | || MV .D2X A6,B4 | ||
766 | MV .L1 A8,A6 | ||
767 | || MV .D1X B6,A7 | ||
768 | #else | ||
769 | MV .D2X A6,B5 | ||
770 | MV .L1 A8,A7 | ||
771 | || MV .D1X B6,A6 | ||
772 | #endif | ||
773 | MV .L2 B8,B6 | ||
774 | ENDPROC(sys_fadvise64_64_c6x) | ||
775 | |||
776 | ;; On Entry | ||
777 | ;; A4 - fd | ||
778 | ;; B4 - mode | ||
779 | ;; A6 - offset_hi | ||
780 | ;; B6 - offset_lo | ||
781 | ;; A8 - len_hi | ||
782 | ;; B8 - len_lo | ||
783 | ENTRY(sys_fallocate_c6x) | ||
784 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
785 | MVKL .S1 sys_fallocate,A0 | ||
786 | MVKH .S1 sys_fallocate,A0 | ||
787 | BNOP .S2X A0,1 | ||
788 | #else | ||
789 | B .S2 sys_fallocate | ||
790 | NOP | ||
791 | #endif | ||
792 | MV .D1 A6,A7 | ||
793 | MV .D1X B6,A6 | ||
794 | MV .D2X A8,B7 | ||
795 | MV .D2 B8,B6 | ||
796 | ENDPROC(sys_fallocate_c6x) | ||
797 | |||
798 | ;; put this in .neardata for faster access when using DSBT mode | ||
799 | .section .neardata,"aw",@progbits | ||
800 | .global current_ksp | ||
801 | .hidden current_ksp | ||
802 | current_ksp: | ||
803 | .word init_thread_union + THREAD_START_SP | ||
diff --git a/arch/c6x/kernel/head.S b/arch/c6x/kernel/head.S new file mode 100644 index 000000000000..133eab6edf6b --- /dev/null +++ b/arch/c6x/kernel/head.S | |||
@@ -0,0 +1,84 @@ | |||
1 | ; | ||
2 | ; Port on Texas Instruments TMS320C6x architecture | ||
3 | ; | ||
4 | ; Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | ; Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | ; | ||
7 | ; This program is free software; you can redistribute it and/or modify | ||
8 | ; it under the terms of the GNU General Public License version 2 as | ||
9 | ; published by the Free Software Foundation. | ||
10 | ; | ||
11 | #include <linux/linkage.h> | ||
12 | #include <linux/of_fdt.h> | ||
13 | #include <asm/asm-offsets.h> | ||
14 | |||
15 | __HEAD | ||
16 | ENTRY(_c_int00) | ||
17 | ;; Save magic and pointer | ||
18 | MV .S1 A4,A10 | ||
19 | MV .S2 B4,B10 | ||
20 | MVKL .S2 __bss_start,B5 | ||
21 | MVKH .S2 __bss_start,B5 | ||
22 | MVKL .S2 __bss_stop,B6 | ||
23 | MVKH .S2 __bss_stop,B6 | ||
24 | SUB .L2 B6,B5,B6 ; bss size | ||
25 | |||
26 | ;; Set the stack pointer | ||
27 | MVKL .S2 current_ksp,B0 | ||
28 | MVKH .S2 current_ksp,B0 | ||
29 | LDW .D2T2 *B0,B15 | ||
30 | |||
31 | ;; clear bss | ||
32 | SHR .S2 B6,3,B0 ; number of dwords to clear | ||
33 | ZERO .L2 B13 | ||
34 | ZERO .L2 B12 | ||
35 | bss_loop: | ||
36 | BDEC .S2 bss_loop,B0 | ||
37 | NOP 3 | ||
38 | CMPLT .L2 B0,0,B1 | ||
39 | [!B1] STDW .D2T2 B13:B12,*B5++[1] | ||
40 | |||
41 | NOP 4 | ||
42 | AND .D2 ~7,B15,B15 | ||
43 | |||
44 | ;; Clear GIE and PGIE | ||
45 | MVC .S2 CSR,B2 | ||
46 | CLR .S2 B2,0,1,B2 | ||
47 | MVC .S2 B2,CSR | ||
48 | MVC .S2 TSR,B2 | ||
49 | CLR .S2 B2,0,1,B2 | ||
50 | MVC .S2 B2,TSR | ||
51 | MVC .S2 ITSR,B2 | ||
52 | CLR .S2 B2,0,1,B2 | ||
53 | MVC .S2 B2,ITSR | ||
54 | MVC .S2 NTSR,B2 | ||
55 | CLR .S2 B2,0,1,B2 | ||
56 | MVC .S2 B2,NTSR | ||
57 | |||
58 | ;; pass DTB pointer to machine_init (or zero if none) | ||
59 | MVKL .S1 OF_DT_HEADER,A0 | ||
60 | MVKH .S1 OF_DT_HEADER,A0 | ||
61 | CMPEQ .L1 A10,A0,A0 | ||
62 | [A0] MV .S1X B10,A4 | ||
63 | [!A0] MVK .S1 0,A4 | ||
64 | |||
65 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
66 | MVKL .S1 machine_init,A0 | ||
67 | MVKH .S1 machine_init,A0 | ||
68 | B .S2X A0 | ||
69 | ADDKPC .S2 0f,B3,4 | ||
70 | 0: | ||
71 | #else | ||
72 | CALLP .S2 machine_init,B3 | ||
73 | #endif | ||
74 | |||
75 | ;; Jump to Linux init | ||
76 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
77 | MVKL .S1 start_kernel,A0 | ||
78 | MVKH .S1 start_kernel,A0 | ||
79 | B .S2X A0 | ||
80 | #else | ||
81 | B .S2 start_kernel | ||
82 | #endif | ||
83 | NOP 5 | ||
84 | L1: BNOP .S2 L1,5 | ||
diff --git a/arch/c6x/kernel/irq.c b/arch/c6x/kernel/irq.c new file mode 100644 index 000000000000..0929e4b2b244 --- /dev/null +++ b/arch/c6x/kernel/irq.c | |||
@@ -0,0 +1,728 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * | ||
4 | * This borrows heavily from powerpc version, which is: | ||
5 | * | ||
6 | * Derived from arch/i386/kernel/irq.c | ||
7 | * Copyright (C) 1992 Linus Torvalds | ||
8 | * Adapted from arch/i386 by Gary Thomas | ||
9 | * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) | ||
10 | * Updated and modified by Cort Dougan <cort@fsmlabs.com> | ||
11 | * Copyright (C) 1996-2001 Cort Dougan | ||
12 | * Adapted for Power Macintosh by Paul Mackerras | ||
13 | * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version | ||
18 | * 2 of the License, or (at your option) any later version. | ||
19 | */ | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/seq_file.h> | ||
22 | #include <linux/radix-tree.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/of.h> | ||
25 | #include <linux/of_irq.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/kernel_stat.h> | ||
28 | |||
29 | #include <asm/megamod-pic.h> | ||
30 | |||
31 | unsigned long irq_err_count; | ||
32 | |||
33 | static DEFINE_RAW_SPINLOCK(core_irq_lock); | ||
34 | |||
35 | static void mask_core_irq(struct irq_data *data) | ||
36 | { | ||
37 | unsigned int prio = data->irq; | ||
38 | |||
39 | BUG_ON(prio < 4 || prio >= NR_PRIORITY_IRQS); | ||
40 | |||
41 | raw_spin_lock(&core_irq_lock); | ||
42 | and_creg(IER, ~(1 << prio)); | ||
43 | raw_spin_unlock(&core_irq_lock); | ||
44 | } | ||
45 | |||
46 | static void unmask_core_irq(struct irq_data *data) | ||
47 | { | ||
48 | unsigned int prio = data->irq; | ||
49 | |||
50 | raw_spin_lock(&core_irq_lock); | ||
51 | or_creg(IER, 1 << prio); | ||
52 | raw_spin_unlock(&core_irq_lock); | ||
53 | } | ||
54 | |||
55 | static struct irq_chip core_chip = { | ||
56 | .name = "core", | ||
57 | .irq_mask = mask_core_irq, | ||
58 | .irq_unmask = unmask_core_irq, | ||
59 | }; | ||
60 | |||
61 | asmlinkage void c6x_do_IRQ(unsigned int prio, struct pt_regs *regs) | ||
62 | { | ||
63 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
64 | |||
65 | irq_enter(); | ||
66 | |||
67 | BUG_ON(prio < 4 || prio >= NR_PRIORITY_IRQS); | ||
68 | |||
69 | generic_handle_irq(prio); | ||
70 | |||
71 | irq_exit(); | ||
72 | |||
73 | set_irq_regs(old_regs); | ||
74 | } | ||
75 | |||
76 | static struct irq_host *core_host; | ||
77 | |||
78 | static int core_host_map(struct irq_host *h, unsigned int virq, | ||
79 | irq_hw_number_t hw) | ||
80 | { | ||
81 | if (hw < 4 || hw >= NR_PRIORITY_IRQS) | ||
82 | return -EINVAL; | ||
83 | |||
84 | irq_set_status_flags(virq, IRQ_LEVEL); | ||
85 | irq_set_chip_and_handler(virq, &core_chip, handle_level_irq); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static struct irq_host_ops core_host_ops = { | ||
90 | .map = core_host_map, | ||
91 | }; | ||
92 | |||
93 | void __init init_IRQ(void) | ||
94 | { | ||
95 | struct device_node *np; | ||
96 | |||
97 | /* Mask all priority IRQs */ | ||
98 | and_creg(IER, ~0xfff0); | ||
99 | |||
100 | np = of_find_compatible_node(NULL, NULL, "ti,c64x+core-pic"); | ||
101 | if (np != NULL) { | ||
102 | /* create the core host */ | ||
103 | core_host = irq_alloc_host(np, IRQ_HOST_MAP_PRIORITY, 0, | ||
104 | &core_host_ops, 0); | ||
105 | if (core_host) | ||
106 | irq_set_default_host(core_host); | ||
107 | of_node_put(np); | ||
108 | } | ||
109 | |||
110 | printk(KERN_INFO "Core interrupt controller initialized\n"); | ||
111 | |||
112 | /* now we're ready for other SoC controllers */ | ||
113 | megamod_pic_init(); | ||
114 | |||
115 | /* Clear all general IRQ flags */ | ||
116 | set_creg(ICR, 0xfff0); | ||
117 | } | ||
118 | |||
119 | void ack_bad_irq(int irq) | ||
120 | { | ||
121 | printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq); | ||
122 | irq_err_count++; | ||
123 | } | ||
124 | |||
125 | int arch_show_interrupts(struct seq_file *p, int prec) | ||
126 | { | ||
127 | seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * IRQ controller and virtual interrupts | ||
133 | */ | ||
134 | |||
135 | /* The main irq map itself is an array of NR_IRQ entries containing the | ||
136 | * associate host and irq number. An entry with a host of NULL is free. | ||
137 | * An entry can be allocated if it's free, the allocator always then sets | ||
138 | * hwirq first to the host's invalid irq number and then fills ops. | ||
139 | */ | ||
140 | struct irq_map_entry { | ||
141 | irq_hw_number_t hwirq; | ||
142 | struct irq_host *host; | ||
143 | }; | ||
144 | |||
145 | static LIST_HEAD(irq_hosts); | ||
146 | static DEFINE_RAW_SPINLOCK(irq_big_lock); | ||
147 | static DEFINE_MUTEX(revmap_trees_mutex); | ||
148 | static struct irq_map_entry irq_map[NR_IRQS]; | ||
149 | static unsigned int irq_virq_count = NR_IRQS; | ||
150 | static struct irq_host *irq_default_host; | ||
151 | |||
152 | irq_hw_number_t irqd_to_hwirq(struct irq_data *d) | ||
153 | { | ||
154 | return irq_map[d->irq].hwirq; | ||
155 | } | ||
156 | EXPORT_SYMBOL_GPL(irqd_to_hwirq); | ||
157 | |||
158 | irq_hw_number_t virq_to_hw(unsigned int virq) | ||
159 | { | ||
160 | return irq_map[virq].hwirq; | ||
161 | } | ||
162 | EXPORT_SYMBOL_GPL(virq_to_hw); | ||
163 | |||
164 | bool virq_is_host(unsigned int virq, struct irq_host *host) | ||
165 | { | ||
166 | return irq_map[virq].host == host; | ||
167 | } | ||
168 | EXPORT_SYMBOL_GPL(virq_is_host); | ||
169 | |||
170 | static int default_irq_host_match(struct irq_host *h, struct device_node *np) | ||
171 | { | ||
172 | return h->of_node != NULL && h->of_node == np; | ||
173 | } | ||
174 | |||
175 | struct irq_host *irq_alloc_host(struct device_node *of_node, | ||
176 | unsigned int revmap_type, | ||
177 | unsigned int revmap_arg, | ||
178 | struct irq_host_ops *ops, | ||
179 | irq_hw_number_t inval_irq) | ||
180 | { | ||
181 | struct irq_host *host; | ||
182 | unsigned int size = sizeof(struct irq_host); | ||
183 | unsigned int i; | ||
184 | unsigned int *rmap; | ||
185 | unsigned long flags; | ||
186 | |||
187 | /* Allocate structure and revmap table if using linear mapping */ | ||
188 | if (revmap_type == IRQ_HOST_MAP_LINEAR) | ||
189 | size += revmap_arg * sizeof(unsigned int); | ||
190 | host = kzalloc(size, GFP_KERNEL); | ||
191 | if (host == NULL) | ||
192 | return NULL; | ||
193 | |||
194 | /* Fill structure */ | ||
195 | host->revmap_type = revmap_type; | ||
196 | host->inval_irq = inval_irq; | ||
197 | host->ops = ops; | ||
198 | host->of_node = of_node_get(of_node); | ||
199 | |||
200 | if (host->ops->match == NULL) | ||
201 | host->ops->match = default_irq_host_match; | ||
202 | |||
203 | raw_spin_lock_irqsave(&irq_big_lock, flags); | ||
204 | |||
205 | /* Check for the priority controller. */ | ||
206 | if (revmap_type == IRQ_HOST_MAP_PRIORITY) { | ||
207 | if (irq_map[0].host != NULL) { | ||
208 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); | ||
209 | of_node_put(host->of_node); | ||
210 | kfree(host); | ||
211 | return NULL; | ||
212 | } | ||
213 | irq_map[0].host = host; | ||
214 | } | ||
215 | |||
216 | list_add(&host->link, &irq_hosts); | ||
217 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); | ||
218 | |||
219 | /* Additional setups per revmap type */ | ||
220 | switch (revmap_type) { | ||
221 | case IRQ_HOST_MAP_PRIORITY: | ||
222 | /* 0 is always the invalid number for priority */ | ||
223 | host->inval_irq = 0; | ||
224 | /* setup us as the host for all priority interrupts */ | ||
225 | for (i = 1; i < NR_PRIORITY_IRQS; i++) { | ||
226 | irq_map[i].hwirq = i; | ||
227 | smp_wmb(); | ||
228 | irq_map[i].host = host; | ||
229 | smp_wmb(); | ||
230 | |||
231 | ops->map(host, i, i); | ||
232 | } | ||
233 | break; | ||
234 | case IRQ_HOST_MAP_LINEAR: | ||
235 | rmap = (unsigned int *)(host + 1); | ||
236 | for (i = 0; i < revmap_arg; i++) | ||
237 | rmap[i] = NO_IRQ; | ||
238 | host->revmap_data.linear.size = revmap_arg; | ||
239 | smp_wmb(); | ||
240 | host->revmap_data.linear.revmap = rmap; | ||
241 | break; | ||
242 | case IRQ_HOST_MAP_TREE: | ||
243 | INIT_RADIX_TREE(&host->revmap_data.tree, GFP_KERNEL); | ||
244 | break; | ||
245 | default: | ||
246 | break; | ||
247 | } | ||
248 | |||
249 | pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type, host); | ||
250 | |||
251 | return host; | ||
252 | } | ||
253 | |||
254 | struct irq_host *irq_find_host(struct device_node *node) | ||
255 | { | ||
256 | struct irq_host *h, *found = NULL; | ||
257 | unsigned long flags; | ||
258 | |||
259 | /* We might want to match the legacy controller last since | ||
260 | * it might potentially be set to match all interrupts in | ||
261 | * the absence of a device node. This isn't a problem so far | ||
262 | * yet though... | ||
263 | */ | ||
264 | raw_spin_lock_irqsave(&irq_big_lock, flags); | ||
265 | list_for_each_entry(h, &irq_hosts, link) | ||
266 | if (h->ops->match(h, node)) { | ||
267 | found = h; | ||
268 | break; | ||
269 | } | ||
270 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); | ||
271 | return found; | ||
272 | } | ||
273 | EXPORT_SYMBOL_GPL(irq_find_host); | ||
274 | |||
275 | void irq_set_default_host(struct irq_host *host) | ||
276 | { | ||
277 | pr_debug("irq: Default host set to @0x%p\n", host); | ||
278 | |||
279 | irq_default_host = host; | ||
280 | } | ||
281 | |||
282 | void irq_set_virq_count(unsigned int count) | ||
283 | { | ||
284 | pr_debug("irq: Trying to set virq count to %d\n", count); | ||
285 | |||
286 | BUG_ON(count < NR_PRIORITY_IRQS); | ||
287 | if (count < NR_IRQS) | ||
288 | irq_virq_count = count; | ||
289 | } | ||
290 | |||
291 | static int irq_setup_virq(struct irq_host *host, unsigned int virq, | ||
292 | irq_hw_number_t hwirq) | ||
293 | { | ||
294 | int res; | ||
295 | |||
296 | res = irq_alloc_desc_at(virq, 0); | ||
297 | if (res != virq) { | ||
298 | pr_debug("irq: -> allocating desc failed\n"); | ||
299 | goto error; | ||
300 | } | ||
301 | |||
302 | /* map it */ | ||
303 | smp_wmb(); | ||
304 | irq_map[virq].hwirq = hwirq; | ||
305 | smp_mb(); | ||
306 | |||
307 | if (host->ops->map(host, virq, hwirq)) { | ||
308 | pr_debug("irq: -> mapping failed, freeing\n"); | ||
309 | goto errdesc; | ||
310 | } | ||
311 | |||
312 | irq_clear_status_flags(virq, IRQ_NOREQUEST); | ||
313 | |||
314 | return 0; | ||
315 | |||
316 | errdesc: | ||
317 | irq_free_descs(virq, 1); | ||
318 | error: | ||
319 | irq_free_virt(virq, 1); | ||
320 | return -1; | ||
321 | } | ||
322 | |||
323 | unsigned int irq_create_direct_mapping(struct irq_host *host) | ||
324 | { | ||
325 | unsigned int virq; | ||
326 | |||
327 | if (host == NULL) | ||
328 | host = irq_default_host; | ||
329 | |||
330 | BUG_ON(host == NULL); | ||
331 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP); | ||
332 | |||
333 | virq = irq_alloc_virt(host, 1, 0); | ||
334 | if (virq == NO_IRQ) { | ||
335 | pr_debug("irq: create_direct virq allocation failed\n"); | ||
336 | return NO_IRQ; | ||
337 | } | ||
338 | |||
339 | pr_debug("irq: create_direct obtained virq %d\n", virq); | ||
340 | |||
341 | if (irq_setup_virq(host, virq, virq)) | ||
342 | return NO_IRQ; | ||
343 | |||
344 | return virq; | ||
345 | } | ||
346 | |||
347 | unsigned int irq_create_mapping(struct irq_host *host, | ||
348 | irq_hw_number_t hwirq) | ||
349 | { | ||
350 | unsigned int virq, hint; | ||
351 | |||
352 | pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq); | ||
353 | |||
354 | /* Look for default host if nececssary */ | ||
355 | if (host == NULL) | ||
356 | host = irq_default_host; | ||
357 | if (host == NULL) { | ||
358 | printk(KERN_WARNING "irq_create_mapping called for" | ||
359 | " NULL host, hwirq=%lx\n", hwirq); | ||
360 | WARN_ON(1); | ||
361 | return NO_IRQ; | ||
362 | } | ||
363 | pr_debug("irq: -> using host @%p\n", host); | ||
364 | |||
365 | /* Check if mapping already exists */ | ||
366 | virq = irq_find_mapping(host, hwirq); | ||
367 | if (virq != NO_IRQ) { | ||
368 | pr_debug("irq: -> existing mapping on virq %d\n", virq); | ||
369 | return virq; | ||
370 | } | ||
371 | |||
372 | /* Allocate a virtual interrupt number */ | ||
373 | hint = hwirq % irq_virq_count; | ||
374 | virq = irq_alloc_virt(host, 1, hint); | ||
375 | if (virq == NO_IRQ) { | ||
376 | pr_debug("irq: -> virq allocation failed\n"); | ||
377 | return NO_IRQ; | ||
378 | } | ||
379 | |||
380 | if (irq_setup_virq(host, virq, hwirq)) | ||
381 | return NO_IRQ; | ||
382 | |||
383 | pr_debug("irq: irq %lu on host %s mapped to virtual irq %u\n", | ||
384 | hwirq, host->of_node ? host->of_node->full_name : "null", virq); | ||
385 | |||
386 | return virq; | ||
387 | } | ||
388 | EXPORT_SYMBOL_GPL(irq_create_mapping); | ||
389 | |||
390 | unsigned int irq_create_of_mapping(struct device_node *controller, | ||
391 | const u32 *intspec, unsigned int intsize) | ||
392 | { | ||
393 | struct irq_host *host; | ||
394 | irq_hw_number_t hwirq; | ||
395 | unsigned int type = IRQ_TYPE_NONE; | ||
396 | unsigned int virq; | ||
397 | |||
398 | if (controller == NULL) | ||
399 | host = irq_default_host; | ||
400 | else | ||
401 | host = irq_find_host(controller); | ||
402 | if (host == NULL) { | ||
403 | printk(KERN_WARNING "irq: no irq host found for %s !\n", | ||
404 | controller->full_name); | ||
405 | return NO_IRQ; | ||
406 | } | ||
407 | |||
408 | /* If host has no translation, then we assume interrupt line */ | ||
409 | if (host->ops->xlate == NULL) | ||
410 | hwirq = intspec[0]; | ||
411 | else { | ||
412 | if (host->ops->xlate(host, controller, intspec, intsize, | ||
413 | &hwirq, &type)) | ||
414 | return NO_IRQ; | ||
415 | } | ||
416 | |||
417 | /* Create mapping */ | ||
418 | virq = irq_create_mapping(host, hwirq); | ||
419 | if (virq == NO_IRQ) | ||
420 | return virq; | ||
421 | |||
422 | /* Set type if specified and different than the current one */ | ||
423 | if (type != IRQ_TYPE_NONE && | ||
424 | type != (irqd_get_trigger_type(irq_get_irq_data(virq)))) | ||
425 | irq_set_irq_type(virq, type); | ||
426 | return virq; | ||
427 | } | ||
428 | EXPORT_SYMBOL_GPL(irq_create_of_mapping); | ||
429 | |||
430 | void irq_dispose_mapping(unsigned int virq) | ||
431 | { | ||
432 | struct irq_host *host; | ||
433 | irq_hw_number_t hwirq; | ||
434 | |||
435 | if (virq == NO_IRQ) | ||
436 | return; | ||
437 | |||
438 | /* Never unmap priority interrupts */ | ||
439 | if (virq < NR_PRIORITY_IRQS) | ||
440 | return; | ||
441 | |||
442 | host = irq_map[virq].host; | ||
443 | if (WARN_ON(host == NULL)) | ||
444 | return; | ||
445 | |||
446 | irq_set_status_flags(virq, IRQ_NOREQUEST); | ||
447 | |||
448 | /* remove chip and handler */ | ||
449 | irq_set_chip_and_handler(virq, NULL, NULL); | ||
450 | |||
451 | /* Make sure it's completed */ | ||
452 | synchronize_irq(virq); | ||
453 | |||
454 | /* Tell the PIC about it */ | ||
455 | if (host->ops->unmap) | ||
456 | host->ops->unmap(host, virq); | ||
457 | smp_mb(); | ||
458 | |||
459 | /* Clear reverse map */ | ||
460 | hwirq = irq_map[virq].hwirq; | ||
461 | switch (host->revmap_type) { | ||
462 | case IRQ_HOST_MAP_LINEAR: | ||
463 | if (hwirq < host->revmap_data.linear.size) | ||
464 | host->revmap_data.linear.revmap[hwirq] = NO_IRQ; | ||
465 | break; | ||
466 | case IRQ_HOST_MAP_TREE: | ||
467 | mutex_lock(&revmap_trees_mutex); | ||
468 | radix_tree_delete(&host->revmap_data.tree, hwirq); | ||
469 | mutex_unlock(&revmap_trees_mutex); | ||
470 | break; | ||
471 | } | ||
472 | |||
473 | /* Destroy map */ | ||
474 | smp_mb(); | ||
475 | irq_map[virq].hwirq = host->inval_irq; | ||
476 | |||
477 | irq_free_descs(virq, 1); | ||
478 | /* Free it */ | ||
479 | irq_free_virt(virq, 1); | ||
480 | } | ||
481 | EXPORT_SYMBOL_GPL(irq_dispose_mapping); | ||
482 | |||
483 | unsigned int irq_find_mapping(struct irq_host *host, | ||
484 | irq_hw_number_t hwirq) | ||
485 | { | ||
486 | unsigned int i; | ||
487 | unsigned int hint = hwirq % irq_virq_count; | ||
488 | |||
489 | /* Look for default host if nececssary */ | ||
490 | if (host == NULL) | ||
491 | host = irq_default_host; | ||
492 | if (host == NULL) | ||
493 | return NO_IRQ; | ||
494 | |||
495 | /* Slow path does a linear search of the map */ | ||
496 | i = hint; | ||
497 | do { | ||
498 | if (irq_map[i].host == host && | ||
499 | irq_map[i].hwirq == hwirq) | ||
500 | return i; | ||
501 | i++; | ||
502 | if (i >= irq_virq_count) | ||
503 | i = 4; | ||
504 | } while (i != hint); | ||
505 | return NO_IRQ; | ||
506 | } | ||
507 | EXPORT_SYMBOL_GPL(irq_find_mapping); | ||
508 | |||
509 | unsigned int irq_radix_revmap_lookup(struct irq_host *host, | ||
510 | irq_hw_number_t hwirq) | ||
511 | { | ||
512 | struct irq_map_entry *ptr; | ||
513 | unsigned int virq; | ||
514 | |||
515 | if (WARN_ON_ONCE(host->revmap_type != IRQ_HOST_MAP_TREE)) | ||
516 | return irq_find_mapping(host, hwirq); | ||
517 | |||
518 | /* | ||
519 | * The ptr returned references the static global irq_map. | ||
520 | * but freeing an irq can delete nodes along the path to | ||
521 | * do the lookup via call_rcu. | ||
522 | */ | ||
523 | rcu_read_lock(); | ||
524 | ptr = radix_tree_lookup(&host->revmap_data.tree, hwirq); | ||
525 | rcu_read_unlock(); | ||
526 | |||
527 | /* | ||
528 | * If found in radix tree, then fine. | ||
529 | * Else fallback to linear lookup - this should not happen in practice | ||
530 | * as it means that we failed to insert the node in the radix tree. | ||
531 | */ | ||
532 | if (ptr) | ||
533 | virq = ptr - irq_map; | ||
534 | else | ||
535 | virq = irq_find_mapping(host, hwirq); | ||
536 | |||
537 | return virq; | ||
538 | } | ||
539 | |||
540 | void irq_radix_revmap_insert(struct irq_host *host, unsigned int virq, | ||
541 | irq_hw_number_t hwirq) | ||
542 | { | ||
543 | if (WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE)) | ||
544 | return; | ||
545 | |||
546 | if (virq != NO_IRQ) { | ||
547 | mutex_lock(&revmap_trees_mutex); | ||
548 | radix_tree_insert(&host->revmap_data.tree, hwirq, | ||
549 | &irq_map[virq]); | ||
550 | mutex_unlock(&revmap_trees_mutex); | ||
551 | } | ||
552 | } | ||
553 | |||
554 | unsigned int irq_linear_revmap(struct irq_host *host, | ||
555 | irq_hw_number_t hwirq) | ||
556 | { | ||
557 | unsigned int *revmap; | ||
558 | |||
559 | if (WARN_ON_ONCE(host->revmap_type != IRQ_HOST_MAP_LINEAR)) | ||
560 | return irq_find_mapping(host, hwirq); | ||
561 | |||
562 | /* Check revmap bounds */ | ||
563 | if (unlikely(hwirq >= host->revmap_data.linear.size)) | ||
564 | return irq_find_mapping(host, hwirq); | ||
565 | |||
566 | /* Check if revmap was allocated */ | ||
567 | revmap = host->revmap_data.linear.revmap; | ||
568 | if (unlikely(revmap == NULL)) | ||
569 | return irq_find_mapping(host, hwirq); | ||
570 | |||
571 | /* Fill up revmap with slow path if no mapping found */ | ||
572 | if (unlikely(revmap[hwirq] == NO_IRQ)) | ||
573 | revmap[hwirq] = irq_find_mapping(host, hwirq); | ||
574 | |||
575 | return revmap[hwirq]; | ||
576 | } | ||
577 | |||
578 | unsigned int irq_alloc_virt(struct irq_host *host, | ||
579 | unsigned int count, | ||
580 | unsigned int hint) | ||
581 | { | ||
582 | unsigned long flags; | ||
583 | unsigned int i, j, found = NO_IRQ; | ||
584 | |||
585 | if (count == 0 || count > (irq_virq_count - NR_PRIORITY_IRQS)) | ||
586 | return NO_IRQ; | ||
587 | |||
588 | raw_spin_lock_irqsave(&irq_big_lock, flags); | ||
589 | |||
590 | /* Use hint for 1 interrupt if any */ | ||
591 | if (count == 1 && hint >= NR_PRIORITY_IRQS && | ||
592 | hint < irq_virq_count && irq_map[hint].host == NULL) { | ||
593 | found = hint; | ||
594 | goto hint_found; | ||
595 | } | ||
596 | |||
597 | /* Look for count consecutive numbers in the allocatable | ||
598 | * (non-legacy) space | ||
599 | */ | ||
600 | for (i = NR_PRIORITY_IRQS, j = 0; i < irq_virq_count; i++) { | ||
601 | if (irq_map[i].host != NULL) | ||
602 | j = 0; | ||
603 | else | ||
604 | j++; | ||
605 | |||
606 | if (j == count) { | ||
607 | found = i - count + 1; | ||
608 | break; | ||
609 | } | ||
610 | } | ||
611 | if (found == NO_IRQ) { | ||
612 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); | ||
613 | return NO_IRQ; | ||
614 | } | ||
615 | hint_found: | ||
616 | for (i = found; i < (found + count); i++) { | ||
617 | irq_map[i].hwirq = host->inval_irq; | ||
618 | smp_wmb(); | ||
619 | irq_map[i].host = host; | ||
620 | } | ||
621 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); | ||
622 | return found; | ||
623 | } | ||
624 | |||
625 | void irq_free_virt(unsigned int virq, unsigned int count) | ||
626 | { | ||
627 | unsigned long flags; | ||
628 | unsigned int i; | ||
629 | |||
630 | WARN_ON(virq < NR_PRIORITY_IRQS); | ||
631 | WARN_ON(count == 0 || (virq + count) > irq_virq_count); | ||
632 | |||
633 | if (virq < NR_PRIORITY_IRQS) { | ||
634 | if (virq + count < NR_PRIORITY_IRQS) | ||
635 | return; | ||
636 | count -= NR_PRIORITY_IRQS - virq; | ||
637 | virq = NR_PRIORITY_IRQS; | ||
638 | } | ||
639 | |||
640 | if (count > irq_virq_count || virq > irq_virq_count - count) { | ||
641 | if (virq > irq_virq_count) | ||
642 | return; | ||
643 | count = irq_virq_count - virq; | ||
644 | } | ||
645 | |||
646 | raw_spin_lock_irqsave(&irq_big_lock, flags); | ||
647 | for (i = virq; i < (virq + count); i++) { | ||
648 | struct irq_host *host; | ||
649 | |||
650 | host = irq_map[i].host; | ||
651 | irq_map[i].hwirq = host->inval_irq; | ||
652 | smp_wmb(); | ||
653 | irq_map[i].host = NULL; | ||
654 | } | ||
655 | raw_spin_unlock_irqrestore(&irq_big_lock, flags); | ||
656 | } | ||
657 | |||
658 | #ifdef CONFIG_VIRQ_DEBUG | ||
659 | static int virq_debug_show(struct seq_file *m, void *private) | ||
660 | { | ||
661 | unsigned long flags; | ||
662 | struct irq_desc *desc; | ||
663 | const char *p; | ||
664 | static const char none[] = "none"; | ||
665 | void *data; | ||
666 | int i; | ||
667 | |||
668 | seq_printf(m, "%-5s %-7s %-15s %-18s %s\n", "virq", "hwirq", | ||
669 | "chip name", "chip data", "host name"); | ||
670 | |||
671 | for (i = 1; i < nr_irqs; i++) { | ||
672 | desc = irq_to_desc(i); | ||
673 | if (!desc) | ||
674 | continue; | ||
675 | |||
676 | raw_spin_lock_irqsave(&desc->lock, flags); | ||
677 | |||
678 | if (desc->action && desc->action->handler) { | ||
679 | struct irq_chip *chip; | ||
680 | |||
681 | seq_printf(m, "%5d ", i); | ||
682 | seq_printf(m, "0x%05lx ", irq_map[i].hwirq); | ||
683 | |||
684 | chip = irq_desc_get_chip(desc); | ||
685 | if (chip && chip->name) | ||
686 | p = chip->name; | ||
687 | else | ||
688 | p = none; | ||
689 | seq_printf(m, "%-15s ", p); | ||
690 | |||
691 | data = irq_desc_get_chip_data(desc); | ||
692 | seq_printf(m, "0x%16p ", data); | ||
693 | |||
694 | if (irq_map[i].host && irq_map[i].host->of_node) | ||
695 | p = irq_map[i].host->of_node->full_name; | ||
696 | else | ||
697 | p = none; | ||
698 | seq_printf(m, "%s\n", p); | ||
699 | } | ||
700 | |||
701 | raw_spin_unlock_irqrestore(&desc->lock, flags); | ||
702 | } | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | static int virq_debug_open(struct inode *inode, struct file *file) | ||
708 | { | ||
709 | return single_open(file, virq_debug_show, inode->i_private); | ||
710 | } | ||
711 | |||
712 | static const struct file_operations virq_debug_fops = { | ||
713 | .open = virq_debug_open, | ||
714 | .read = seq_read, | ||
715 | .llseek = seq_lseek, | ||
716 | .release = single_release, | ||
717 | }; | ||
718 | |||
719 | static int __init irq_debugfs_init(void) | ||
720 | { | ||
721 | if (debugfs_create_file("virq_mapping", S_IRUGO, powerpc_debugfs_root, | ||
722 | NULL, &virq_debug_fops) == NULL) | ||
723 | return -ENOMEM; | ||
724 | |||
725 | return 0; | ||
726 | } | ||
727 | device_initcall(irq_debugfs_init); | ||
728 | #endif /* CONFIG_VIRQ_DEBUG */ | ||
diff --git a/arch/c6x/kernel/module.c b/arch/c6x/kernel/module.c new file mode 100644 index 000000000000..5fc03f18f56c --- /dev/null +++ b/arch/c6x/kernel/module.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2005, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Thomas Charleux (thomas.charleux@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | #include <linux/moduleloader.h> | ||
13 | #include <linux/elf.h> | ||
14 | #include <linux/vmalloc.h> | ||
15 | #include <linux/kernel.h> | ||
16 | |||
17 | static inline int fixup_pcr(u32 *ip, Elf32_Addr dest, u32 maskbits, int shift) | ||
18 | { | ||
19 | u32 opcode; | ||
20 | long ep = (long)ip & ~31; | ||
21 | long delta = ((long)dest - ep) >> 2; | ||
22 | long mask = (1 << maskbits) - 1; | ||
23 | |||
24 | if ((delta >> (maskbits - 1)) == 0 || | ||
25 | (delta >> (maskbits - 1)) == -1) { | ||
26 | opcode = *ip; | ||
27 | opcode &= ~(mask << shift); | ||
28 | opcode |= ((delta & mask) << shift); | ||
29 | *ip = opcode; | ||
30 | |||
31 | pr_debug("REL PCR_S%d[%p] dest[%p] opcode[%08x]\n", | ||
32 | maskbits, ip, (void *)dest, opcode); | ||
33 | |||
34 | return 0; | ||
35 | } | ||
36 | pr_err("PCR_S%d reloc %p -> %p out of range!\n", | ||
37 | maskbits, ip, (void *)dest); | ||
38 | |||
39 | return -1; | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * apply a RELA relocation | ||
44 | */ | ||
45 | int apply_relocate_add(Elf32_Shdr *sechdrs, | ||
46 | const char *strtab, | ||
47 | unsigned int symindex, | ||
48 | unsigned int relsec, | ||
49 | struct module *me) | ||
50 | { | ||
51 | Elf32_Rela *rel = (void *) sechdrs[relsec].sh_addr; | ||
52 | Elf_Sym *sym; | ||
53 | u32 *location, opcode; | ||
54 | unsigned int i; | ||
55 | Elf32_Addr v; | ||
56 | Elf_Addr offset = 0; | ||
57 | |||
58 | pr_debug("Applying relocate section %u to %u with offset 0x%x\n", | ||
59 | relsec, sechdrs[relsec].sh_info, offset); | ||
60 | |||
61 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
62 | /* This is where to make the change */ | ||
63 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
64 | + rel[i].r_offset - offset; | ||
65 | |||
66 | /* This is the symbol it is referring to. Note that all | ||
67 | undefined symbols have been resolved. */ | ||
68 | sym = (Elf_Sym *)sechdrs[symindex].sh_addr | ||
69 | + ELF32_R_SYM(rel[i].r_info); | ||
70 | |||
71 | /* this is the adjustment to be made */ | ||
72 | v = sym->st_value + rel[i].r_addend; | ||
73 | |||
74 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
75 | case R_C6000_ABS32: | ||
76 | pr_debug("RELA ABS32: [%p] = 0x%x\n", location, v); | ||
77 | *location = v; | ||
78 | break; | ||
79 | case R_C6000_ABS16: | ||
80 | pr_debug("RELA ABS16: [%p] = 0x%x\n", location, v); | ||
81 | *(u16 *)location = v; | ||
82 | break; | ||
83 | case R_C6000_ABS8: | ||
84 | pr_debug("RELA ABS8: [%p] = 0x%x\n", location, v); | ||
85 | *(u8 *)location = v; | ||
86 | break; | ||
87 | case R_C6000_ABS_L16: | ||
88 | opcode = *location; | ||
89 | opcode &= ~0x7fff80; | ||
90 | opcode |= ((v & 0xffff) << 7); | ||
91 | pr_debug("RELA ABS_L16[%p] v[0x%x] opcode[0x%x]\n", | ||
92 | location, v, opcode); | ||
93 | *location = opcode; | ||
94 | break; | ||
95 | case R_C6000_ABS_H16: | ||
96 | opcode = *location; | ||
97 | opcode &= ~0x7fff80; | ||
98 | opcode |= ((v >> 9) & 0x7fff80); | ||
99 | pr_debug("RELA ABS_H16[%p] v[0x%x] opcode[0x%x]\n", | ||
100 | location, v, opcode); | ||
101 | *location = opcode; | ||
102 | break; | ||
103 | case R_C6000_PCR_S21: | ||
104 | if (fixup_pcr(location, v, 21, 7)) | ||
105 | return -ENOEXEC; | ||
106 | break; | ||
107 | case R_C6000_PCR_S12: | ||
108 | if (fixup_pcr(location, v, 12, 16)) | ||
109 | return -ENOEXEC; | ||
110 | break; | ||
111 | case R_C6000_PCR_S10: | ||
112 | if (fixup_pcr(location, v, 10, 13)) | ||
113 | return -ENOEXEC; | ||
114 | break; | ||
115 | default: | ||
116 | pr_err("module %s: Unknown RELA relocation: %u\n", | ||
117 | me->name, ELF32_R_TYPE(rel[i].r_info)); | ||
118 | return -ENOEXEC; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | return 0; | ||
123 | } | ||
diff --git a/arch/c6x/kernel/process.c b/arch/c6x/kernel/process.c new file mode 100644 index 000000000000..7ca8c41b03cd --- /dev/null +++ b/arch/c6x/kernel/process.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/unistd.h> | ||
14 | #include <linux/ptrace.h> | ||
15 | #include <linux/init_task.h> | ||
16 | #include <linux/tick.h> | ||
17 | #include <linux/mqueue.h> | ||
18 | #include <linux/syscalls.h> | ||
19 | #include <linux/reboot.h> | ||
20 | |||
21 | #include <asm/syscalls.h> | ||
22 | |||
23 | /* hooks for board specific support */ | ||
24 | void (*c6x_restart)(void); | ||
25 | void (*c6x_halt)(void); | ||
26 | |||
27 | extern asmlinkage void ret_from_fork(void); | ||
28 | |||
29 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
30 | static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | ||
31 | |||
32 | /* | ||
33 | * Initial thread structure. | ||
34 | */ | ||
35 | union thread_union init_thread_union __init_task_data = { | ||
36 | INIT_THREAD_INFO(init_task) | ||
37 | }; | ||
38 | |||
39 | /* | ||
40 | * Initial task structure. | ||
41 | */ | ||
42 | struct task_struct init_task = INIT_TASK(init_task); | ||
43 | EXPORT_SYMBOL(init_task); | ||
44 | |||
45 | /* | ||
46 | * power off function, if any | ||
47 | */ | ||
48 | void (*pm_power_off)(void); | ||
49 | EXPORT_SYMBOL(pm_power_off); | ||
50 | |||
51 | static void c6x_idle(void) | ||
52 | { | ||
53 | unsigned long tmp; | ||
54 | |||
55 | /* | ||
56 | * Put local_irq_enable and idle in same execute packet | ||
57 | * to make them atomic and avoid race to idle with | ||
58 | * interrupts enabled. | ||
59 | */ | ||
60 | asm volatile (" mvc .s2 CSR,%0\n" | ||
61 | " or .d2 1,%0,%0\n" | ||
62 | " mvc .s2 %0,CSR\n" | ||
63 | "|| idle\n" | ||
64 | : "=b"(tmp)); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * The idle loop for C64x | ||
69 | */ | ||
70 | void cpu_idle(void) | ||
71 | { | ||
72 | /* endless idle loop with no priority at all */ | ||
73 | while (1) { | ||
74 | tick_nohz_idle_enter(); | ||
75 | rcu_idle_enter(); | ||
76 | while (1) { | ||
77 | local_irq_disable(); | ||
78 | if (need_resched()) { | ||
79 | local_irq_enable(); | ||
80 | break; | ||
81 | } | ||
82 | c6x_idle(); /* enables local irqs */ | ||
83 | } | ||
84 | rcu_idle_exit(); | ||
85 | tick_nohz_idle_exit(); | ||
86 | |||
87 | preempt_enable_no_resched(); | ||
88 | schedule(); | ||
89 | preempt_disable(); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | static void halt_loop(void) | ||
94 | { | ||
95 | printk(KERN_EMERG "System Halted, OK to turn off power\n"); | ||
96 | local_irq_disable(); | ||
97 | while (1) | ||
98 | asm volatile("idle\n"); | ||
99 | } | ||
100 | |||
101 | void machine_restart(char *__unused) | ||
102 | { | ||
103 | if (c6x_restart) | ||
104 | c6x_restart(); | ||
105 | halt_loop(); | ||
106 | } | ||
107 | |||
108 | void machine_halt(void) | ||
109 | { | ||
110 | if (c6x_halt) | ||
111 | c6x_halt(); | ||
112 | halt_loop(); | ||
113 | } | ||
114 | |||
115 | void machine_power_off(void) | ||
116 | { | ||
117 | if (pm_power_off) | ||
118 | pm_power_off(); | ||
119 | halt_loop(); | ||
120 | } | ||
121 | |||
122 | static void kernel_thread_helper(int dummy, void *arg, int (*fn)(void *)) | ||
123 | { | ||
124 | do_exit(fn(arg)); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Create a kernel thread | ||
129 | */ | ||
130 | int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | ||
131 | { | ||
132 | struct pt_regs regs; | ||
133 | |||
134 | /* | ||
135 | * copy_thread sets a4 to zero (child return from fork) | ||
136 | * so we can't just set things up to directly return to | ||
137 | * fn. | ||
138 | */ | ||
139 | memset(®s, 0, sizeof(regs)); | ||
140 | regs.b4 = (unsigned long) arg; | ||
141 | regs.a6 = (unsigned long) fn; | ||
142 | regs.pc = (unsigned long) kernel_thread_helper; | ||
143 | local_save_flags(regs.csr); | ||
144 | regs.csr |= 1; | ||
145 | regs.tsr = 5; /* Set GEE and GIE in TSR */ | ||
146 | |||
147 | /* Ok, create the new process.. */ | ||
148 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, -1, ®s, | ||
149 | 0, NULL, NULL); | ||
150 | } | ||
151 | EXPORT_SYMBOL(kernel_thread); | ||
152 | |||
153 | void flush_thread(void) | ||
154 | { | ||
155 | } | ||
156 | |||
157 | void exit_thread(void) | ||
158 | { | ||
159 | } | ||
160 | |||
161 | SYSCALL_DEFINE1(c6x_clone, struct pt_regs *, regs) | ||
162 | { | ||
163 | unsigned long clone_flags; | ||
164 | unsigned long newsp; | ||
165 | |||
166 | /* syscall puts clone_flags in A4 and usp in B4 */ | ||
167 | clone_flags = regs->orig_a4; | ||
168 | if (regs->b4) | ||
169 | newsp = regs->b4; | ||
170 | else | ||
171 | newsp = regs->sp; | ||
172 | |||
173 | return do_fork(clone_flags, newsp, regs, 0, (int __user *)regs->a6, | ||
174 | (int __user *)regs->b6); | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * Do necessary setup to start up a newly executed thread. | ||
179 | */ | ||
180 | void start_thread(struct pt_regs *regs, unsigned int pc, unsigned long usp) | ||
181 | { | ||
182 | /* | ||
183 | * The binfmt loader will setup a "full" stack, but the C6X | ||
184 | * operates an "empty" stack. So we adjust the usp so that | ||
185 | * argc doesn't get destroyed if an interrupt is taken before | ||
186 | * it is read from the stack. | ||
187 | * | ||
188 | * NB: Library startup code needs to match this. | ||
189 | */ | ||
190 | usp -= 8; | ||
191 | |||
192 | set_fs(USER_DS); | ||
193 | regs->pc = pc; | ||
194 | regs->sp = usp; | ||
195 | regs->tsr |= 0x40; /* set user mode */ | ||
196 | current->thread.usp = usp; | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * Copy a new thread context in its stack. | ||
201 | */ | ||
202 | int copy_thread(unsigned long clone_flags, unsigned long usp, | ||
203 | unsigned long ustk_size, | ||
204 | struct task_struct *p, struct pt_regs *regs) | ||
205 | { | ||
206 | struct pt_regs *childregs; | ||
207 | |||
208 | childregs = task_pt_regs(p); | ||
209 | |||
210 | *childregs = *regs; | ||
211 | childregs->a4 = 0; | ||
212 | |||
213 | if (usp == -1) | ||
214 | /* case of __kernel_thread: we return to supervisor space */ | ||
215 | childregs->sp = (unsigned long)(childregs + 1); | ||
216 | else | ||
217 | /* Otherwise use the given stack */ | ||
218 | childregs->sp = usp; | ||
219 | |||
220 | /* Set usp/ksp */ | ||
221 | p->thread.usp = childregs->sp; | ||
222 | /* switch_to uses stack to save/restore 14 callee-saved regs */ | ||
223 | thread_saved_ksp(p) = (unsigned long)childregs - 8; | ||
224 | p->thread.pc = (unsigned int) ret_from_fork; | ||
225 | p->thread.wchan = (unsigned long) ret_from_fork; | ||
226 | #ifdef __DSBT__ | ||
227 | { | ||
228 | unsigned long dp; | ||
229 | |||
230 | asm volatile ("mv .S2 b14,%0\n" : "=b"(dp)); | ||
231 | |||
232 | thread_saved_dp(p) = dp; | ||
233 | if (usp == -1) | ||
234 | childregs->dp = dp; | ||
235 | } | ||
236 | #endif | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * c6x_execve() executes a new program. | ||
242 | */ | ||
243 | SYSCALL_DEFINE4(c6x_execve, const char __user *, name, | ||
244 | const char __user *const __user *, argv, | ||
245 | const char __user *const __user *, envp, | ||
246 | struct pt_regs *, regs) | ||
247 | { | ||
248 | int error; | ||
249 | char *filename; | ||
250 | |||
251 | filename = getname(name); | ||
252 | error = PTR_ERR(filename); | ||
253 | if (IS_ERR(filename)) | ||
254 | goto out; | ||
255 | |||
256 | error = do_execve(filename, argv, envp, regs); | ||
257 | putname(filename); | ||
258 | out: | ||
259 | return error; | ||
260 | } | ||
261 | |||
262 | unsigned long get_wchan(struct task_struct *p) | ||
263 | { | ||
264 | return p->thread.wchan; | ||
265 | } | ||
diff --git a/arch/c6x/kernel/ptrace.c b/arch/c6x/kernel/ptrace.c new file mode 100644 index 000000000000..3c494e84444d --- /dev/null +++ b/arch/c6x/kernel/ptrace.c | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Updated for 2.6.34: Mark Salter <msalter@redhat.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #include <linux/ptrace.h> | ||
14 | #include <linux/tracehook.h> | ||
15 | #include <linux/regset.h> | ||
16 | #include <linux/elf.h> | ||
17 | |||
18 | #include <asm/cacheflush.h> | ||
19 | |||
20 | #define PT_REG_SIZE (sizeof(struct pt_regs)) | ||
21 | |||
22 | /* | ||
23 | * Called by kernel/ptrace.c when detaching. | ||
24 | */ | ||
25 | void ptrace_disable(struct task_struct *child) | ||
26 | { | ||
27 | /* nothing to do */ | ||
28 | } | ||
29 | |||
30 | /* | ||
31 | * Get a register number from live pt_regs for the specified task. | ||
32 | */ | ||
33 | static inline long get_reg(struct task_struct *task, int regno) | ||
34 | { | ||
35 | long *addr = (long *)task_pt_regs(task); | ||
36 | |||
37 | if (regno == PT_TSR || regno == PT_CSR) | ||
38 | return 0; | ||
39 | |||
40 | return addr[regno]; | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | * Write contents of register REGNO in task TASK. | ||
45 | */ | ||
46 | static inline int put_reg(struct task_struct *task, | ||
47 | int regno, | ||
48 | unsigned long data) | ||
49 | { | ||
50 | unsigned long *addr = (unsigned long *)task_pt_regs(task); | ||
51 | |||
52 | if (regno != PT_TSR && regno != PT_CSR) | ||
53 | addr[regno] = data; | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | /* regset get/set implementations */ | ||
59 | |||
60 | static int gpr_get(struct task_struct *target, | ||
61 | const struct user_regset *regset, | ||
62 | unsigned int pos, unsigned int count, | ||
63 | void *kbuf, void __user *ubuf) | ||
64 | { | ||
65 | struct pt_regs *regs = task_pt_regs(target); | ||
66 | |||
67 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
68 | regs, | ||
69 | 0, sizeof(*regs)); | ||
70 | } | ||
71 | |||
72 | static int gpr_set(struct task_struct *target, | ||
73 | const struct user_regset *regset, | ||
74 | unsigned int pos, unsigned int count, | ||
75 | const void *kbuf, const void __user *ubuf) | ||
76 | { | ||
77 | int ret; | ||
78 | struct pt_regs *regs = task_pt_regs(target); | ||
79 | |||
80 | /* Don't copyin TSR or CSR */ | ||
81 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
82 | ®s, | ||
83 | 0, PT_TSR * sizeof(long)); | ||
84 | if (ret) | ||
85 | return ret; | ||
86 | |||
87 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
88 | PT_TSR * sizeof(long), | ||
89 | (PT_TSR + 1) * sizeof(long)); | ||
90 | if (ret) | ||
91 | return ret; | ||
92 | |||
93 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
94 | ®s, | ||
95 | (PT_TSR + 1) * sizeof(long), | ||
96 | PT_CSR * sizeof(long)); | ||
97 | if (ret) | ||
98 | return ret; | ||
99 | |||
100 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
101 | PT_CSR * sizeof(long), | ||
102 | (PT_CSR + 1) * sizeof(long)); | ||
103 | if (ret) | ||
104 | return ret; | ||
105 | |||
106 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
107 | ®s, | ||
108 | (PT_CSR + 1) * sizeof(long), -1); | ||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | enum c6x_regset { | ||
113 | REGSET_GPR, | ||
114 | }; | ||
115 | |||
116 | static const struct user_regset c6x_regsets[] = { | ||
117 | [REGSET_GPR] = { | ||
118 | .core_note_type = NT_PRSTATUS, | ||
119 | .n = ELF_NGREG, | ||
120 | .size = sizeof(u32), | ||
121 | .align = sizeof(u32), | ||
122 | .get = gpr_get, | ||
123 | .set = gpr_set | ||
124 | }, | ||
125 | }; | ||
126 | |||
127 | static const struct user_regset_view user_c6x_native_view = { | ||
128 | .name = "tic6x", | ||
129 | .e_machine = EM_TI_C6000, | ||
130 | .regsets = c6x_regsets, | ||
131 | .n = ARRAY_SIZE(c6x_regsets), | ||
132 | }; | ||
133 | |||
134 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
135 | { | ||
136 | return &user_c6x_native_view; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Perform ptrace request | ||
141 | */ | ||
142 | long arch_ptrace(struct task_struct *child, long request, | ||
143 | unsigned long addr, unsigned long data) | ||
144 | { | ||
145 | int ret = 0; | ||
146 | |||
147 | switch (request) { | ||
148 | /* | ||
149 | * write the word at location addr. | ||
150 | */ | ||
151 | case PTRACE_POKETEXT: | ||
152 | ret = generic_ptrace_pokedata(child, addr, data); | ||
153 | if (ret == 0 && request == PTRACE_POKETEXT) | ||
154 | flush_icache_range(addr, addr + 4); | ||
155 | break; | ||
156 | default: | ||
157 | ret = ptrace_request(child, request, addr, data); | ||
158 | break; | ||
159 | } | ||
160 | |||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * handle tracing of system call entry | ||
166 | * - return the revised system call number or ULONG_MAX to cause ENOSYS | ||
167 | */ | ||
168 | asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs) | ||
169 | { | ||
170 | if (tracehook_report_syscall_entry(regs)) | ||
171 | /* tracing decided this syscall should not happen, so | ||
172 | * We'll return a bogus call number to get an ENOSYS | ||
173 | * error, but leave the original number in | ||
174 | * regs->orig_a4 | ||
175 | */ | ||
176 | return ULONG_MAX; | ||
177 | |||
178 | return regs->b0; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * handle tracing of system call exit | ||
183 | */ | ||
184 | asmlinkage void syscall_trace_exit(struct pt_regs *regs) | ||
185 | { | ||
186 | tracehook_report_syscall_exit(regs, 0); | ||
187 | } | ||
diff --git a/arch/c6x/kernel/setup.c b/arch/c6x/kernel/setup.c new file mode 100644 index 000000000000..0c07921747f4 --- /dev/null +++ b/arch/c6x/kernel/setup.c | |||
@@ -0,0 +1,510 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/dma-mapping.h> | ||
12 | #include <linux/memblock.h> | ||
13 | #include <linux/seq_file.h> | ||
14 | #include <linux/bootmem.h> | ||
15 | #include <linux/clkdev.h> | ||
16 | #include <linux/initrd.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of_fdt.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/cache.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/clk.h> | ||
26 | #include <linux/cpu.h> | ||
27 | #include <linux/fs.h> | ||
28 | #include <linux/of.h> | ||
29 | |||
30 | |||
31 | #include <asm/sections.h> | ||
32 | #include <asm/div64.h> | ||
33 | #include <asm/setup.h> | ||
34 | #include <asm/dscr.h> | ||
35 | #include <asm/clock.h> | ||
36 | #include <asm/soc.h> | ||
37 | |||
38 | static const char *c6x_soc_name; | ||
39 | |||
40 | int c6x_num_cores; | ||
41 | EXPORT_SYMBOL_GPL(c6x_num_cores); | ||
42 | |||
43 | unsigned int c6x_silicon_rev; | ||
44 | EXPORT_SYMBOL_GPL(c6x_silicon_rev); | ||
45 | |||
46 | /* | ||
47 | * Device status register. This holds information | ||
48 | * about device configuration needed by some drivers. | ||
49 | */ | ||
50 | unsigned int c6x_devstat; | ||
51 | EXPORT_SYMBOL_GPL(c6x_devstat); | ||
52 | |||
53 | /* | ||
54 | * Some SoCs have fuse registers holding a unique MAC | ||
55 | * address. This is parsed out of the device tree with | ||
56 | * the resulting MAC being held here. | ||
57 | */ | ||
58 | unsigned char c6x_fuse_mac[6]; | ||
59 | |||
60 | unsigned long memory_start; | ||
61 | unsigned long memory_end; | ||
62 | |||
63 | unsigned long ram_start; | ||
64 | unsigned long ram_end; | ||
65 | |||
66 | /* Uncached memory for DMA consistent use (memdma=) */ | ||
67 | static unsigned long dma_start __initdata; | ||
68 | static unsigned long dma_size __initdata; | ||
69 | |||
70 | char c6x_command_line[COMMAND_LINE_SIZE]; | ||
71 | |||
72 | #if defined(CONFIG_CMDLINE_BOOL) | ||
73 | static const char default_command_line[COMMAND_LINE_SIZE] __section(.cmdline) = | ||
74 | CONFIG_CMDLINE; | ||
75 | #endif | ||
76 | |||
77 | struct cpuinfo_c6x { | ||
78 | const char *cpu_name; | ||
79 | const char *cpu_voltage; | ||
80 | const char *mmu; | ||
81 | const char *fpu; | ||
82 | char *cpu_rev; | ||
83 | unsigned int core_id; | ||
84 | char __cpu_rev[5]; | ||
85 | }; | ||
86 | |||
87 | static DEFINE_PER_CPU(struct cpuinfo_c6x, cpu_data); | ||
88 | |||
89 | unsigned int ticks_per_ns_scaled; | ||
90 | EXPORT_SYMBOL(ticks_per_ns_scaled); | ||
91 | |||
92 | unsigned int c6x_core_freq; | ||
93 | |||
94 | static void __init get_cpuinfo(void) | ||
95 | { | ||
96 | unsigned cpu_id, rev_id, csr; | ||
97 | struct clk *coreclk = clk_get_sys(NULL, "core"); | ||
98 | unsigned long core_khz; | ||
99 | u64 tmp; | ||
100 | struct cpuinfo_c6x *p; | ||
101 | struct device_node *node, *np; | ||
102 | |||
103 | p = &per_cpu(cpu_data, smp_processor_id()); | ||
104 | |||
105 | if (!IS_ERR(coreclk)) | ||
106 | c6x_core_freq = clk_get_rate(coreclk); | ||
107 | else { | ||
108 | printk(KERN_WARNING | ||
109 | "Cannot find core clock frequency. Using 700MHz\n"); | ||
110 | c6x_core_freq = 700000000; | ||
111 | } | ||
112 | |||
113 | core_khz = c6x_core_freq / 1000; | ||
114 | |||
115 | tmp = (uint64_t)core_khz << C6X_NDELAY_SCALE; | ||
116 | do_div(tmp, 1000000); | ||
117 | ticks_per_ns_scaled = tmp; | ||
118 | |||
119 | csr = get_creg(CSR); | ||
120 | cpu_id = csr >> 24; | ||
121 | rev_id = (csr >> 16) & 0xff; | ||
122 | |||
123 | p->mmu = "none"; | ||
124 | p->fpu = "none"; | ||
125 | p->cpu_voltage = "unknown"; | ||
126 | |||
127 | switch (cpu_id) { | ||
128 | case 0: | ||
129 | p->cpu_name = "C67x"; | ||
130 | p->fpu = "yes"; | ||
131 | break; | ||
132 | case 2: | ||
133 | p->cpu_name = "C62x"; | ||
134 | break; | ||
135 | case 8: | ||
136 | p->cpu_name = "C64x"; | ||
137 | break; | ||
138 | case 12: | ||
139 | p->cpu_name = "C64x"; | ||
140 | break; | ||
141 | case 16: | ||
142 | p->cpu_name = "C64x+"; | ||
143 | p->cpu_voltage = "1.2"; | ||
144 | break; | ||
145 | default: | ||
146 | p->cpu_name = "unknown"; | ||
147 | break; | ||
148 | } | ||
149 | |||
150 | if (cpu_id < 16) { | ||
151 | switch (rev_id) { | ||
152 | case 0x1: | ||
153 | if (cpu_id > 8) { | ||
154 | p->cpu_rev = "DM640/DM641/DM642/DM643"; | ||
155 | p->cpu_voltage = "1.2 - 1.4"; | ||
156 | } else { | ||
157 | p->cpu_rev = "C6201"; | ||
158 | p->cpu_voltage = "2.5"; | ||
159 | } | ||
160 | break; | ||
161 | case 0x2: | ||
162 | p->cpu_rev = "C6201B/C6202/C6211"; | ||
163 | p->cpu_voltage = "1.8"; | ||
164 | break; | ||
165 | case 0x3: | ||
166 | p->cpu_rev = "C6202B/C6203/C6204/C6205"; | ||
167 | p->cpu_voltage = "1.5"; | ||
168 | break; | ||
169 | case 0x201: | ||
170 | p->cpu_rev = "C6701 revision 0 (early CPU)"; | ||
171 | p->cpu_voltage = "1.8"; | ||
172 | break; | ||
173 | case 0x202: | ||
174 | p->cpu_rev = "C6701/C6711/C6712"; | ||
175 | p->cpu_voltage = "1.8"; | ||
176 | break; | ||
177 | case 0x801: | ||
178 | p->cpu_rev = "C64x"; | ||
179 | p->cpu_voltage = "1.5"; | ||
180 | break; | ||
181 | default: | ||
182 | p->cpu_rev = "unknown"; | ||
183 | } | ||
184 | } else { | ||
185 | p->cpu_rev = p->__cpu_rev; | ||
186 | snprintf(p->__cpu_rev, sizeof(p->__cpu_rev), "0x%x", cpu_id); | ||
187 | } | ||
188 | |||
189 | p->core_id = get_coreid(); | ||
190 | |||
191 | node = of_find_node_by_name(NULL, "cpus"); | ||
192 | if (node) { | ||
193 | for_each_child_of_node(node, np) | ||
194 | if (!strcmp("cpu", np->name)) | ||
195 | ++c6x_num_cores; | ||
196 | of_node_put(node); | ||
197 | } | ||
198 | |||
199 | node = of_find_node_by_name(NULL, "soc"); | ||
200 | if (node) { | ||
201 | if (of_property_read_string(node, "model", &c6x_soc_name)) | ||
202 | c6x_soc_name = "unknown"; | ||
203 | of_node_put(node); | ||
204 | } else | ||
205 | c6x_soc_name = "unknown"; | ||
206 | |||
207 | printk(KERN_INFO "CPU%d: %s rev %s, %s volts, %uMHz\n", | ||
208 | p->core_id, p->cpu_name, p->cpu_rev, | ||
209 | p->cpu_voltage, c6x_core_freq / 1000000); | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * Early parsing of the command line | ||
214 | */ | ||
215 | static u32 mem_size __initdata; | ||
216 | |||
217 | /* "mem=" parsing. */ | ||
218 | static int __init early_mem(char *p) | ||
219 | { | ||
220 | if (!p) | ||
221 | return -EINVAL; | ||
222 | |||
223 | mem_size = memparse(p, &p); | ||
224 | /* don't remove all of memory when handling "mem={invalid}" */ | ||
225 | if (mem_size == 0) | ||
226 | return -EINVAL; | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | early_param("mem", early_mem); | ||
231 | |||
232 | /* "memdma=<size>[@<address>]" parsing. */ | ||
233 | static int __init early_memdma(char *p) | ||
234 | { | ||
235 | if (!p) | ||
236 | return -EINVAL; | ||
237 | |||
238 | dma_size = memparse(p, &p); | ||
239 | if (*p == '@') | ||
240 | dma_start = memparse(p, &p); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | early_param("memdma", early_memdma); | ||
245 | |||
246 | int __init c6x_add_memory(phys_addr_t start, unsigned long size) | ||
247 | { | ||
248 | static int ram_found __initdata; | ||
249 | |||
250 | /* We only handle one bank (the one with PAGE_OFFSET) for now */ | ||
251 | if (ram_found) | ||
252 | return -EINVAL; | ||
253 | |||
254 | if (start > PAGE_OFFSET || PAGE_OFFSET >= (start + size)) | ||
255 | return 0; | ||
256 | |||
257 | ram_start = start; | ||
258 | ram_end = start + size; | ||
259 | |||
260 | ram_found = 1; | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Do early machine setup and device tree parsing. This is called very | ||
266 | * early on the boot process. | ||
267 | */ | ||
268 | notrace void __init machine_init(unsigned long dt_ptr) | ||
269 | { | ||
270 | struct boot_param_header *dtb = __va(dt_ptr); | ||
271 | struct boot_param_header *fdt = (struct boot_param_header *)_fdt_start; | ||
272 | |||
273 | /* interrupts must be masked */ | ||
274 | set_creg(IER, 2); | ||
275 | |||
276 | /* | ||
277 | * Set the Interrupt Service Table (IST) to the beginning of the | ||
278 | * vector table. | ||
279 | */ | ||
280 | set_ist(_vectors_start); | ||
281 | |||
282 | lockdep_init(); | ||
283 | |||
284 | /* | ||
285 | * dtb is passed in from bootloader. | ||
286 | * fdt is linked in blob. | ||
287 | */ | ||
288 | if (dtb && dtb != fdt) | ||
289 | fdt = dtb; | ||
290 | |||
291 | /* Do some early initialization based on the flat device tree */ | ||
292 | early_init_devtree(fdt); | ||
293 | |||
294 | /* parse_early_param needs a boot_command_line */ | ||
295 | strlcpy(boot_command_line, c6x_command_line, COMMAND_LINE_SIZE); | ||
296 | parse_early_param(); | ||
297 | } | ||
298 | |||
299 | void __init setup_arch(char **cmdline_p) | ||
300 | { | ||
301 | int bootmap_size; | ||
302 | struct memblock_region *reg; | ||
303 | |||
304 | printk(KERN_INFO "Initializing kernel\n"); | ||
305 | |||
306 | /* Initialize command line */ | ||
307 | *cmdline_p = c6x_command_line; | ||
308 | |||
309 | memory_end = ram_end; | ||
310 | memory_end &= ~(PAGE_SIZE - 1); | ||
311 | |||
312 | if (mem_size && (PAGE_OFFSET + PAGE_ALIGN(mem_size)) < memory_end) | ||
313 | memory_end = PAGE_OFFSET + PAGE_ALIGN(mem_size); | ||
314 | |||
315 | /* add block that this kernel can use */ | ||
316 | memblock_add(PAGE_OFFSET, memory_end - PAGE_OFFSET); | ||
317 | |||
318 | /* reserve kernel text/data/bss */ | ||
319 | memblock_reserve(PAGE_OFFSET, | ||
320 | PAGE_ALIGN((unsigned long)&_end - PAGE_OFFSET)); | ||
321 | |||
322 | if (dma_size) { | ||
323 | /* align to cacheability granularity */ | ||
324 | dma_size = CACHE_REGION_END(dma_size); | ||
325 | |||
326 | if (!dma_start) | ||
327 | dma_start = memory_end - dma_size; | ||
328 | |||
329 | /* align to cacheability granularity */ | ||
330 | dma_start = CACHE_REGION_START(dma_start); | ||
331 | |||
332 | /* reserve DMA memory taken from kernel memory */ | ||
333 | if (memblock_is_region_memory(dma_start, dma_size)) | ||
334 | memblock_reserve(dma_start, dma_size); | ||
335 | } | ||
336 | |||
337 | memory_start = PAGE_ALIGN((unsigned int) &_end); | ||
338 | |||
339 | printk(KERN_INFO "Memory Start=%08lx, Memory End=%08lx\n", | ||
340 | memory_start, memory_end); | ||
341 | |||
342 | #ifdef CONFIG_BLK_DEV_INITRD | ||
343 | /* | ||
344 | * Reserve initrd memory if in kernel memory. | ||
345 | */ | ||
346 | if (initrd_start < initrd_end) | ||
347 | if (memblock_is_region_memory(initrd_start, | ||
348 | initrd_end - initrd_start)) | ||
349 | memblock_reserve(initrd_start, | ||
350 | initrd_end - initrd_start); | ||
351 | #endif | ||
352 | |||
353 | init_mm.start_code = (unsigned long) &_stext; | ||
354 | init_mm.end_code = (unsigned long) &_etext; | ||
355 | init_mm.end_data = memory_start; | ||
356 | init_mm.brk = memory_start; | ||
357 | |||
358 | /* | ||
359 | * Give all the memory to the bootmap allocator, tell it to put the | ||
360 | * boot mem_map at the start of memory | ||
361 | */ | ||
362 | bootmap_size = init_bootmem_node(NODE_DATA(0), | ||
363 | memory_start >> PAGE_SHIFT, | ||
364 | PAGE_OFFSET >> PAGE_SHIFT, | ||
365 | memory_end >> PAGE_SHIFT); | ||
366 | memblock_reserve(memory_start, bootmap_size); | ||
367 | |||
368 | unflatten_device_tree(); | ||
369 | |||
370 | c6x_cache_init(); | ||
371 | |||
372 | /* Set the whole external memory as non-cacheable */ | ||
373 | disable_caching(ram_start, ram_end - 1); | ||
374 | |||
375 | /* Set caching of external RAM used by Linux */ | ||
376 | for_each_memblock(memory, reg) | ||
377 | enable_caching(CACHE_REGION_START(reg->base), | ||
378 | CACHE_REGION_START(reg->base + reg->size - 1)); | ||
379 | |||
380 | #ifdef CONFIG_BLK_DEV_INITRD | ||
381 | /* | ||
382 | * Enable caching for initrd which falls outside kernel memory. | ||
383 | */ | ||
384 | if (initrd_start < initrd_end) { | ||
385 | if (!memblock_is_region_memory(initrd_start, | ||
386 | initrd_end - initrd_start)) | ||
387 | enable_caching(CACHE_REGION_START(initrd_start), | ||
388 | CACHE_REGION_START(initrd_end - 1)); | ||
389 | } | ||
390 | #endif | ||
391 | |||
392 | /* | ||
393 | * Disable caching for dma coherent memory taken from kernel memory. | ||
394 | */ | ||
395 | if (dma_size && memblock_is_region_memory(dma_start, dma_size)) | ||
396 | disable_caching(dma_start, | ||
397 | CACHE_REGION_START(dma_start + dma_size - 1)); | ||
398 | |||
399 | /* Initialize the coherent memory allocator */ | ||
400 | coherent_mem_init(dma_start, dma_size); | ||
401 | |||
402 | /* | ||
403 | * Free all memory as a starting point. | ||
404 | */ | ||
405 | free_bootmem(PAGE_OFFSET, memory_end - PAGE_OFFSET); | ||
406 | |||
407 | /* | ||
408 | * Then reserve memory which is already being used. | ||
409 | */ | ||
410 | for_each_memblock(reserved, reg) { | ||
411 | pr_debug("reserved - 0x%08x-0x%08x\n", | ||
412 | (u32) reg->base, (u32) reg->size); | ||
413 | reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT); | ||
414 | } | ||
415 | |||
416 | max_low_pfn = PFN_DOWN(memory_end); | ||
417 | min_low_pfn = PFN_UP(memory_start); | ||
418 | max_mapnr = max_low_pfn - min_low_pfn; | ||
419 | |||
420 | /* Get kmalloc into gear */ | ||
421 | paging_init(); | ||
422 | |||
423 | /* | ||
424 | * Probe for Device State Configuration Registers. | ||
425 | * We have to do this early in case timer needs to be enabled | ||
426 | * through DSCR. | ||
427 | */ | ||
428 | dscr_probe(); | ||
429 | |||
430 | /* We do this early for timer and core clock frequency */ | ||
431 | c64x_setup_clocks(); | ||
432 | |||
433 | /* Get CPU info */ | ||
434 | get_cpuinfo(); | ||
435 | |||
436 | #if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) | ||
437 | conswitchp = &dummy_con; | ||
438 | #endif | ||
439 | } | ||
440 | |||
441 | #define cpu_to_ptr(n) ((void *)((long)(n)+1)) | ||
442 | #define ptr_to_cpu(p) ((long)(p) - 1) | ||
443 | |||
444 | static int show_cpuinfo(struct seq_file *m, void *v) | ||
445 | { | ||
446 | int n = ptr_to_cpu(v); | ||
447 | struct cpuinfo_c6x *p = &per_cpu(cpu_data, n); | ||
448 | |||
449 | if (n == 0) { | ||
450 | seq_printf(m, | ||
451 | "soc\t\t: %s\n" | ||
452 | "soc revision\t: 0x%x\n" | ||
453 | "soc cores\t: %d\n", | ||
454 | c6x_soc_name, c6x_silicon_rev, c6x_num_cores); | ||
455 | } | ||
456 | |||
457 | seq_printf(m, | ||
458 | "\n" | ||
459 | "processor\t: %d\n" | ||
460 | "cpu\t\t: %s\n" | ||
461 | "core revision\t: %s\n" | ||
462 | "core voltage\t: %s\n" | ||
463 | "core id\t\t: %d\n" | ||
464 | "mmu\t\t: %s\n" | ||
465 | "fpu\t\t: %s\n" | ||
466 | "cpu MHz\t\t: %u\n" | ||
467 | "bogomips\t: %lu.%02lu\n\n", | ||
468 | n, | ||
469 | p->cpu_name, p->cpu_rev, p->cpu_voltage, | ||
470 | p->core_id, p->mmu, p->fpu, | ||
471 | (c6x_core_freq + 500000) / 1000000, | ||
472 | (loops_per_jiffy/(500000/HZ)), | ||
473 | (loops_per_jiffy/(5000/HZ))%100); | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static void *c_start(struct seq_file *m, loff_t *pos) | ||
479 | { | ||
480 | return *pos < nr_cpu_ids ? cpu_to_ptr(*pos) : NULL; | ||
481 | } | ||
482 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) | ||
483 | { | ||
484 | ++*pos; | ||
485 | return NULL; | ||
486 | } | ||
487 | static void c_stop(struct seq_file *m, void *v) | ||
488 | { | ||
489 | } | ||
490 | |||
491 | const struct seq_operations cpuinfo_op = { | ||
492 | c_start, | ||
493 | c_stop, | ||
494 | c_next, | ||
495 | show_cpuinfo | ||
496 | }; | ||
497 | |||
498 | static struct cpu cpu_devices[NR_CPUS]; | ||
499 | |||
500 | static int __init topology_init(void) | ||
501 | { | ||
502 | int i; | ||
503 | |||
504 | for_each_present_cpu(i) | ||
505 | register_cpu(&cpu_devices[i], i); | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | subsys_initcall(topology_init); | ||
diff --git a/arch/c6x/kernel/signal.c b/arch/c6x/kernel/signal.c new file mode 100644 index 000000000000..304f675826e9 --- /dev/null +++ b/arch/c6x/kernel/signal.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * Updated for 2.6.34: Mark Salter <msalter@redhat.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/uaccess.h> | ||
16 | #include <linux/syscalls.h> | ||
17 | #include <linux/tracehook.h> | ||
18 | |||
19 | #include <asm/ucontext.h> | ||
20 | #include <asm/cacheflush.h> | ||
21 | |||
22 | |||
23 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
24 | |||
25 | /* | ||
26 | * Do a signal return, undo the signal stack. | ||
27 | */ | ||
28 | |||
29 | #define RETCODE_SIZE (9 << 2) /* 9 instructions = 36 bytes */ | ||
30 | |||
31 | struct rt_sigframe { | ||
32 | struct siginfo __user *pinfo; | ||
33 | void __user *puc; | ||
34 | struct siginfo info; | ||
35 | struct ucontext uc; | ||
36 | unsigned long retcode[RETCODE_SIZE >> 2]; | ||
37 | }; | ||
38 | |||
39 | static int restore_sigcontext(struct pt_regs *regs, | ||
40 | struct sigcontext __user *sc) | ||
41 | { | ||
42 | int err = 0; | ||
43 | |||
44 | /* The access_ok check was done by caller, so use __get_user here */ | ||
45 | #define COPY(x) (err |= __get_user(regs->x, &sc->sc_##x)) | ||
46 | |||
47 | COPY(sp); COPY(a4); COPY(b4); COPY(a6); COPY(b6); COPY(a8); COPY(b8); | ||
48 | COPY(a0); COPY(a1); COPY(a2); COPY(a3); COPY(a5); COPY(a7); COPY(a9); | ||
49 | COPY(b0); COPY(b1); COPY(b2); COPY(b3); COPY(b5); COPY(b7); COPY(b9); | ||
50 | |||
51 | COPY(a16); COPY(a17); COPY(a18); COPY(a19); | ||
52 | COPY(a20); COPY(a21); COPY(a22); COPY(a23); | ||
53 | COPY(a24); COPY(a25); COPY(a26); COPY(a27); | ||
54 | COPY(a28); COPY(a29); COPY(a30); COPY(a31); | ||
55 | COPY(b16); COPY(b17); COPY(b18); COPY(b19); | ||
56 | COPY(b20); COPY(b21); COPY(b22); COPY(b23); | ||
57 | COPY(b24); COPY(b25); COPY(b26); COPY(b27); | ||
58 | COPY(b28); COPY(b29); COPY(b30); COPY(b31); | ||
59 | |||
60 | COPY(csr); COPY(pc); | ||
61 | |||
62 | #undef COPY | ||
63 | |||
64 | return err; | ||
65 | } | ||
66 | |||
67 | asmlinkage int do_rt_sigreturn(struct pt_regs *regs) | ||
68 | { | ||
69 | struct rt_sigframe __user *frame; | ||
70 | sigset_t set; | ||
71 | |||
72 | /* | ||
73 | * Since we stacked the signal on a dword boundary, | ||
74 | * 'sp' should be dword aligned here. If it's | ||
75 | * not, then the user is trying to mess with us. | ||
76 | */ | ||
77 | if (regs->sp & 7) | ||
78 | goto badframe; | ||
79 | |||
80 | frame = (struct rt_sigframe __user *) ((unsigned long) regs->sp + 8); | ||
81 | |||
82 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
83 | goto badframe; | ||
84 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
85 | goto badframe; | ||
86 | |||
87 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
88 | spin_lock_irq(¤t->sighand->siglock); | ||
89 | current->blocked = set; | ||
90 | recalc_sigpending(); | ||
91 | spin_unlock_irq(¤t->sighand->siglock); | ||
92 | |||
93 | if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) | ||
94 | goto badframe; | ||
95 | |||
96 | return regs->a4; | ||
97 | |||
98 | badframe: | ||
99 | force_sig(SIGSEGV, current); | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | ||
104 | unsigned long mask) | ||
105 | { | ||
106 | int err = 0; | ||
107 | |||
108 | err |= __put_user(mask, &sc->sc_mask); | ||
109 | |||
110 | /* The access_ok check was done by caller, so use __put_user here */ | ||
111 | #define COPY(x) (err |= __put_user(regs->x, &sc->sc_##x)) | ||
112 | |||
113 | COPY(sp); COPY(a4); COPY(b4); COPY(a6); COPY(b6); COPY(a8); COPY(b8); | ||
114 | COPY(a0); COPY(a1); COPY(a2); COPY(a3); COPY(a5); COPY(a7); COPY(a9); | ||
115 | COPY(b0); COPY(b1); COPY(b2); COPY(b3); COPY(b5); COPY(b7); COPY(b9); | ||
116 | |||
117 | COPY(a16); COPY(a17); COPY(a18); COPY(a19); | ||
118 | COPY(a20); COPY(a21); COPY(a22); COPY(a23); | ||
119 | COPY(a24); COPY(a25); COPY(a26); COPY(a27); | ||
120 | COPY(a28); COPY(a29); COPY(a30); COPY(a31); | ||
121 | COPY(b16); COPY(b17); COPY(b18); COPY(b19); | ||
122 | COPY(b20); COPY(b21); COPY(b22); COPY(b23); | ||
123 | COPY(b24); COPY(b25); COPY(b26); COPY(b27); | ||
124 | COPY(b28); COPY(b29); COPY(b30); COPY(b31); | ||
125 | |||
126 | COPY(csr); COPY(pc); | ||
127 | |||
128 | #undef COPY | ||
129 | |||
130 | return err; | ||
131 | } | ||
132 | |||
133 | static inline void __user *get_sigframe(struct k_sigaction *ka, | ||
134 | struct pt_regs *regs, | ||
135 | unsigned long framesize) | ||
136 | { | ||
137 | unsigned long sp = regs->sp; | ||
138 | |||
139 | /* | ||
140 | * This is the X/Open sanctioned signal stack switching. | ||
141 | */ | ||
142 | if ((ka->sa.sa_flags & SA_ONSTACK) && sas_ss_flags(sp) == 0) | ||
143 | sp = current->sas_ss_sp + current->sas_ss_size; | ||
144 | |||
145 | /* | ||
146 | * No matter what happens, 'sp' must be dword | ||
147 | * aligned. Otherwise, nasty things will happen | ||
148 | */ | ||
149 | return (void __user *)((sp - framesize) & ~7); | ||
150 | } | ||
151 | |||
152 | static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info, | ||
153 | sigset_t *set, struct pt_regs *regs) | ||
154 | { | ||
155 | struct rt_sigframe __user *frame; | ||
156 | unsigned long __user *retcode; | ||
157 | int err = 0; | ||
158 | |||
159 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
160 | |||
161 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
162 | goto segv_and_exit; | ||
163 | |||
164 | err |= __put_user(&frame->info, &frame->pinfo); | ||
165 | err |= __put_user(&frame->uc, &frame->puc); | ||
166 | err |= copy_siginfo_to_user(&frame->info, info); | ||
167 | |||
168 | /* Clear all the bits of the ucontext we don't use. */ | ||
169 | err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); | ||
170 | |||
171 | err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); | ||
172 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
173 | |||
174 | /* Set up to return from userspace */ | ||
175 | retcode = (unsigned long __user *) &frame->retcode; | ||
176 | |||
177 | /* The access_ok check was done above, so use __put_user here */ | ||
178 | #define COPY(x) (err |= __put_user(x, retcode++)) | ||
179 | |||
180 | COPY(0x0000002AUL | (__NR_rt_sigreturn << 7)); | ||
181 | /* MVK __NR_rt_sigreturn,B0 */ | ||
182 | COPY(0x10000000UL); /* SWE */ | ||
183 | COPY(0x00006000UL); /* NOP 4 */ | ||
184 | COPY(0x00006000UL); /* NOP 4 */ | ||
185 | COPY(0x00006000UL); /* NOP 4 */ | ||
186 | COPY(0x00006000UL); /* NOP 4 */ | ||
187 | COPY(0x00006000UL); /* NOP 4 */ | ||
188 | COPY(0x00006000UL); /* NOP 4 */ | ||
189 | COPY(0x00006000UL); /* NOP 4 */ | ||
190 | |||
191 | #undef COPY | ||
192 | |||
193 | if (err) | ||
194 | goto segv_and_exit; | ||
195 | |||
196 | flush_icache_range((unsigned long) &frame->retcode, | ||
197 | (unsigned long) &frame->retcode + RETCODE_SIZE); | ||
198 | |||
199 | retcode = (unsigned long __user *) &frame->retcode; | ||
200 | |||
201 | /* Change user context to branch to signal handler */ | ||
202 | regs->sp = (unsigned long) frame - 8; | ||
203 | regs->b3 = (unsigned long) retcode; | ||
204 | regs->pc = (unsigned long) ka->sa.sa_handler; | ||
205 | |||
206 | /* Give the signal number to the handler */ | ||
207 | regs->a4 = signr; | ||
208 | |||
209 | /* | ||
210 | * For realtime signals we must also set the second and third | ||
211 | * arguments for the signal handler. | ||
212 | * -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06 | ||
213 | */ | ||
214 | regs->b4 = (unsigned long)&frame->info; | ||
215 | regs->a6 = (unsigned long)&frame->uc; | ||
216 | |||
217 | return 0; | ||
218 | |||
219 | segv_and_exit: | ||
220 | force_sigsegv(signr, current); | ||
221 | return -EFAULT; | ||
222 | } | ||
223 | |||
224 | static inline void | ||
225 | handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) | ||
226 | { | ||
227 | switch (regs->a4) { | ||
228 | case -ERESTARTNOHAND: | ||
229 | if (!has_handler) | ||
230 | goto do_restart; | ||
231 | regs->a4 = -EINTR; | ||
232 | break; | ||
233 | |||
234 | case -ERESTARTSYS: | ||
235 | if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { | ||
236 | regs->a4 = -EINTR; | ||
237 | break; | ||
238 | } | ||
239 | /* fallthrough */ | ||
240 | case -ERESTARTNOINTR: | ||
241 | do_restart: | ||
242 | regs->a4 = regs->orig_a4; | ||
243 | regs->pc -= 4; | ||
244 | break; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | /* | ||
249 | * handle the actual delivery of a signal to userspace | ||
250 | */ | ||
251 | static int handle_signal(int sig, | ||
252 | siginfo_t *info, struct k_sigaction *ka, | ||
253 | sigset_t *oldset, struct pt_regs *regs, | ||
254 | int syscall) | ||
255 | { | ||
256 | int ret; | ||
257 | |||
258 | /* Are we from a system call? */ | ||
259 | if (syscall) { | ||
260 | /* If so, check system call restarting.. */ | ||
261 | switch (regs->a4) { | ||
262 | case -ERESTART_RESTARTBLOCK: | ||
263 | case -ERESTARTNOHAND: | ||
264 | regs->a4 = -EINTR; | ||
265 | break; | ||
266 | |||
267 | case -ERESTARTSYS: | ||
268 | if (!(ka->sa.sa_flags & SA_RESTART)) { | ||
269 | regs->a4 = -EINTR; | ||
270 | break; | ||
271 | } | ||
272 | |||
273 | /* fallthrough */ | ||
274 | case -ERESTARTNOINTR: | ||
275 | regs->a4 = regs->orig_a4; | ||
276 | regs->pc -= 4; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /* Set up the stack frame */ | ||
281 | ret = setup_rt_frame(sig, ka, info, oldset, regs); | ||
282 | if (ret == 0) { | ||
283 | spin_lock_irq(¤t->sighand->siglock); | ||
284 | sigorsets(¤t->blocked, ¤t->blocked, | ||
285 | &ka->sa.sa_mask); | ||
286 | if (!(ka->sa.sa_flags & SA_NODEFER)) | ||
287 | sigaddset(¤t->blocked, sig); | ||
288 | recalc_sigpending(); | ||
289 | spin_unlock_irq(¤t->sighand->siglock); | ||
290 | } | ||
291 | |||
292 | return ret; | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * handle a potential signal | ||
297 | */ | ||
298 | static void do_signal(struct pt_regs *regs, int syscall) | ||
299 | { | ||
300 | struct k_sigaction ka; | ||
301 | siginfo_t info; | ||
302 | sigset_t *oldset; | ||
303 | int signr; | ||
304 | |||
305 | /* we want the common case to go fast, which is why we may in certain | ||
306 | * cases get here from kernel mode */ | ||
307 | if (!user_mode(regs)) | ||
308 | return; | ||
309 | |||
310 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
311 | oldset = ¤t->saved_sigmask; | ||
312 | else | ||
313 | oldset = ¤t->blocked; | ||
314 | |||
315 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
316 | if (signr > 0) { | ||
317 | if (handle_signal(signr, &info, &ka, oldset, | ||
318 | regs, syscall) == 0) { | ||
319 | /* a signal was successfully delivered; the saved | ||
320 | * sigmask will have been stored in the signal frame, | ||
321 | * and will be restored by sigreturn, so we can simply | ||
322 | * clear the TIF_RESTORE_SIGMASK flag */ | ||
323 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
324 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
325 | |||
326 | tracehook_signal_handler(signr, &info, &ka, regs, 0); | ||
327 | } | ||
328 | |||
329 | return; | ||
330 | } | ||
331 | |||
332 | /* did we come from a system call? */ | ||
333 | if (syscall) { | ||
334 | /* restart the system call - no handlers present */ | ||
335 | switch (regs->a4) { | ||
336 | case -ERESTARTNOHAND: | ||
337 | case -ERESTARTSYS: | ||
338 | case -ERESTARTNOINTR: | ||
339 | regs->a4 = regs->orig_a4; | ||
340 | regs->pc -= 4; | ||
341 | break; | ||
342 | |||
343 | case -ERESTART_RESTARTBLOCK: | ||
344 | regs->a4 = regs->orig_a4; | ||
345 | regs->b0 = __NR_restart_syscall; | ||
346 | regs->pc -= 4; | ||
347 | break; | ||
348 | } | ||
349 | } | ||
350 | |||
351 | /* if there's no signal to deliver, we just put the saved sigmask | ||
352 | * back */ | ||
353 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
354 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
355 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * notification of userspace execution resumption | ||
361 | * - triggered by current->work.notify_resume | ||
362 | */ | ||
363 | asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags, | ||
364 | int syscall) | ||
365 | { | ||
366 | /* deal with pending signal delivery */ | ||
367 | if (thread_info_flags & ((1 << TIF_SIGPENDING) | | ||
368 | (1 << TIF_RESTORE_SIGMASK))) | ||
369 | do_signal(regs, syscall); | ||
370 | |||
371 | if (thread_info_flags & (1 << TIF_NOTIFY_RESUME)) { | ||
372 | clear_thread_flag(TIF_NOTIFY_RESUME); | ||
373 | tracehook_notify_resume(regs); | ||
374 | if (current->replacement_session_keyring) | ||
375 | key_replace_session_keyring(); | ||
376 | } | ||
377 | } | ||
diff --git a/arch/c6x/kernel/soc.c b/arch/c6x/kernel/soc.c new file mode 100644 index 000000000000..dd45bc39af0e --- /dev/null +++ b/arch/c6x/kernel/soc.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Miscellaneous SoC-specific hooks. | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
5 | * Author: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/ctype.h> | ||
13 | #include <linux/etherdevice.h> | ||
14 | #include <asm/system.h> | ||
15 | #include <asm/setup.h> | ||
16 | #include <asm/soc.h> | ||
17 | |||
18 | struct soc_ops soc_ops; | ||
19 | |||
20 | int soc_get_exception(void) | ||
21 | { | ||
22 | if (!soc_ops.get_exception) | ||
23 | return -1; | ||
24 | return soc_ops.get_exception(); | ||
25 | } | ||
26 | |||
27 | void soc_assert_event(unsigned int evt) | ||
28 | { | ||
29 | if (soc_ops.assert_event) | ||
30 | soc_ops.assert_event(evt); | ||
31 | } | ||
32 | |||
33 | static u8 cmdline_mac[6]; | ||
34 | |||
35 | static int __init get_mac_addr_from_cmdline(char *str) | ||
36 | { | ||
37 | int count, i, val; | ||
38 | |||
39 | for (count = 0; count < 6 && *str; count++, str += 3) { | ||
40 | if (!isxdigit(str[0]) || !isxdigit(str[1])) | ||
41 | return 0; | ||
42 | if (str[2] != ((count < 5) ? ':' : '\0')) | ||
43 | return 0; | ||
44 | |||
45 | for (i = 0, val = 0; i < 2; i++) { | ||
46 | val = val << 4; | ||
47 | val |= isdigit(str[i]) ? | ||
48 | str[i] - '0' : toupper(str[i]) - 'A' + 10; | ||
49 | } | ||
50 | cmdline_mac[count] = val; | ||
51 | } | ||
52 | return 1; | ||
53 | } | ||
54 | __setup("emac_addr=", get_mac_addr_from_cmdline); | ||
55 | |||
56 | /* | ||
57 | * Setup the MAC address for SoC ethernet devices. | ||
58 | * | ||
59 | * Before calling this function, the ethernet driver will have | ||
60 | * initialized the addr with local-mac-address from the device | ||
61 | * tree (if found). Allow command line to override, but not | ||
62 | * the fused address. | ||
63 | */ | ||
64 | int soc_mac_addr(unsigned int index, u8 *addr) | ||
65 | { | ||
66 | int i, have_dt_mac = 0, have_cmdline_mac = 0, have_fuse_mac = 0; | ||
67 | |||
68 | for (i = 0; i < 6; i++) { | ||
69 | if (cmdline_mac[i]) | ||
70 | have_cmdline_mac = 1; | ||
71 | if (c6x_fuse_mac[i]) | ||
72 | have_fuse_mac = 1; | ||
73 | if (addr[i]) | ||
74 | have_dt_mac = 1; | ||
75 | } | ||
76 | |||
77 | /* cmdline overrides all */ | ||
78 | if (have_cmdline_mac) | ||
79 | memcpy(addr, cmdline_mac, 6); | ||
80 | else if (!have_dt_mac) { | ||
81 | if (have_fuse_mac) | ||
82 | memcpy(addr, c6x_fuse_mac, 6); | ||
83 | else | ||
84 | random_ether_addr(addr); | ||
85 | } | ||
86 | |||
87 | /* adjust for specific EMAC device */ | ||
88 | addr[5] += index * c6x_num_cores; | ||
89 | return 1; | ||
90 | } | ||
91 | EXPORT_SYMBOL_GPL(soc_mac_addr); | ||
diff --git a/arch/c6x/kernel/switch_to.S b/arch/c6x/kernel/switch_to.S new file mode 100644 index 000000000000..09177ed0fa5c --- /dev/null +++ b/arch/c6x/kernel/switch_to.S | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter (msalter@redhat.com) | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/linkage.h> | ||
11 | #include <asm/asm-offsets.h> | ||
12 | |||
13 | #define SP B15 | ||
14 | |||
15 | /* | ||
16 | * void __switch_to(struct thread_info *prev, | ||
17 | * struct thread_info *next, | ||
18 | * struct task_struct *tsk) ; | ||
19 | */ | ||
20 | ENTRY(__switch_to) | ||
21 | LDDW .D2T2 *+B4(THREAD_B15_14),B7:B6 | ||
22 | || MV .L2X A4,B5 ; prev | ||
23 | || MV .L1X B4,A5 ; next | ||
24 | || MVC .S2 RILC,B1 | ||
25 | |||
26 | STW .D2T2 B3,*+B5(THREAD_PC) | ||
27 | || STDW .D1T1 A13:A12,*+A4(THREAD_A13_12) | ||
28 | || MVC .S2 ILC,B0 | ||
29 | |||
30 | LDW .D2T2 *+B4(THREAD_PC),B3 | ||
31 | || LDDW .D1T1 *+A5(THREAD_A13_12),A13:A12 | ||
32 | |||
33 | STDW .D1T1 A11:A10,*+A4(THREAD_A11_10) | ||
34 | || STDW .D2T2 B1:B0,*+B5(THREAD_RICL_ICL) | ||
35 | #ifndef __DSBT__ | ||
36 | || MVKL .S2 current_ksp,B1 | ||
37 | #endif | ||
38 | |||
39 | STDW .D2T2 B15:B14,*+B5(THREAD_B15_14) | ||
40 | || STDW .D1T1 A15:A14,*+A4(THREAD_A15_14) | ||
41 | #ifndef __DSBT__ | ||
42 | || MVKH .S2 current_ksp,B1 | ||
43 | #endif | ||
44 | |||
45 | ;; Switch to next SP | ||
46 | MV .S2 B7,SP | ||
47 | #ifdef __DSBT__ | ||
48 | || STW .D2T2 B7,*+B14(current_ksp) | ||
49 | #else | ||
50 | || STW .D2T2 B7,*B1 | ||
51 | || MV .L2 B6,B14 | ||
52 | #endif | ||
53 | || LDDW .D1T1 *+A5(THREAD_RICL_ICL),A1:A0 | ||
54 | |||
55 | STDW .D2T2 B11:B10,*+B5(THREAD_B11_10) | ||
56 | || LDDW .D1T1 *+A5(THREAD_A15_14),A15:A14 | ||
57 | |||
58 | STDW .D2T2 B13:B12,*+B5(THREAD_B13_12) | ||
59 | || LDDW .D1T1 *+A5(THREAD_A11_10),A11:A10 | ||
60 | |||
61 | B .S2 B3 ; return in next E1 | ||
62 | || LDDW .D2T2 *+B4(THREAD_B13_12),B13:B12 | ||
63 | |||
64 | LDDW .D2T2 *+B4(THREAD_B11_10),B11:B10 | ||
65 | NOP | ||
66 | |||
67 | MV .L2X A0,B0 | ||
68 | || MV .S1 A6,A4 | ||
69 | |||
70 | MVC .S2 B0,ILC | ||
71 | || MV .L2X A1,B1 | ||
72 | |||
73 | MVC .S2 B1,RILC | ||
74 | ENDPROC(__switch_to) | ||
diff --git a/arch/c6x/kernel/sys_c6x.c b/arch/c6x/kernel/sys_c6x.c new file mode 100644 index 000000000000..3e9bdfbee8ad --- /dev/null +++ b/arch/c6x/kernel/sys_c6x.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/syscalls.h> | ||
13 | #include <linux/uaccess.h> | ||
14 | |||
15 | #include <asm/syscalls.h> | ||
16 | |||
17 | #ifdef CONFIG_ACCESS_CHECK | ||
18 | int _access_ok(unsigned long addr, unsigned long size) | ||
19 | { | ||
20 | if (!size) | ||
21 | return 1; | ||
22 | |||
23 | if (!addr || addr > (0xffffffffUL - (size - 1))) | ||
24 | goto _bad_access; | ||
25 | |||
26 | if (segment_eq(get_fs(), KERNEL_DS)) | ||
27 | return 1; | ||
28 | |||
29 | if (memory_start <= addr && (addr + size - 1) < memory_end) | ||
30 | return 1; | ||
31 | |||
32 | _bad_access: | ||
33 | pr_debug("Bad access attempt: pid[%d] addr[%08lx] size[0x%lx]\n", | ||
34 | current->pid, addr, size); | ||
35 | return 0; | ||
36 | } | ||
37 | EXPORT_SYMBOL(_access_ok); | ||
38 | #endif | ||
39 | |||
40 | /* sys_cache_sync -- sync caches over given range */ | ||
41 | asmlinkage int sys_cache_sync(unsigned long s, unsigned long e) | ||
42 | { | ||
43 | L1D_cache_block_writeback_invalidate(s, e); | ||
44 | L1P_cache_block_invalidate(s, e); | ||
45 | |||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | /* Provide the actual syscall number to call mapping. */ | ||
50 | #undef __SYSCALL | ||
51 | #define __SYSCALL(nr, call) [nr] = (call), | ||
52 | |||
53 | /* | ||
54 | * Use trampolines | ||
55 | */ | ||
56 | #define sys_pread64 sys_pread_c6x | ||
57 | #define sys_pwrite64 sys_pwrite_c6x | ||
58 | #define sys_truncate64 sys_truncate64_c6x | ||
59 | #define sys_ftruncate64 sys_ftruncate64_c6x | ||
60 | #define sys_fadvise64 sys_fadvise64_c6x | ||
61 | #define sys_fadvise64_64 sys_fadvise64_64_c6x | ||
62 | #define sys_fallocate sys_fallocate_c6x | ||
63 | |||
64 | /* Use sys_mmap_pgoff directly */ | ||
65 | #define sys_mmap2 sys_mmap_pgoff | ||
66 | |||
67 | /* | ||
68 | * Note that we can't include <linux/unistd.h> here since the header | ||
69 | * guard will defeat us; <asm/unistd.h> checks for __SYSCALL as well. | ||
70 | */ | ||
71 | void *sys_call_table[__NR_syscalls] = { | ||
72 | [0 ... __NR_syscalls-1] = sys_ni_syscall, | ||
73 | #include <asm/unistd.h> | ||
74 | }; | ||
diff --git a/arch/c6x/kernel/time.c b/arch/c6x/kernel/time.c new file mode 100644 index 000000000000..4c9f136165f7 --- /dev/null +++ b/arch/c6x/kernel/time.c | |||
@@ -0,0 +1,65 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/clocksource.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/param.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/timex.h> | ||
21 | #include <linux/profile.h> | ||
22 | |||
23 | #include <asm/timer64.h> | ||
24 | |||
25 | static u32 sched_clock_multiplier; | ||
26 | #define SCHED_CLOCK_SHIFT 16 | ||
27 | |||
28 | static cycle_t tsc_read(struct clocksource *cs) | ||
29 | { | ||
30 | return get_cycles(); | ||
31 | } | ||
32 | |||
33 | static struct clocksource clocksource_tsc = { | ||
34 | .name = "timestamp", | ||
35 | .rating = 300, | ||
36 | .read = tsc_read, | ||
37 | .mask = CLOCKSOURCE_MASK(64), | ||
38 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
39 | }; | ||
40 | |||
41 | /* | ||
42 | * scheduler clock - returns current time in nanoseconds. | ||
43 | */ | ||
44 | u64 sched_clock(void) | ||
45 | { | ||
46 | u64 tsc = get_cycles(); | ||
47 | |||
48 | return (tsc * sched_clock_multiplier) >> SCHED_CLOCK_SHIFT; | ||
49 | } | ||
50 | |||
51 | void time_init(void) | ||
52 | { | ||
53 | u64 tmp = (u64)NSEC_PER_SEC << SCHED_CLOCK_SHIFT; | ||
54 | |||
55 | do_div(tmp, c6x_core_freq); | ||
56 | sched_clock_multiplier = tmp; | ||
57 | |||
58 | clocksource_register_hz(&clocksource_tsc, c6x_core_freq); | ||
59 | |||
60 | /* write anything into TSCL to enable counting */ | ||
61 | set_creg(TSCL, 0); | ||
62 | |||
63 | /* probe for timer64 event timer */ | ||
64 | timer64_init(); | ||
65 | } | ||
diff --git a/arch/c6x/kernel/traps.c b/arch/c6x/kernel/traps.c new file mode 100644 index 000000000000..f50e3edd6dad --- /dev/null +++ b/arch/c6x/kernel/traps.c | |||
@@ -0,0 +1,423 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/ptrace.h> | ||
13 | #include <linux/kallsyms.h> | ||
14 | #include <linux/bug.h> | ||
15 | |||
16 | #include <asm/soc.h> | ||
17 | #include <asm/traps.h> | ||
18 | |||
19 | int (*c6x_nmi_handler)(struct pt_regs *regs); | ||
20 | |||
21 | void __init trap_init(void) | ||
22 | { | ||
23 | ack_exception(EXCEPT_TYPE_NXF); | ||
24 | ack_exception(EXCEPT_TYPE_EXC); | ||
25 | ack_exception(EXCEPT_TYPE_IXF); | ||
26 | ack_exception(EXCEPT_TYPE_SXF); | ||
27 | enable_exception(); | ||
28 | } | ||
29 | |||
30 | void show_regs(struct pt_regs *regs) | ||
31 | { | ||
32 | pr_err("\n"); | ||
33 | pr_err("PC: %08lx SP: %08lx\n", regs->pc, regs->sp); | ||
34 | pr_err("Status: %08lx ORIG_A4: %08lx\n", regs->csr, regs->orig_a4); | ||
35 | pr_err("A0: %08lx B0: %08lx\n", regs->a0, regs->b0); | ||
36 | pr_err("A1: %08lx B1: %08lx\n", regs->a1, regs->b1); | ||
37 | pr_err("A2: %08lx B2: %08lx\n", regs->a2, regs->b2); | ||
38 | pr_err("A3: %08lx B3: %08lx\n", regs->a3, regs->b3); | ||
39 | pr_err("A4: %08lx B4: %08lx\n", regs->a4, regs->b4); | ||
40 | pr_err("A5: %08lx B5: %08lx\n", regs->a5, regs->b5); | ||
41 | pr_err("A6: %08lx B6: %08lx\n", regs->a6, regs->b6); | ||
42 | pr_err("A7: %08lx B7: %08lx\n", regs->a7, regs->b7); | ||
43 | pr_err("A8: %08lx B8: %08lx\n", regs->a8, regs->b8); | ||
44 | pr_err("A9: %08lx B9: %08lx\n", regs->a9, regs->b9); | ||
45 | pr_err("A10: %08lx B10: %08lx\n", regs->a10, regs->b10); | ||
46 | pr_err("A11: %08lx B11: %08lx\n", regs->a11, regs->b11); | ||
47 | pr_err("A12: %08lx B12: %08lx\n", regs->a12, regs->b12); | ||
48 | pr_err("A13: %08lx B13: %08lx\n", regs->a13, regs->b13); | ||
49 | pr_err("A14: %08lx B14: %08lx\n", regs->a14, regs->dp); | ||
50 | pr_err("A15: %08lx B15: %08lx\n", regs->a15, regs->sp); | ||
51 | pr_err("A16: %08lx B16: %08lx\n", regs->a16, regs->b16); | ||
52 | pr_err("A17: %08lx B17: %08lx\n", regs->a17, regs->b17); | ||
53 | pr_err("A18: %08lx B18: %08lx\n", regs->a18, regs->b18); | ||
54 | pr_err("A19: %08lx B19: %08lx\n", regs->a19, regs->b19); | ||
55 | pr_err("A20: %08lx B20: %08lx\n", regs->a20, regs->b20); | ||
56 | pr_err("A21: %08lx B21: %08lx\n", regs->a21, regs->b21); | ||
57 | pr_err("A22: %08lx B22: %08lx\n", regs->a22, regs->b22); | ||
58 | pr_err("A23: %08lx B23: %08lx\n", regs->a23, regs->b23); | ||
59 | pr_err("A24: %08lx B24: %08lx\n", regs->a24, regs->b24); | ||
60 | pr_err("A25: %08lx B25: %08lx\n", regs->a25, regs->b25); | ||
61 | pr_err("A26: %08lx B26: %08lx\n", regs->a26, regs->b26); | ||
62 | pr_err("A27: %08lx B27: %08lx\n", regs->a27, regs->b27); | ||
63 | pr_err("A28: %08lx B28: %08lx\n", regs->a28, regs->b28); | ||
64 | pr_err("A29: %08lx B29: %08lx\n", regs->a29, regs->b29); | ||
65 | pr_err("A30: %08lx B30: %08lx\n", regs->a30, regs->b30); | ||
66 | pr_err("A31: %08lx B31: %08lx\n", regs->a31, regs->b31); | ||
67 | } | ||
68 | |||
69 | void dump_stack(void) | ||
70 | { | ||
71 | unsigned long stack; | ||
72 | |||
73 | show_stack(current, &stack); | ||
74 | } | ||
75 | EXPORT_SYMBOL(dump_stack); | ||
76 | |||
77 | |||
78 | void die(char *str, struct pt_regs *fp, int nr) | ||
79 | { | ||
80 | console_verbose(); | ||
81 | pr_err("%s: %08x\n", str, nr); | ||
82 | show_regs(fp); | ||
83 | |||
84 | pr_err("Process %s (pid: %d, stackpage=%08lx)\n", | ||
85 | current->comm, current->pid, (PAGE_SIZE + | ||
86 | (unsigned long) current)); | ||
87 | |||
88 | dump_stack(); | ||
89 | while (1) | ||
90 | ; | ||
91 | } | ||
92 | |||
93 | static void die_if_kernel(char *str, struct pt_regs *fp, int nr) | ||
94 | { | ||
95 | if (user_mode(fp)) | ||
96 | return; | ||
97 | |||
98 | die(str, fp, nr); | ||
99 | } | ||
100 | |||
101 | |||
102 | /* Internal exceptions */ | ||
103 | static struct exception_info iexcept_table[10] = { | ||
104 | { "Oops - instruction fetch", SIGBUS, BUS_ADRERR }, | ||
105 | { "Oops - fetch packet", SIGBUS, BUS_ADRERR }, | ||
106 | { "Oops - execute packet", SIGILL, ILL_ILLOPC }, | ||
107 | { "Oops - undefined instruction", SIGILL, ILL_ILLOPC }, | ||
108 | { "Oops - resource conflict", SIGILL, ILL_ILLOPC }, | ||
109 | { "Oops - resource access", SIGILL, ILL_PRVREG }, | ||
110 | { "Oops - privilege", SIGILL, ILL_PRVOPC }, | ||
111 | { "Oops - loops buffer", SIGILL, ILL_ILLOPC }, | ||
112 | { "Oops - software exception", SIGILL, ILL_ILLTRP }, | ||
113 | { "Oops - unknown exception", SIGILL, ILL_ILLOPC } | ||
114 | }; | ||
115 | |||
116 | /* External exceptions */ | ||
117 | static struct exception_info eexcept_table[128] = { | ||
118 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
119 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
120 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
121 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
122 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
123 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
124 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
125 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
126 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
127 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
128 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
129 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
130 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
131 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
132 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
133 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
134 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
135 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
136 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
137 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
138 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
139 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
140 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
141 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
142 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
143 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
144 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
145 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
146 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
147 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
148 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
149 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
150 | |||
151 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
152 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
153 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
154 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
155 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
156 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
157 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
158 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
159 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
160 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
161 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
162 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
163 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
164 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
165 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
166 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
167 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
168 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
169 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
170 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
171 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
172 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
173 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
174 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
175 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
176 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
177 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
178 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
179 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
180 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
181 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
182 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
183 | |||
184 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
185 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
186 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
187 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
188 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
189 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
190 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
191 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
192 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
193 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
194 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
195 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
196 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
197 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
198 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
199 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
200 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
201 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
202 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
203 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
204 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
205 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
206 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
207 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
208 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
209 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
210 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
211 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
212 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
213 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
214 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
215 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
216 | |||
217 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
218 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
219 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
220 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
221 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
222 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
223 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
224 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
225 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
226 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
227 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
228 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
229 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
230 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
231 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
232 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
233 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
234 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
235 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
236 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
237 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
238 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
239 | { "Oops - external exception", SIGBUS, BUS_ADRERR }, | ||
240 | { "Oops - CPU memory protection fault", SIGSEGV, SEGV_ACCERR }, | ||
241 | { "Oops - CPU memory protection fault in L1P", SIGSEGV, SEGV_ACCERR }, | ||
242 | { "Oops - DMA memory protection fault in L1P", SIGSEGV, SEGV_ACCERR }, | ||
243 | { "Oops - CPU memory protection fault in L1D", SIGSEGV, SEGV_ACCERR }, | ||
244 | { "Oops - DMA memory protection fault in L1D", SIGSEGV, SEGV_ACCERR }, | ||
245 | { "Oops - CPU memory protection fault in L2", SIGSEGV, SEGV_ACCERR }, | ||
246 | { "Oops - DMA memory protection fault in L2", SIGSEGV, SEGV_ACCERR }, | ||
247 | { "Oops - EMC CPU memory protection fault", SIGSEGV, SEGV_ACCERR }, | ||
248 | { "Oops - EMC bus error", SIGBUS, BUS_ADRERR } | ||
249 | }; | ||
250 | |||
251 | static void do_trap(struct exception_info *except_info, struct pt_regs *regs) | ||
252 | { | ||
253 | unsigned long addr = instruction_pointer(regs); | ||
254 | siginfo_t info; | ||
255 | |||
256 | if (except_info->code != TRAP_BRKPT) | ||
257 | pr_err("TRAP: %s PC[0x%lx] signo[%d] code[%d]\n", | ||
258 | except_info->kernel_str, regs->pc, | ||
259 | except_info->signo, except_info->code); | ||
260 | |||
261 | die_if_kernel(except_info->kernel_str, regs, addr); | ||
262 | |||
263 | info.si_signo = except_info->signo; | ||
264 | info.si_errno = 0; | ||
265 | info.si_code = except_info->code; | ||
266 | info.si_addr = (void __user *)addr; | ||
267 | |||
268 | force_sig_info(except_info->signo, &info, current); | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * Process an internal exception (non maskable) | ||
273 | */ | ||
274 | static int process_iexcept(struct pt_regs *regs) | ||
275 | { | ||
276 | unsigned int iexcept_report = get_iexcept(); | ||
277 | unsigned int iexcept_num; | ||
278 | |||
279 | ack_exception(EXCEPT_TYPE_IXF); | ||
280 | |||
281 | pr_err("IEXCEPT: PC[0x%lx]\n", regs->pc); | ||
282 | |||
283 | while (iexcept_report) { | ||
284 | iexcept_num = __ffs(iexcept_report); | ||
285 | iexcept_report &= ~(1 << iexcept_num); | ||
286 | set_iexcept(iexcept_report); | ||
287 | if (*(unsigned int *)regs->pc == BKPT_OPCODE) { | ||
288 | /* This is a breakpoint */ | ||
289 | struct exception_info bkpt_exception = { | ||
290 | "Oops - undefined instruction", | ||
291 | SIGTRAP, TRAP_BRKPT | ||
292 | }; | ||
293 | do_trap(&bkpt_exception, regs); | ||
294 | iexcept_report &= ~(0xFF); | ||
295 | set_iexcept(iexcept_report); | ||
296 | continue; | ||
297 | } | ||
298 | |||
299 | do_trap(&iexcept_table[iexcept_num], regs); | ||
300 | } | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Process an external exception (maskable) | ||
306 | */ | ||
307 | static void process_eexcept(struct pt_regs *regs) | ||
308 | { | ||
309 | int evt; | ||
310 | |||
311 | pr_err("EEXCEPT: PC[0x%lx]\n", regs->pc); | ||
312 | |||
313 | while ((evt = soc_get_exception()) >= 0) | ||
314 | do_trap(&eexcept_table[evt], regs); | ||
315 | |||
316 | ack_exception(EXCEPT_TYPE_EXC); | ||
317 | } | ||
318 | |||
319 | /* | ||
320 | * Main exception processing | ||
321 | */ | ||
322 | asmlinkage int process_exception(struct pt_regs *regs) | ||
323 | { | ||
324 | unsigned int type; | ||
325 | unsigned int type_num; | ||
326 | unsigned int ie_num = 9; /* default is unknown exception */ | ||
327 | |||
328 | while ((type = get_except_type()) != 0) { | ||
329 | type_num = fls(type) - 1; | ||
330 | |||
331 | switch (type_num) { | ||
332 | case EXCEPT_TYPE_NXF: | ||
333 | ack_exception(EXCEPT_TYPE_NXF); | ||
334 | if (c6x_nmi_handler) | ||
335 | (c6x_nmi_handler)(regs); | ||
336 | else | ||
337 | pr_alert("NMI interrupt!\n"); | ||
338 | break; | ||
339 | |||
340 | case EXCEPT_TYPE_IXF: | ||
341 | if (process_iexcept(regs)) | ||
342 | return 1; | ||
343 | break; | ||
344 | |||
345 | case EXCEPT_TYPE_EXC: | ||
346 | process_eexcept(regs); | ||
347 | break; | ||
348 | |||
349 | case EXCEPT_TYPE_SXF: | ||
350 | ie_num = 8; | ||
351 | default: | ||
352 | ack_exception(type_num); | ||
353 | do_trap(&iexcept_table[ie_num], regs); | ||
354 | break; | ||
355 | } | ||
356 | } | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | static int kstack_depth_to_print = 48; | ||
361 | |||
362 | static void show_trace(unsigned long *stack, unsigned long *endstack) | ||
363 | { | ||
364 | unsigned long addr; | ||
365 | int i; | ||
366 | |||
367 | pr_debug("Call trace:"); | ||
368 | i = 0; | ||
369 | while (stack + 1 <= endstack) { | ||
370 | addr = *stack++; | ||
371 | /* | ||
372 | * If the address is either in the text segment of the | ||
373 | * kernel, or in the region which contains vmalloc'ed | ||
374 | * memory, it *may* be the address of a calling | ||
375 | * routine; if so, print it so that someone tracing | ||
376 | * down the cause of the crash will be able to figure | ||
377 | * out the call path that was taken. | ||
378 | */ | ||
379 | if (__kernel_text_address(addr)) { | ||
380 | #ifndef CONFIG_KALLSYMS | ||
381 | if (i % 5 == 0) | ||
382 | pr_debug("\n "); | ||
383 | #endif | ||
384 | pr_debug(" [<%08lx>]", addr); | ||
385 | print_symbol(" %s\n", addr); | ||
386 | i++; | ||
387 | } | ||
388 | } | ||
389 | pr_debug("\n"); | ||
390 | } | ||
391 | |||
392 | void show_stack(struct task_struct *task, unsigned long *stack) | ||
393 | { | ||
394 | unsigned long *p, *endstack; | ||
395 | int i; | ||
396 | |||
397 | if (!stack) { | ||
398 | if (task && task != current) | ||
399 | /* We know this is a kernel stack, | ||
400 | so this is the start/end */ | ||
401 | stack = (unsigned long *)thread_saved_ksp(task); | ||
402 | else | ||
403 | stack = (unsigned long *)&stack; | ||
404 | } | ||
405 | endstack = (unsigned long *)(((unsigned long)stack + THREAD_SIZE - 1) | ||
406 | & -THREAD_SIZE); | ||
407 | |||
408 | pr_debug("Stack from %08lx:", (unsigned long)stack); | ||
409 | for (i = 0, p = stack; i < kstack_depth_to_print; i++) { | ||
410 | if (p + 1 > endstack) | ||
411 | break; | ||
412 | if (i % 8 == 0) | ||
413 | pr_cont("\n "); | ||
414 | pr_cont(" %08lx", *p++); | ||
415 | } | ||
416 | pr_cont("\n"); | ||
417 | show_trace(stack, endstack); | ||
418 | } | ||
419 | |||
420 | int is_valid_bugaddr(unsigned long addr) | ||
421 | { | ||
422 | return __kernel_text_address(addr); | ||
423 | } | ||
diff --git a/arch/c6x/kernel/vectors.S b/arch/c6x/kernel/vectors.S new file mode 100644 index 000000000000..c95c66fc71e8 --- /dev/null +++ b/arch/c6x/kernel/vectors.S | |||
@@ -0,0 +1,81 @@ | |||
1 | ; | ||
2 | ; Port on Texas Instruments TMS320C6x architecture | ||
3 | ; | ||
4 | ; Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | ; Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | ; | ||
7 | ; This program is free software; you can redistribute it and/or modify | ||
8 | ; it under the terms of the GNU General Public License version 2 as | ||
9 | ; published by the Free Software Foundation. | ||
10 | ; | ||
11 | ; This section handles all the interrupt vector routines. | ||
12 | ; At RESET the processor sets up the DRAM timing parameters and | ||
13 | ; branches to the label _c_int00 which handles initialization for the C code. | ||
14 | ; | ||
15 | |||
16 | #define ALIGNMENT 5 | ||
17 | |||
18 | .macro IRQVEC name, handler | ||
19 | .align ALIGNMENT | ||
20 | .hidden \name | ||
21 | .global \name | ||
22 | \name: | ||
23 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
24 | STW .D2T1 A0,*B15--[2] | ||
25 | || MVKL .S1 \handler,A0 | ||
26 | MVKH .S1 \handler,A0 | ||
27 | B .S2X A0 | ||
28 | LDW .D2T1 *++B15[2],A0 | ||
29 | NOP 4 | ||
30 | NOP | ||
31 | NOP | ||
32 | .endm | ||
33 | #else /* CONFIG_C6X_BIG_KERNEL */ | ||
34 | B .S2 \handler | ||
35 | NOP | ||
36 | NOP | ||
37 | NOP | ||
38 | NOP | ||
39 | NOP | ||
40 | NOP | ||
41 | NOP | ||
42 | .endm | ||
43 | #endif /* CONFIG_C6X_BIG_KERNEL */ | ||
44 | |||
45 | .sect ".vectors","ax" | ||
46 | .align ALIGNMENT | ||
47 | .global RESET | ||
48 | .hidden RESET | ||
49 | RESET: | ||
50 | #ifdef CONFIG_C6X_BIG_KERNEL | ||
51 | MVKL .S1 _c_int00,A0 ; branch to _c_int00 | ||
52 | MVKH .S1 _c_int00,A0 | ||
53 | B .S2X A0 | ||
54 | #else | ||
55 | B .S2 _c_int00 | ||
56 | NOP | ||
57 | NOP | ||
58 | #endif | ||
59 | NOP | ||
60 | NOP | ||
61 | NOP | ||
62 | NOP | ||
63 | NOP | ||
64 | |||
65 | |||
66 | IRQVEC NMI,_nmi_handler ; NMI interrupt | ||
67 | IRQVEC AINT,_bad_interrupt ; reserved | ||
68 | IRQVEC MSGINT,_bad_interrupt ; reserved | ||
69 | |||
70 | IRQVEC INT4,_int4_handler | ||
71 | IRQVEC INT5,_int5_handler | ||
72 | IRQVEC INT6,_int6_handler | ||
73 | IRQVEC INT7,_int7_handler | ||
74 | IRQVEC INT8,_int8_handler | ||
75 | IRQVEC INT9,_int9_handler | ||
76 | IRQVEC INT10,_int10_handler | ||
77 | IRQVEC INT11,_int11_handler | ||
78 | IRQVEC INT12,_int12_handler | ||
79 | IRQVEC INT13,_int13_handler | ||
80 | IRQVEC INT14,_int14_handler | ||
81 | IRQVEC INT15,_int15_handler | ||
diff --git a/arch/c6x/kernel/vmlinux.lds.S b/arch/c6x/kernel/vmlinux.lds.S new file mode 100644 index 000000000000..1d81c4c129ec --- /dev/null +++ b/arch/c6x/kernel/vmlinux.lds.S | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * ld script for the c6x kernel | ||
3 | * | ||
4 | * Copyright (C) 2010, 2011 Texas Instruments Incorporated | ||
5 | * Mark Salter <msalter@redhat.com> | ||
6 | */ | ||
7 | #include <asm-generic/vmlinux.lds.h> | ||
8 | #include <asm/thread_info.h> | ||
9 | #include <asm/page.h> | ||
10 | |||
11 | ENTRY(_c_int00) | ||
12 | |||
13 | #if defined(CONFIG_CPU_BIG_ENDIAN) | ||
14 | jiffies = jiffies_64 + 4; | ||
15 | #else | ||
16 | jiffies = jiffies_64; | ||
17 | #endif | ||
18 | |||
19 | #define READONLY_SEGMENT_START \ | ||
20 | . = PAGE_OFFSET; | ||
21 | #define READWRITE_SEGMENT_START \ | ||
22 | . = ALIGN(128); \ | ||
23 | _data_lma = .; | ||
24 | |||
25 | SECTIONS | ||
26 | { | ||
27 | /* | ||
28 | * Start kernel read only segment | ||
29 | */ | ||
30 | READONLY_SEGMENT_START | ||
31 | |||
32 | .vectors : | ||
33 | { | ||
34 | _vectors_start = .; | ||
35 | *(.vectors) | ||
36 | . = ALIGN(0x400); | ||
37 | _vectors_end = .; | ||
38 | } | ||
39 | |||
40 | . = ALIGN(0x1000); | ||
41 | .cmdline : | ||
42 | { | ||
43 | *(.cmdline) | ||
44 | } | ||
45 | |||
46 | /* | ||
47 | * This section contains data which may be shared with other | ||
48 | * cores. It needs to be a fixed offset from PAGE_OFFSET | ||
49 | * regardless of kernel configuration. | ||
50 | */ | ||
51 | .virtio_ipc_dev : | ||
52 | { | ||
53 | *(.virtio_ipc_dev) | ||
54 | } | ||
55 | |||
56 | . = ALIGN(PAGE_SIZE); | ||
57 | .init : | ||
58 | { | ||
59 | _stext = .; | ||
60 | _sinittext = .; | ||
61 | HEAD_TEXT | ||
62 | INIT_TEXT | ||
63 | _einittext = .; | ||
64 | } | ||
65 | |||
66 | __init_begin = _stext; | ||
67 | INIT_DATA_SECTION(16) | ||
68 | |||
69 | PERCPU_SECTION(128) | ||
70 | |||
71 | . = ALIGN(PAGE_SIZE); | ||
72 | __init_end = .; | ||
73 | |||
74 | .text : | ||
75 | { | ||
76 | _text = .; | ||
77 | TEXT_TEXT | ||
78 | SCHED_TEXT | ||
79 | LOCK_TEXT | ||
80 | IRQENTRY_TEXT | ||
81 | KPROBES_TEXT | ||
82 | *(.fixup) | ||
83 | *(.gnu.warning) | ||
84 | } | ||
85 | |||
86 | EXCEPTION_TABLE(16) | ||
87 | NOTES | ||
88 | |||
89 | RO_DATA_SECTION(PAGE_SIZE) | ||
90 | .const : | ||
91 | { | ||
92 | *(.const .const.* .gnu.linkonce.r.*) | ||
93 | *(.switch) | ||
94 | } | ||
95 | |||
96 | . = ALIGN (8) ; | ||
97 | __fdt_blob : AT(ADDR(__fdt_blob) - LOAD_OFFSET) | ||
98 | { | ||
99 | _fdt_start = . ; /* place for fdt blob */ | ||
100 | *(__fdt_blob) ; /* Any link-placed DTB */ | ||
101 | BYTE(0); /* section always has contents */ | ||
102 | . = _fdt_start + 0x4000; /* Pad up to 16kbyte */ | ||
103 | _fdt_end = . ; | ||
104 | } | ||
105 | |||
106 | _etext = .; | ||
107 | |||
108 | /* | ||
109 | * Start kernel read-write segment. | ||
110 | */ | ||
111 | READWRITE_SEGMENT_START | ||
112 | _sdata = .; | ||
113 | |||
114 | .fardata : AT(ADDR(.fardata) - LOAD_OFFSET) | ||
115 | { | ||
116 | INIT_TASK_DATA(THREAD_SIZE) | ||
117 | NOSAVE_DATA | ||
118 | PAGE_ALIGNED_DATA(PAGE_SIZE) | ||
119 | CACHELINE_ALIGNED_DATA(128) | ||
120 | READ_MOSTLY_DATA(128) | ||
121 | DATA_DATA | ||
122 | CONSTRUCTORS | ||
123 | *(.data1) | ||
124 | *(.fardata .fardata.*) | ||
125 | *(.data.debug_bpt) | ||
126 | } | ||
127 | |||
128 | .neardata ALIGN(8) : AT(ADDR(.neardata) - LOAD_OFFSET) | ||
129 | { | ||
130 | *(.neardata2 .neardata2.* .gnu.linkonce.s2.*) | ||
131 | *(.neardata .neardata.* .gnu.linkonce.s.*) | ||
132 | . = ALIGN(8); | ||
133 | } | ||
134 | |||
135 | _edata = .; | ||
136 | |||
137 | __bss_start = .; | ||
138 | SBSS(8) | ||
139 | BSS(8) | ||
140 | .far : | ||
141 | { | ||
142 | . = ALIGN(8); | ||
143 | *(.dynfar) | ||
144 | *(.far .far.* .gnu.linkonce.b.*) | ||
145 | . = ALIGN(8); | ||
146 | } | ||
147 | __bss_stop = .; | ||
148 | |||
149 | _end = .; | ||
150 | |||
151 | DWARF_DEBUG | ||
152 | |||
153 | /DISCARD/ : | ||
154 | { | ||
155 | EXIT_TEXT | ||
156 | EXIT_DATA | ||
157 | EXIT_CALL | ||
158 | *(.discard) | ||
159 | *(.discard.*) | ||
160 | *(.interp) | ||
161 | } | ||
162 | } | ||
diff --git a/arch/c6x/lib/Makefile b/arch/c6x/lib/Makefile new file mode 100644 index 000000000000..ffd3c659091a --- /dev/null +++ b/arch/c6x/lib/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for arch/c6x/lib/ | ||
3 | # | ||
4 | |||
5 | lib-y := divu.o divi.o pop_rts.o push_rts.o remi.o remu.o strasgi.o llshru.o | ||
6 | lib-y += llshr.o llshl.o negll.o mpyll.o divremi.o divremu.o | ||
7 | lib-y += checksum.o csum_64plus.o memcpy_64plus.o strasgi_64plus.o | ||
diff --git a/arch/c6x/lib/checksum.c b/arch/c6x/lib/checksum.c new file mode 100644 index 000000000000..67cc93b0b932 --- /dev/null +++ b/arch/c6x/lib/checksum.c | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or | ||
3 | * modify it under the terms of the GNU General Public License | ||
4 | * as published by the Free Software Foundation; either version | ||
5 | * 2 of the License, or (at your option) any later version. | ||
6 | */ | ||
7 | #include <linux/module.h> | ||
8 | #include <net/checksum.h> | ||
9 | |||
10 | #include <asm/byteorder.h> | ||
11 | |||
12 | /* | ||
13 | * copy from fs while checksumming, otherwise like csum_partial | ||
14 | */ | ||
15 | __wsum | ||
16 | csum_partial_copy_from_user(const void __user *src, void *dst, int len, | ||
17 | __wsum sum, int *csum_err) | ||
18 | { | ||
19 | int missing; | ||
20 | |||
21 | missing = __copy_from_user(dst, src, len); | ||
22 | if (missing) { | ||
23 | memset(dst + len - missing, 0, missing); | ||
24 | *csum_err = -EFAULT; | ||
25 | } else | ||
26 | *csum_err = 0; | ||
27 | |||
28 | return csum_partial(dst, len, sum); | ||
29 | } | ||
30 | EXPORT_SYMBOL(csum_partial_copy_from_user); | ||
31 | |||
32 | /* These are from csum_64plus.S */ | ||
33 | EXPORT_SYMBOL(csum_partial); | ||
34 | EXPORT_SYMBOL(csum_partial_copy); | ||
35 | EXPORT_SYMBOL(ip_compute_csum); | ||
36 | EXPORT_SYMBOL(ip_fast_csum); | ||
diff --git a/arch/c6x/lib/csum_64plus.S b/arch/c6x/lib/csum_64plus.S new file mode 100644 index 000000000000..6d2589647227 --- /dev/null +++ b/arch/c6x/lib/csum_64plus.S | |||
@@ -0,0 +1,419 @@ | |||
1 | ; | ||
2 | ; linux/arch/c6x/lib/csum_64plus.s | ||
3 | ; | ||
4 | ; Port on Texas Instruments TMS320C6x architecture | ||
5 | ; | ||
6 | ; Copyright (C) 2006, 2009, 2010, 2011 Texas Instruments Incorporated | ||
7 | ; Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
8 | ; | ||
9 | ; This program is free software; you can redistribute it and/or modify | ||
10 | ; it under the terms of the GNU General Public License version 2 as | ||
11 | ; published by the Free Software Foundation. | ||
12 | ; | ||
13 | #include <linux/linkage.h> | ||
14 | |||
15 | ; | ||
16 | ;unsigned int csum_partial_copy(const char *src, char * dst, | ||
17 | ; int len, int sum) | ||
18 | ; | ||
19 | ; A4: src | ||
20 | ; B4: dst | ||
21 | ; A6: len | ||
22 | ; B6: sum | ||
23 | ; return csum in A4 | ||
24 | ; | ||
25 | |||
26 | .text | ||
27 | ENTRY(csum_partial_copy) | ||
28 | MVC .S2 ILC,B30 | ||
29 | |||
30 | MV .D1X B6,A31 ; given csum | ||
31 | ZERO .D1 A9 ; csum (a side) | ||
32 | || ZERO .D2 B9 ; csum (b side) | ||
33 | || SHRU .S2X A6,2,B5 ; len / 4 | ||
34 | |||
35 | ;; Check alignment and size | ||
36 | AND .S1 3,A4,A1 | ||
37 | || AND .S2 3,B4,B0 | ||
38 | OR .L2X B0,A1,B0 ; non aligned condition | ||
39 | || MVC .S2 B5,ILC | ||
40 | || MVK .D2 1,B2 | ||
41 | || MV .D1X B5,A1 ; words condition | ||
42 | [!A1] B .S1 L8 | ||
43 | [B0] BNOP .S1 L6,5 | ||
44 | |||
45 | SPLOOP 1 | ||
46 | |||
47 | ;; Main loop for aligned words | ||
48 | LDW .D1T1 *A4++,A7 | ||
49 | NOP 4 | ||
50 | MV .S2X A7,B7 | ||
51 | || EXTU .S1 A7,0,16,A16 | ||
52 | STW .D2T2 B7,*B4++ | ||
53 | || MPYU .M2 B7,B2,B8 | ||
54 | || ADD .L1 A16,A9,A9 | ||
55 | NOP | ||
56 | SPKERNEL 8,0 | ||
57 | || ADD .L2 B8,B9,B9 | ||
58 | |||
59 | ZERO .D1 A1 | ||
60 | || ADD .L1X A9,B9,A9 ; add csum from a and b sides | ||
61 | |||
62 | L6: | ||
63 | [!A1] BNOP .S1 L8,5 | ||
64 | |||
65 | ;; Main loop for non-aligned words | ||
66 | SPLOOP 2 | ||
67 | || MVK .L1 1,A2 | ||
68 | |||
69 | LDNW .D1T1 *A4++,A7 | ||
70 | NOP 3 | ||
71 | |||
72 | NOP | ||
73 | MV .S2X A7,B7 | ||
74 | || EXTU .S1 A7,0,16,A16 | ||
75 | || MPYU .M1 A7,A2,A8 | ||
76 | |||
77 | ADD .L1 A16,A9,A9 | ||
78 | SPKERNEL 6,0 | ||
79 | || STNW .D2T2 B7,*B4++ | ||
80 | || ADD .L1 A8,A9,A9 | ||
81 | |||
82 | L8: AND .S2X 2,A6,B5 | ||
83 | CMPGT .L2 B5,0,B0 | ||
84 | [!B0] BNOP .S1 L82,4 | ||
85 | |||
86 | ;; Manage half-word | ||
87 | ZERO .L1 A7 | ||
88 | || ZERO .D1 A8 | ||
89 | |||
90 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
91 | |||
92 | LDBU .D1T1 *A4++,A7 | ||
93 | LDBU .D1T1 *A4++,A8 | ||
94 | NOP 3 | ||
95 | SHL .S1 A7,8,A0 | ||
96 | ADD .S1 A8,A9,A9 | ||
97 | STB .D2T1 A7,*B4++ | ||
98 | || ADD .S1 A0,A9,A9 | ||
99 | STB .D2T1 A8,*B4++ | ||
100 | |||
101 | #else | ||
102 | |||
103 | LDBU .D1T1 *A4++,A7 | ||
104 | LDBU .D1T1 *A4++,A8 | ||
105 | NOP 3 | ||
106 | ADD .S1 A7,A9,A9 | ||
107 | SHL .S1 A8,8,A0 | ||
108 | |||
109 | STB .D2T1 A7,*B4++ | ||
110 | || ADD .S1 A0,A9,A9 | ||
111 | STB .D2T1 A8,*B4++ | ||
112 | |||
113 | #endif | ||
114 | |||
115 | ;; Manage eventually the last byte | ||
116 | L82: AND .S2X 1,A6,B0 | ||
117 | [!B0] BNOP .S1 L9,5 | ||
118 | |||
119 | || ZERO .L1 A7 | ||
120 | |||
121 | L83: LDBU .D1T1 *A4++,A7 | ||
122 | NOP 4 | ||
123 | |||
124 | MV .L2X A7,B7 | ||
125 | |||
126 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
127 | |||
128 | STB .D2T2 B7,*B4++ | ||
129 | || SHL .S1 A7,8,A7 | ||
130 | ADD .S1 A7,A9,A9 | ||
131 | |||
132 | #else | ||
133 | |||
134 | STB .D2T2 B7,*B4++ | ||
135 | || ADD .S1 A7,A9,A9 | ||
136 | |||
137 | #endif | ||
138 | |||
139 | ;; Fold the csum | ||
140 | L9: SHRU .S2X A9,16,B0 | ||
141 | [!B0] BNOP .S1 L10,5 | ||
142 | |||
143 | L91: SHRU .S2X A9,16,B4 | ||
144 | || EXTU .S1 A9,16,16,A3 | ||
145 | ADD .D1X A3,B4,A9 | ||
146 | |||
147 | SHRU .S1 A9,16,A0 | ||
148 | [A0] BNOP .S1 L91,5 | ||
149 | |||
150 | L10: ADD .D1 A31,A9,A9 | ||
151 | MV .D1 A9,A4 | ||
152 | |||
153 | BNOP .S2 B3,4 | ||
154 | MVC .S2 B30,ILC | ||
155 | ENDPROC(csum_partial_copy) | ||
156 | |||
157 | ; | ||
158 | ;unsigned short | ||
159 | ;ip_fast_csum(unsigned char *iph, unsigned int ihl) | ||
160 | ;{ | ||
161 | ; unsigned int checksum = 0; | ||
162 | ; unsigned short *tosum = (unsigned short *) iph; | ||
163 | ; int len; | ||
164 | ; | ||
165 | ; len = ihl*4; | ||
166 | ; | ||
167 | ; if (len <= 0) | ||
168 | ; return 0; | ||
169 | ; | ||
170 | ; while(len) { | ||
171 | ; len -= 2; | ||
172 | ; checksum += *tosum++; | ||
173 | ; } | ||
174 | ; if (len & 1) | ||
175 | ; checksum += *(unsigned char*) tosum; | ||
176 | ; | ||
177 | ; while(checksum >> 16) | ||
178 | ; checksum = (checksum & 0xffff) + (checksum >> 16); | ||
179 | ; | ||
180 | ; return ~checksum; | ||
181 | ;} | ||
182 | ; | ||
183 | ; A4: iph | ||
184 | ; B4: ihl | ||
185 | ; return checksum in A4 | ||
186 | ; | ||
187 | .text | ||
188 | |||
189 | ENTRY(ip_fast_csum) | ||
190 | ZERO .D1 A5 | ||
191 | || MVC .S2 ILC,B30 | ||
192 | SHL .S2 B4,2,B0 | ||
193 | CMPGT .L2 B0,0,B1 | ||
194 | [!B1] BNOP .S1 L15,4 | ||
195 | [!B1] ZERO .D1 A3 | ||
196 | |||
197 | [!B0] B .S1 L12 | ||
198 | SHRU .S2 B0,1,B0 | ||
199 | MVC .S2 B0,ILC | ||
200 | NOP 3 | ||
201 | |||
202 | SPLOOP 1 | ||
203 | LDHU .D1T1 *A4++,A3 | ||
204 | NOP 3 | ||
205 | NOP | ||
206 | SPKERNEL 5,0 | ||
207 | || ADD .L1 A3,A5,A5 | ||
208 | |||
209 | L12: SHRU .S1 A5,16,A0 | ||
210 | [!A0] BNOP .S1 L14,5 | ||
211 | |||
212 | L13: SHRU .S2X A5,16,B4 | ||
213 | EXTU .S1 A5,16,16,A3 | ||
214 | ADD .D1X A3,B4,A5 | ||
215 | SHRU .S1 A5,16,A0 | ||
216 | [A0] BNOP .S1 L13,5 | ||
217 | |||
218 | L14: NOT .D1 A5,A3 | ||
219 | EXTU .S1 A3,16,16,A3 | ||
220 | |||
221 | L15: BNOP .S2 B3,3 | ||
222 | MVC .S2 B30,ILC | ||
223 | MV .D1 A3,A4 | ||
224 | ENDPROC(ip_fast_csum) | ||
225 | |||
226 | ; | ||
227 | ;unsigned short | ||
228 | ;do_csum(unsigned char *buff, unsigned int len) | ||
229 | ;{ | ||
230 | ; int odd, count; | ||
231 | ; unsigned int result = 0; | ||
232 | ; | ||
233 | ; if (len <= 0) | ||
234 | ; goto out; | ||
235 | ; odd = 1 & (unsigned long) buff; | ||
236 | ; if (odd) { | ||
237 | ;#ifdef __LITTLE_ENDIAN | ||
238 | ; result += (*buff << 8); | ||
239 | ;#else | ||
240 | ; result = *buff; | ||
241 | ;#endif | ||
242 | ; len--; | ||
243 | ; buff++; | ||
244 | ; } | ||
245 | ; count = len >> 1; /* nr of 16-bit words.. */ | ||
246 | ; if (count) { | ||
247 | ; if (2 & (unsigned long) buff) { | ||
248 | ; result += *(unsigned short *) buff; | ||
249 | ; count--; | ||
250 | ; len -= 2; | ||
251 | ; buff += 2; | ||
252 | ; } | ||
253 | ; count >>= 1; /* nr of 32-bit words.. */ | ||
254 | ; if (count) { | ||
255 | ; unsigned int carry = 0; | ||
256 | ; do { | ||
257 | ; unsigned int w = *(unsigned int *) buff; | ||
258 | ; count--; | ||
259 | ; buff += 4; | ||
260 | ; result += carry; | ||
261 | ; result += w; | ||
262 | ; carry = (w > result); | ||
263 | ; } while (count); | ||
264 | ; result += carry; | ||
265 | ; result = (result & 0xffff) + (result >> 16); | ||
266 | ; } | ||
267 | ; if (len & 2) { | ||
268 | ; result += *(unsigned short *) buff; | ||
269 | ; buff += 2; | ||
270 | ; } | ||
271 | ; } | ||
272 | ; if (len & 1) | ||
273 | ;#ifdef __LITTLE_ENDIAN | ||
274 | ; result += *buff; | ||
275 | ;#else | ||
276 | ; result += (*buff << 8); | ||
277 | ;#endif | ||
278 | ; result = (result & 0xffff) + (result >> 16); | ||
279 | ; /* add up carry.. */ | ||
280 | ; result = (result & 0xffff) + (result >> 16); | ||
281 | ; if (odd) | ||
282 | ; result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); | ||
283 | ;out: | ||
284 | ; return result; | ||
285 | ;} | ||
286 | ; | ||
287 | ; A4: buff | ||
288 | ; B4: len | ||
289 | ; return checksum in A4 | ||
290 | ; | ||
291 | |||
292 | ENTRY(do_csum) | ||
293 | CMPGT .L2 B4,0,B0 | ||
294 | [!B0] BNOP .S1 L26,3 | ||
295 | EXTU .S1 A4,31,31,A0 | ||
296 | |||
297 | MV .L1 A0,A3 | ||
298 | || MV .S1X B3,A5 | ||
299 | || MV .L2 B4,B3 | ||
300 | || ZERO .D1 A1 | ||
301 | |||
302 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
303 | [A0] SUB .L2 B3,1,B3 | ||
304 | || [A0] LDBU .D1T1 *A4++,A1 | ||
305 | #else | ||
306 | [!A0] BNOP .S1 L21,5 | ||
307 | || [A0] LDBU .D1T1 *A4++,A0 | ||
308 | SUB .L2 B3,1,B3 | ||
309 | || SHL .S1 A0,8,A1 | ||
310 | L21: | ||
311 | #endif | ||
312 | SHR .S2 B3,1,B0 | ||
313 | [!B0] BNOP .S1 L24,3 | ||
314 | MVK .L1 2,A0 | ||
315 | AND .L1 A4,A0,A0 | ||
316 | |||
317 | [!A0] BNOP .S1 L22,5 | ||
318 | || [A0] LDHU .D1T1 *A4++,A0 | ||
319 | SUB .L2 B0,1,B0 | ||
320 | || SUB .S2 B3,2,B3 | ||
321 | || ADD .L1 A0,A1,A1 | ||
322 | L22: | ||
323 | SHR .S2 B0,1,B0 | ||
324 | || ZERO .L1 A0 | ||
325 | |||
326 | [!B0] BNOP .S1 L23,5 | ||
327 | || [B0] MVC .S2 B0,ILC | ||
328 | |||
329 | SPLOOP 3 | ||
330 | SPMASK L1 | ||
331 | || MV .L1 A1,A2 | ||
332 | || LDW .D1T1 *A4++,A1 | ||
333 | |||
334 | NOP 4 | ||
335 | ADD .L1 A0,A1,A0 | ||
336 | ADD .L1 A2,A0,A2 | ||
337 | |||
338 | SPKERNEL 1,2 | ||
339 | || CMPGTU .L1 A1,A2,A0 | ||
340 | |||
341 | ADD .L1 A0,A2,A6 | ||
342 | EXTU .S1 A6,16,16,A7 | ||
343 | SHRU .S2X A6,16,B0 | ||
344 | NOP 1 | ||
345 | ADD .L1X A7,B0,A1 | ||
346 | L23: | ||
347 | MVK .L2 2,B0 | ||
348 | AND .L2 B3,B0,B0 | ||
349 | [B0] LDHU .D1T1 *A4++,A0 | ||
350 | NOP 4 | ||
351 | [B0] ADD .L1 A0,A1,A1 | ||
352 | L24: | ||
353 | EXTU .S2 B3,31,31,B0 | ||
354 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
355 | [!B0] BNOP .S1 L25,4 | ||
356 | || [B0] LDBU .D1T1 *A4,A0 | ||
357 | SHL .S1 A0,8,A0 | ||
358 | ADD .L1 A0,A1,A1 | ||
359 | L25: | ||
360 | #else | ||
361 | [B0] LDBU .D1T1 *A4,A0 | ||
362 | NOP 4 | ||
363 | [B0] ADD .L1 A0,A1,A1 | ||
364 | #endif | ||
365 | EXTU .S1 A1,16,16,A0 | ||
366 | SHRU .S2X A1,16,B0 | ||
367 | NOP 1 | ||
368 | ADD .L1X A0,B0,A0 | ||
369 | SHRU .S1 A0,16,A1 | ||
370 | ADD .L1 A0,A1,A0 | ||
371 | EXTU .S1 A0,16,16,A1 | ||
372 | EXTU .S1 A1,16,24,A2 | ||
373 | |||
374 | EXTU .S1 A1,24,16,A0 | ||
375 | || MV .L2X A3,B0 | ||
376 | |||
377 | [B0] OR .L1 A0,A2,A1 | ||
378 | L26: | ||
379 | NOP 1 | ||
380 | BNOP .S2X A5,4 | ||
381 | MV .L1 A1,A4 | ||
382 | ENDPROC(do_csum) | ||
383 | |||
384 | ;__wsum csum_partial(const void *buff, int len, __wsum wsum) | ||
385 | ;{ | ||
386 | ; unsigned int sum = (__force unsigned int)wsum; | ||
387 | ; unsigned int result = do_csum(buff, len); | ||
388 | ; | ||
389 | ; /* add in old sum, and carry.. */ | ||
390 | ; result += sum; | ||
391 | ; if (sum > result) | ||
392 | ; result += 1; | ||
393 | ; return (__force __wsum)result; | ||
394 | ;} | ||
395 | ; | ||
396 | ENTRY(csum_partial) | ||
397 | MV .L1X B3,A9 | ||
398 | || CALLP .S2 do_csum,B3 | ||
399 | || MV .S1 A6,A8 | ||
400 | BNOP .S2X A9,2 | ||
401 | ADD .L1 A8,A4,A1 | ||
402 | CMPGTU .L1 A8,A1,A0 | ||
403 | ADD .L1 A1,A0,A4 | ||
404 | ENDPROC(csum_partial) | ||
405 | |||
406 | ;unsigned short | ||
407 | ;ip_compute_csum(unsigned char *buff, unsigned int len) | ||
408 | ; | ||
409 | ; A4: buff | ||
410 | ; B4: len | ||
411 | ; return checksum in A4 | ||
412 | |||
413 | ENTRY(ip_compute_csum) | ||
414 | MV .L1X B3,A9 | ||
415 | || CALLP .S2 do_csum,B3 | ||
416 | BNOP .S2X A9,3 | ||
417 | NOT .S1 A4,A4 | ||
418 | CLR .S1 A4,16,31,A4 | ||
419 | ENDPROC(ip_compute_csum) | ||
diff --git a/arch/c6x/lib/divi.S b/arch/c6x/lib/divi.S new file mode 100644 index 000000000000..4bde924f2a98 --- /dev/null +++ b/arch/c6x/lib/divi.S | |||
@@ -0,0 +1,53 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | ;; ABI considerations for the divide functions | ||
21 | ;; The following registers are call-used: | ||
22 | ;; __c6xabi_divi A0,A1,A2,A4,A6,B0,B1,B2,B4,B5 | ||
23 | ;; __c6xabi_divu A0,A1,A2,A4,A6,B0,B1,B2,B4 | ||
24 | ;; __c6xabi_remi A1,A2,A4,A5,A6,B0,B1,B2,B4 | ||
25 | ;; __c6xabi_remu A1,A4,A5,A7,B0,B1,B2,B4 | ||
26 | ;; | ||
27 | ;; In our implementation, divu and remu are leaf functions, | ||
28 | ;; while both divi and remi call into divu. | ||
29 | ;; A0 is not clobbered by any of the functions. | ||
30 | ;; divu does not clobber B2 either, which is taken advantage of | ||
31 | ;; in remi. | ||
32 | ;; divi uses B5 to hold the original return address during | ||
33 | ;; the call to divu. | ||
34 | ;; remi uses B2 and A5 to hold the input values during the | ||
35 | ;; call to divu. It stores B3 in on the stack. | ||
36 | |||
37 | .text | ||
38 | ENTRY(__c6xabi_divi) | ||
39 | call .s2 __c6xabi_divu | ||
40 | || mv .d2 B3, B5 | ||
41 | || cmpgt .l1 0, A4, A1 | ||
42 | || cmpgt .l2 0, B4, B1 | ||
43 | |||
44 | [A1] neg .l1 A4, A4 | ||
45 | || [B1] neg .l2 B4, B4 | ||
46 | || xor .s1x A1, B1, A1 | ||
47 | [A1] addkpc .s2 _divu_ret, B3, 4 | ||
48 | _divu_ret: | ||
49 | neg .l1 A4, A4 | ||
50 | || mv .l2 B3,B5 | ||
51 | || ret .s2 B5 | ||
52 | nop 5 | ||
53 | ENDPROC(__c6xabi_divi) | ||
diff --git a/arch/c6x/lib/divremi.S b/arch/c6x/lib/divremi.S new file mode 100644 index 000000000000..64bc5aa95ad3 --- /dev/null +++ b/arch/c6x/lib/divremi.S | |||
@@ -0,0 +1,46 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | .text | ||
21 | ENTRY(__c6xabi_divremi) | ||
22 | stw .d2t2 B3, *B15--[2] | ||
23 | || cmpgt .l1 0, A4, A1 | ||
24 | || cmpgt .l2 0, B4, B2 | ||
25 | || mv .s1 A4, A5 | ||
26 | || call .s2 __c6xabi_divu | ||
27 | |||
28 | [A1] neg .l1 A4, A4 | ||
29 | || [B2] neg .l2 B4, B4 | ||
30 | || xor .s2x B2, A1, B0 | ||
31 | || mv .d2 B4, B2 | ||
32 | |||
33 | [B0] addkpc .s2 _divu_ret_1, B3, 1 | ||
34 | [!B0] addkpc .s2 _divu_ret_2, B3, 1 | ||
35 | nop 2 | ||
36 | _divu_ret_1: | ||
37 | neg .l1 A4, A4 | ||
38 | _divu_ret_2: | ||
39 | ldw .d2t2 *++B15[2], B3 | ||
40 | |||
41 | mpy32 .m1x A4, B2, A6 | ||
42 | nop 3 | ||
43 | ret .s2 B3 | ||
44 | sub .l1 A5, A6, A5 | ||
45 | nop 4 | ||
46 | ENDPROC(__c6xabi_divremi) | ||
diff --git a/arch/c6x/lib/divremu.S b/arch/c6x/lib/divremu.S new file mode 100644 index 000000000000..caa9f23ee167 --- /dev/null +++ b/arch/c6x/lib/divremu.S | |||
@@ -0,0 +1,87 @@ | |||
1 | ;; Copyright 2011 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | .text | ||
21 | ENTRY(__c6xabi_divremu) | ||
22 | ;; We use a series of up to 31 subc instructions. First, we find | ||
23 | ;; out how many leading zero bits there are in the divisor. This | ||
24 | ;; gives us both a shift count for aligning (shifting) the divisor | ||
25 | ;; to the, and the number of times we have to execute subc. | ||
26 | |||
27 | ;; At the end, we have both the remainder and most of the quotient | ||
28 | ;; in A4. The top bit of the quotient is computed first and is | ||
29 | ;; placed in A2. | ||
30 | |||
31 | ;; Return immediately if the dividend is zero. Setting B4 to 1 | ||
32 | ;; is a trick to allow us to leave the following insns in the jump | ||
33 | ;; delay slot without affecting the result. | ||
34 | mv .s2x A4, B1 | ||
35 | |||
36 | [b1] lmbd .l2 1, B4, B1 | ||
37 | ||[!b1] b .s2 B3 ; RETURN A | ||
38 | ||[!b1] mvk .d2 1, B4 | ||
39 | |||
40 | ||[!b1] zero .s1 A5 | ||
41 | mv .l1x B1, A6 | ||
42 | || shl .s2 B4, B1, B4 | ||
43 | |||
44 | ;; The loop performs a maximum of 28 steps, so we do the | ||
45 | ;; first 3 here. | ||
46 | cmpltu .l1x A4, B4, A2 | ||
47 | [!A2] sub .l1x A4, B4, A4 | ||
48 | || shru .s2 B4, 1, B4 | ||
49 | || xor .s1 1, A2, A2 | ||
50 | |||
51 | shl .s1 A2, 31, A2 | ||
52 | || [b1] subc .l1x A4,B4,A4 | ||
53 | || [b1] add .s2 -1, B1, B1 | ||
54 | [b1] subc .l1x A4,B4,A4 | ||
55 | || [b1] add .s2 -1, B1, B1 | ||
56 | |||
57 | ;; RETURN A may happen here (note: must happen before the next branch) | ||
58 | __divremu0: | ||
59 | cmpgt .l2 B1, 7, B0 | ||
60 | || [b1] subc .l1x A4,B4,A4 | ||
61 | || [b1] add .s2 -1, B1, B1 | ||
62 | [b1] subc .l1x A4,B4,A4 | ||
63 | || [b1] add .s2 -1, B1, B1 | ||
64 | || [b0] b .s1 __divremu0 | ||
65 | [b1] subc .l1x A4,B4,A4 | ||
66 | || [b1] add .s2 -1, B1, B1 | ||
67 | [b1] subc .l1x A4,B4,A4 | ||
68 | || [b1] add .s2 -1, B1, B1 | ||
69 | [b1] subc .l1x A4,B4,A4 | ||
70 | || [b1] add .s2 -1, B1, B1 | ||
71 | [b1] subc .l1x A4,B4,A4 | ||
72 | || [b1] add .s2 -1, B1, B1 | ||
73 | [b1] subc .l1x A4,B4,A4 | ||
74 | || [b1] add .s2 -1, B1, B1 | ||
75 | ;; loop backwards branch happens here | ||
76 | |||
77 | ret .s2 B3 | ||
78 | || mvk .s1 32, A1 | ||
79 | sub .l1 A1, A6, A6 | ||
80 | || extu .s1 A4, A6, A5 | ||
81 | shl .s1 A4, A6, A4 | ||
82 | shru .s1 A4, 1, A4 | ||
83 | || sub .l1 A6, 1, A6 | ||
84 | or .l1 A2, A4, A4 | ||
85 | shru .s1 A4, A6, A4 | ||
86 | nop | ||
87 | ENDPROC(__c6xabi_divremu) | ||
diff --git a/arch/c6x/lib/divu.S b/arch/c6x/lib/divu.S new file mode 100644 index 000000000000..64af3c006dd3 --- /dev/null +++ b/arch/c6x/lib/divu.S | |||
@@ -0,0 +1,98 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | ;; ABI considerations for the divide functions | ||
21 | ;; The following registers are call-used: | ||
22 | ;; __c6xabi_divi A0,A1,A2,A4,A6,B0,B1,B2,B4,B5 | ||
23 | ;; __c6xabi_divu A0,A1,A2,A4,A6,B0,B1,B2,B4 | ||
24 | ;; __c6xabi_remi A1,A2,A4,A5,A6,B0,B1,B2,B4 | ||
25 | ;; __c6xabi_remu A1,A4,A5,A7,B0,B1,B2,B4 | ||
26 | ;; | ||
27 | ;; In our implementation, divu and remu are leaf functions, | ||
28 | ;; while both divi and remi call into divu. | ||
29 | ;; A0 is not clobbered by any of the functions. | ||
30 | ;; divu does not clobber B2 either, which is taken advantage of | ||
31 | ;; in remi. | ||
32 | ;; divi uses B5 to hold the original return address during | ||
33 | ;; the call to divu. | ||
34 | ;; remi uses B2 and A5 to hold the input values during the | ||
35 | ;; call to divu. It stores B3 in on the stack. | ||
36 | |||
37 | .text | ||
38 | ENTRY(__c6xabi_divu) | ||
39 | ;; We use a series of up to 31 subc instructions. First, we find | ||
40 | ;; out how many leading zero bits there are in the divisor. This | ||
41 | ;; gives us both a shift count for aligning (shifting) the divisor | ||
42 | ;; to the, and the number of times we have to execute subc. | ||
43 | |||
44 | ;; At the end, we have both the remainder and most of the quotient | ||
45 | ;; in A4. The top bit of the quotient is computed first and is | ||
46 | ;; placed in A2. | ||
47 | |||
48 | ;; Return immediately if the dividend is zero. | ||
49 | mv .s2x A4, B1 | ||
50 | [B1] lmbd .l2 1, B4, B1 | ||
51 | || [!B1] b .s2 B3 ; RETURN A | ||
52 | || [!B1] mvk .d2 1, B4 | ||
53 | mv .l1x B1, A6 | ||
54 | || shl .s2 B4, B1, B4 | ||
55 | |||
56 | ;; The loop performs a maximum of 28 steps, so we do the | ||
57 | ;; first 3 here. | ||
58 | cmpltu .l1x A4, B4, A2 | ||
59 | [!A2] sub .l1x A4, B4, A4 | ||
60 | || shru .s2 B4, 1, B4 | ||
61 | || xor .s1 1, A2, A2 | ||
62 | |||
63 | shl .s1 A2, 31, A2 | ||
64 | || [B1] subc .l1x A4,B4,A4 | ||
65 | || [B1] add .s2 -1, B1, B1 | ||
66 | [B1] subc .l1x A4,B4,A4 | ||
67 | || [B1] add .s2 -1, B1, B1 | ||
68 | |||
69 | ;; RETURN A may happen here (note: must happen before the next branch) | ||
70 | _divu_loop: | ||
71 | cmpgt .l2 B1, 7, B0 | ||
72 | || [B1] subc .l1x A4,B4,A4 | ||
73 | || [B1] add .s2 -1, B1, B1 | ||
74 | [B1] subc .l1x A4,B4,A4 | ||
75 | || [B1] add .s2 -1, B1, B1 | ||
76 | || [B0] b .s1 _divu_loop | ||
77 | [B1] subc .l1x A4,B4,A4 | ||
78 | || [B1] add .s2 -1, B1, B1 | ||
79 | [B1] subc .l1x A4,B4,A4 | ||
80 | || [B1] add .s2 -1, B1, B1 | ||
81 | [B1] subc .l1x A4,B4,A4 | ||
82 | || [B1] add .s2 -1, B1, B1 | ||
83 | [B1] subc .l1x A4,B4,A4 | ||
84 | || [B1] add .s2 -1, B1, B1 | ||
85 | [B1] subc .l1x A4,B4,A4 | ||
86 | || [B1] add .s2 -1, B1, B1 | ||
87 | ;; loop backwards branch happens here | ||
88 | |||
89 | ret .s2 B3 | ||
90 | || mvk .s1 32, A1 | ||
91 | sub .l1 A1, A6, A6 | ||
92 | shl .s1 A4, A6, A4 | ||
93 | shru .s1 A4, 1, A4 | ||
94 | || sub .l1 A6, 1, A6 | ||
95 | or .l1 A2, A4, A4 | ||
96 | shru .s1 A4, A6, A4 | ||
97 | nop | ||
98 | ENDPROC(__c6xabi_divu) | ||
diff --git a/arch/c6x/lib/llshl.S b/arch/c6x/lib/llshl.S new file mode 100644 index 000000000000..7b105e2d1b78 --- /dev/null +++ b/arch/c6x/lib/llshl.S | |||
@@ -0,0 +1,37 @@ | |||
1 | ;; Copyright (C) 2010 Texas Instruments Incorporated | ||
2 | ;; Contributed by Mark Salter <msalter@redhat.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | ;; uint64_t __c6xabi_llshl(uint64_t val, uint shift) | ||
19 | |||
20 | #include <linux/linkage.h> | ||
21 | |||
22 | .text | ||
23 | ENTRY(__c6xabi_llshl) | ||
24 | mv .l1x B4,A1 | ||
25 | [!A1] b .s2 B3 ; just return if zero shift | ||
26 | mvk .s1 32,A0 | ||
27 | sub .d1 A0,A1,A0 | ||
28 | cmplt .l1 0,A0,A2 | ||
29 | [A2] shru .s1 A4,A0,A0 | ||
30 | [!A2] neg .l1 A0,A5 | ||
31 | || [A2] shl .s1 A5,A1,A5 | ||
32 | [!A2] shl .s1 A4,A5,A5 | ||
33 | || [A2] or .d1 A5,A0,A5 | ||
34 | || [!A2] mvk .l1 0,A4 | ||
35 | [A2] shl .s1 A4,A1,A4 | ||
36 | bnop .s2 B3,5 | ||
37 | ENDPROC(__c6xabi_llshl) | ||
diff --git a/arch/c6x/lib/llshr.S b/arch/c6x/lib/llshr.S new file mode 100644 index 000000000000..fde1bec7cf5a --- /dev/null +++ b/arch/c6x/lib/llshr.S | |||
@@ -0,0 +1,38 @@ | |||
1 | ;; Copyright (C) 2010 Texas Instruments Incorporated | ||
2 | ;; Contributed by Mark Salter <msalter@redhat.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | ;; uint64_t __c6xabi_llshr(uint64_t val, uint shift) | ||
19 | |||
20 | #include <linux/linkage.h> | ||
21 | |||
22 | .text | ||
23 | ENTRY(__c6xabi_llshr) | ||
24 | mv .l1x B4,A1 | ||
25 | [!A1] b .s2 B3 ; return if zero shift count | ||
26 | mvk .s1 32,A0 | ||
27 | sub .d1 A0,A1,A0 | ||
28 | cmplt .l1 0,A0,A2 | ||
29 | [A2] shl .s1 A5,A0,A0 | ||
30 | nop | ||
31 | [!A2] neg .l1 A0,A4 | ||
32 | || [A2] shru .s1 A4,A1,A4 | ||
33 | [!A2] shr .s1 A5,A4,A4 | ||
34 | || [A2] or .d1 A4,A0,A4 | ||
35 | [!A2] shr .s1 A5,0x1f,A5 | ||
36 | [A2] shr .s1 A5,A1,A5 | ||
37 | bnop .s2 B3,5 | ||
38 | ENDPROC(__c6xabi_llshr) | ||
diff --git a/arch/c6x/lib/llshru.S b/arch/c6x/lib/llshru.S new file mode 100644 index 000000000000..596ae3ff5c0f --- /dev/null +++ b/arch/c6x/lib/llshru.S | |||
@@ -0,0 +1,38 @@ | |||
1 | ;; Copyright (C) 2010 Texas Instruments Incorporated | ||
2 | ;; Contributed by Mark Salter <msalter@redhat.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | ;; uint64_t __c6xabi_llshru(uint64_t val, uint shift) | ||
19 | |||
20 | #include <linux/linkage.h> | ||
21 | |||
22 | .text | ||
23 | ENTRY(__c6xabi_llshru) | ||
24 | mv .l1x B4,A1 | ||
25 | [!A1] b .s2 B3 ; return if zero shift count | ||
26 | mvk .s1 32,A0 | ||
27 | sub .d1 A0,A1,A0 | ||
28 | cmplt .l1 0,A0,A2 | ||
29 | [A2] shl .s1 A5,A0,A0 | ||
30 | nop | ||
31 | [!A2] neg .l1 A0,A4 | ||
32 | || [A2] shru .s1 A4,A1,A4 | ||
33 | [!A2] shru .s1 A5,A4,A4 | ||
34 | || [A2] or .d1 A4,A0,A4 | ||
35 | || [!A2] mvk .l1 0,A5 | ||
36 | [A2] shru .s1 A5,A1,A5 | ||
37 | bnop .s2 B3,5 | ||
38 | ENDPROC(__c6xabi_llshru) | ||
diff --git a/arch/c6x/lib/memcpy_64plus.S b/arch/c6x/lib/memcpy_64plus.S new file mode 100644 index 000000000000..0bbc2cbf9318 --- /dev/null +++ b/arch/c6x/lib/memcpy_64plus.S | |||
@@ -0,0 +1,46 @@ | |||
1 | ; Port on Texas Instruments TMS320C6x architecture | ||
2 | ; | ||
3 | ; Copyright (C) 2006, 2009, 2010 Texas Instruments Incorporated | ||
4 | ; Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
5 | ; | ||
6 | ; This program is free software; you can redistribute it and/or modify | ||
7 | ; it under the terms of the GNU General Public License version 2 as | ||
8 | ; published by the Free Software Foundation. | ||
9 | ; | ||
10 | |||
11 | #include <linux/linkage.h> | ||
12 | |||
13 | .text | ||
14 | |||
15 | ENTRY(memcpy) | ||
16 | AND .L1 0x1,A6,A0 | ||
17 | || AND .S1 0x2,A6,A1 | ||
18 | || AND .L2X 0x4,A6,B0 | ||
19 | || MV .D1 A4,A3 | ||
20 | || MVC .S2 ILC,B2 | ||
21 | |||
22 | [A0] LDB .D2T1 *B4++,A5 | ||
23 | [A1] LDB .D2T1 *B4++,A7 | ||
24 | [A1] LDB .D2T1 *B4++,A8 | ||
25 | [B0] LDNW .D2T1 *B4++,A9 | ||
26 | || SHRU .S2X A6,0x3,B1 | ||
27 | [!B1] BNOP .S2 B3,1 | ||
28 | |||
29 | [A0] STB .D1T1 A5,*A3++ | ||
30 | ||[B1] MVC .S2 B1,ILC | ||
31 | [A1] STB .D1T1 A7,*A3++ | ||
32 | [A1] STB .D1T1 A8,*A3++ | ||
33 | [B0] STNW .D1T1 A9,*A3++ ; return when len < 8 | ||
34 | |||
35 | SPLOOP 2 | ||
36 | |||
37 | LDNDW .D2T1 *B4++,A9:A8 | ||
38 | NOP 3 | ||
39 | |||
40 | NOP | ||
41 | SPKERNEL 0,0 | ||
42 | || STNDW .D1T1 A9:A8,*A3++ | ||
43 | |||
44 | BNOP .S2 B3,4 | ||
45 | MVC .S2 B2,ILC | ||
46 | ENDPROC(memcpy) | ||
diff --git a/arch/c6x/lib/mpyll.S b/arch/c6x/lib/mpyll.S new file mode 100644 index 000000000000..f1034418b4db --- /dev/null +++ b/arch/c6x/lib/mpyll.S | |||
@@ -0,0 +1,49 @@ | |||
1 | ;; Copyright (C) 2010 Texas Instruments Incorporated | ||
2 | ;; Contributed by Mark Salter <msalter@redhat.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | ;; uint64_t __c6xabi_mpyll(uint64_t x, uint64_t y) | ||
21 | ;; | ||
22 | ;; 64x64 multiply | ||
23 | ;; First compute partial results using 32-bit parts of x and y: | ||
24 | ;; | ||
25 | ;; b63 b32 b31 b0 | ||
26 | ;; ----------------------------- | ||
27 | ;; | 1 | 0 | | ||
28 | ;; ----------------------------- | ||
29 | ;; | ||
30 | ;; P0 = X0*Y0 | ||
31 | ;; P1 = X0*Y1 + X1*Y0 | ||
32 | ;; P2 = X1*Y1 | ||
33 | ;; | ||
34 | ;; result = (P2 << 64) + (P1 << 32) + P0 | ||
35 | ;; | ||
36 | ;; Since the result is also 64-bit, we can skip the P2 term. | ||
37 | |||
38 | .text | ||
39 | ENTRY(__c6xabi_mpyll) | ||
40 | mpy32u .m1x A4,B4,A1:A0 ; X0*Y0 | ||
41 | b .s2 B3 | ||
42 | || mpy32u .m2x B5,A4,B1:B0 ; X0*Y1 (don't need upper 32-bits) | ||
43 | || mpy32u .m1x A5,B4,A3:A2 ; X1*Y0 (don't need upper 32-bits) | ||
44 | nop | ||
45 | nop | ||
46 | mv .s1 A0,A4 | ||
47 | add .l1x A2,B0,A5 | ||
48 | add .s1 A1,A5,A5 | ||
49 | ENDPROC(__c6xabi_mpyll) | ||
diff --git a/arch/c6x/lib/negll.S b/arch/c6x/lib/negll.S new file mode 100644 index 000000000000..82f4bcec9afb --- /dev/null +++ b/arch/c6x/lib/negll.S | |||
@@ -0,0 +1,31 @@ | |||
1 | ;; Copyright (C) 2010 Texas Instruments Incorporated | ||
2 | ;; Contributed by Mark Salter <msalter@redhat.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | ;; int64_t __c6xabi_negll(int64_t val) | ||
19 | |||
20 | #include <linux/linkage.h> | ||
21 | |||
22 | .text | ||
23 | ENTRY(__c6xabi_negll) | ||
24 | b .s2 B3 | ||
25 | mvk .l1 0,A0 | ||
26 | subu .l1 A0,A4,A3:A2 | ||
27 | sub .l1 A0,A5,A0 | ||
28 | || ext .s1 A3,24,24,A5 | ||
29 | add .l1 A5,A0,A5 | ||
30 | mv .s1 A2,A4 | ||
31 | ENDPROC(__c6xabi_negll) | ||
diff --git a/arch/c6x/lib/pop_rts.S b/arch/c6x/lib/pop_rts.S new file mode 100644 index 000000000000..d7d96c70e9e7 --- /dev/null +++ b/arch/c6x/lib/pop_rts.S | |||
@@ -0,0 +1,32 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | .text | ||
21 | |||
22 | ENTRY(__c6xabi_pop_rts) | ||
23 | lddw .d2t2 *++B15, B3:B2 | ||
24 | lddw .d2t1 *++B15, A11:A10 | ||
25 | lddw .d2t2 *++B15, B11:B10 | ||
26 | lddw .d2t1 *++B15, A13:A12 | ||
27 | lddw .d2t2 *++B15, B13:B12 | ||
28 | lddw .d2t1 *++B15, A15:A14 | ||
29 | || b .s2 B3 | ||
30 | ldw .d2t2 *++B15[2], B14 | ||
31 | nop 4 | ||
32 | ENDPROC(__c6xabi_pop_rts) | ||
diff --git a/arch/c6x/lib/push_rts.S b/arch/c6x/lib/push_rts.S new file mode 100644 index 000000000000..f6e3db3b6065 --- /dev/null +++ b/arch/c6x/lib/push_rts.S | |||
@@ -0,0 +1,31 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | .text | ||
21 | |||
22 | ENTRY(__c6xabi_push_rts) | ||
23 | stw .d2t2 B14, *B15--[2] | ||
24 | stdw .d2t1 A15:A14, *B15-- | ||
25 | || b .s2x A3 | ||
26 | stdw .d2t2 B13:B12, *B15-- | ||
27 | stdw .d2t1 A13:A12, *B15-- | ||
28 | stdw .d2t2 B11:B10, *B15-- | ||
29 | stdw .d2t1 A11:A10, *B15-- | ||
30 | stdw .d2t2 B3:B2, *B15-- | ||
31 | ENDPROC(__c6xabi_push_rts) | ||
diff --git a/arch/c6x/lib/remi.S b/arch/c6x/lib/remi.S new file mode 100644 index 000000000000..6f2ca18c3f98 --- /dev/null +++ b/arch/c6x/lib/remi.S | |||
@@ -0,0 +1,64 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | ;; ABI considerations for the divide functions | ||
21 | ;; The following registers are call-used: | ||
22 | ;; __c6xabi_divi A0,A1,A2,A4,A6,B0,B1,B2,B4,B5 | ||
23 | ;; __c6xabi_divu A0,A1,A2,A4,A6,B0,B1,B2,B4 | ||
24 | ;; __c6xabi_remi A1,A2,A4,A5,A6,B0,B1,B2,B4 | ||
25 | ;; __c6xabi_remu A1,A4,A5,A7,B0,B1,B2,B4 | ||
26 | ;; | ||
27 | ;; In our implementation, divu and remu are leaf functions, | ||
28 | ;; while both divi and remi call into divu. | ||
29 | ;; A0 is not clobbered by any of the functions. | ||
30 | ;; divu does not clobber B2 either, which is taken advantage of | ||
31 | ;; in remi. | ||
32 | ;; divi uses B5 to hold the original return address during | ||
33 | ;; the call to divu. | ||
34 | ;; remi uses B2 and A5 to hold the input values during the | ||
35 | ;; call to divu. It stores B3 in on the stack. | ||
36 | |||
37 | .text | ||
38 | |||
39 | ENTRY(__c6xabi_remi) | ||
40 | stw .d2t2 B3, *B15--[2] | ||
41 | || cmpgt .l1 0, A4, A1 | ||
42 | || cmpgt .l2 0, B4, B2 | ||
43 | || mv .s1 A4, A5 | ||
44 | || call .s2 __c6xabi_divu | ||
45 | |||
46 | [A1] neg .l1 A4, A4 | ||
47 | || [B2] neg .l2 B4, B4 | ||
48 | || xor .s2x B2, A1, B0 | ||
49 | || mv .d2 B4, B2 | ||
50 | |||
51 | [B0] addkpc .s2 _divu_ret_1, B3, 1 | ||
52 | [!B0] addkpc .s2 _divu_ret_2, B3, 1 | ||
53 | nop 2 | ||
54 | _divu_ret_1: | ||
55 | neg .l1 A4, A4 | ||
56 | _divu_ret_2: | ||
57 | ldw .d2t2 *++B15[2], B3 | ||
58 | |||
59 | mpy32 .m1x A4, B2, A6 | ||
60 | nop 3 | ||
61 | ret .s2 B3 | ||
62 | sub .l1 A5, A6, A4 | ||
63 | nop 4 | ||
64 | ENDPROC(__c6xabi_remi) | ||
diff --git a/arch/c6x/lib/remu.S b/arch/c6x/lib/remu.S new file mode 100644 index 000000000000..3fae719185ab --- /dev/null +++ b/arch/c6x/lib/remu.S | |||
@@ -0,0 +1,82 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | ;; ABI considerations for the divide functions | ||
21 | ;; The following registers are call-used: | ||
22 | ;; __c6xabi_divi A0,A1,A2,A4,A6,B0,B1,B2,B4,B5 | ||
23 | ;; __c6xabi_divu A0,A1,A2,A4,A6,B0,B1,B2,B4 | ||
24 | ;; __c6xabi_remi A1,A2,A4,A5,A6,B0,B1,B2,B4 | ||
25 | ;; __c6xabi_remu A1,A4,A5,A7,B0,B1,B2,B4 | ||
26 | ;; | ||
27 | ;; In our implementation, divu and remu are leaf functions, | ||
28 | ;; while both divi and remi call into divu. | ||
29 | ;; A0 is not clobbered by any of the functions. | ||
30 | ;; divu does not clobber B2 either, which is taken advantage of | ||
31 | ;; in remi. | ||
32 | ;; divi uses B5 to hold the original return address during | ||
33 | ;; the call to divu. | ||
34 | ;; remi uses B2 and A5 to hold the input values during the | ||
35 | ;; call to divu. It stores B3 in on the stack. | ||
36 | |||
37 | |||
38 | .text | ||
39 | |||
40 | ENTRY(__c6xabi_remu) | ||
41 | ;; The ABI seems designed to prevent these functions calling each other, | ||
42 | ;; so we duplicate most of the divsi3 code here. | ||
43 | mv .s2x A4, B1 | ||
44 | lmbd .l2 1, B4, B1 | ||
45 | || [!B1] b .s2 B3 ; RETURN A | ||
46 | || [!B1] mvk .d2 1, B4 | ||
47 | |||
48 | mv .l1x B1, A7 | ||
49 | || shl .s2 B4, B1, B4 | ||
50 | |||
51 | cmpltu .l1x A4, B4, A1 | ||
52 | [!A1] sub .l1x A4, B4, A4 | ||
53 | shru .s2 B4, 1, B4 | ||
54 | |||
55 | _remu_loop: | ||
56 | cmpgt .l2 B1, 7, B0 | ||
57 | || [B1] subc .l1x A4,B4,A4 | ||
58 | || [B1] add .s2 -1, B1, B1 | ||
59 | ;; RETURN A may happen here (note: must happen before the next branch) | ||
60 | [B1] subc .l1x A4,B4,A4 | ||
61 | || [B1] add .s2 -1, B1, B1 | ||
62 | || [B0] b .s1 _remu_loop | ||
63 | [B1] subc .l1x A4,B4,A4 | ||
64 | || [B1] add .s2 -1, B1, B1 | ||
65 | [B1] subc .l1x A4,B4,A4 | ||
66 | || [B1] add .s2 -1, B1, B1 | ||
67 | [B1] subc .l1x A4,B4,A4 | ||
68 | || [B1] add .s2 -1, B1, B1 | ||
69 | [B1] subc .l1x A4,B4,A4 | ||
70 | || [B1] add .s2 -1, B1, B1 | ||
71 | [B1] subc .l1x A4,B4,A4 | ||
72 | || [B1] add .s2 -1, B1, B1 | ||
73 | ;; loop backwards branch happens here | ||
74 | |||
75 | ret .s2 B3 | ||
76 | [B1] subc .l1x A4,B4,A4 | ||
77 | || [B1] add .s2 -1, B1, B1 | ||
78 | [B1] subc .l1x A4,B4,A4 | ||
79 | |||
80 | extu .s1 A4, A7, A4 | ||
81 | nop 2 | ||
82 | ENDPROC(__c6xabi_remu) | ||
diff --git a/arch/c6x/lib/strasgi.S b/arch/c6x/lib/strasgi.S new file mode 100644 index 000000000000..de2740765536 --- /dev/null +++ b/arch/c6x/lib/strasgi.S | |||
@@ -0,0 +1,89 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | .text | ||
21 | |||
22 | ENTRY(__c6xabi_strasgi) | ||
23 | ;; This is essentially memcpy, with alignment known to be at least | ||
24 | ;; 4, and the size a multiple of 4 greater than or equal to 28. | ||
25 | ldw .d2t1 *B4++, A0 | ||
26 | || mvk .s2 16, B1 | ||
27 | ldw .d2t1 *B4++, A1 | ||
28 | || mvk .s2 20, B2 | ||
29 | || sub .d1 A6, 24, A6 | ||
30 | ldw .d2t1 *B4++, A5 | ||
31 | ldw .d2t1 *B4++, A7 | ||
32 | || mv .l2x A6, B7 | ||
33 | ldw .d2t1 *B4++, A8 | ||
34 | ldw .d2t1 *B4++, A9 | ||
35 | || mv .s2x A0, B5 | ||
36 | || cmpltu .l2 B2, B7, B0 | ||
37 | |||
38 | _strasgi_loop: | ||
39 | stw .d1t2 B5, *A4++ | ||
40 | || [B0] ldw .d2t1 *B4++, A0 | ||
41 | || mv .s2x A1, B5 | ||
42 | || mv .l2 B7, B6 | ||
43 | |||
44 | [B0] sub .d2 B6, 24, B7 | ||
45 | || [B0] b .s2 _strasgi_loop | ||
46 | || cmpltu .l2 B1, B6, B0 | ||
47 | |||
48 | [B0] ldw .d2t1 *B4++, A1 | ||
49 | || stw .d1t2 B5, *A4++ | ||
50 | || mv .s2x A5, B5 | ||
51 | || cmpltu .l2 12, B6, B0 | ||
52 | |||
53 | [B0] ldw .d2t1 *B4++, A5 | ||
54 | || stw .d1t2 B5, *A4++ | ||
55 | || mv .s2x A7, B5 | ||
56 | || cmpltu .l2 8, B6, B0 | ||
57 | |||
58 | [B0] ldw .d2t1 *B4++, A7 | ||
59 | || stw .d1t2 B5, *A4++ | ||
60 | || mv .s2x A8, B5 | ||
61 | || cmpltu .l2 4, B6, B0 | ||
62 | |||
63 | [B0] ldw .d2t1 *B4++, A8 | ||
64 | || stw .d1t2 B5, *A4++ | ||
65 | || mv .s2x A9, B5 | ||
66 | || cmpltu .l2 0, B6, B0 | ||
67 | |||
68 | [B0] ldw .d2t1 *B4++, A9 | ||
69 | || stw .d1t2 B5, *A4++ | ||
70 | || mv .s2x A0, B5 | ||
71 | || cmpltu .l2 B2, B7, B0 | ||
72 | |||
73 | ;; loop back branch happens here | ||
74 | |||
75 | cmpltu .l2 B1, B6, B0 | ||
76 | || ret .s2 b3 | ||
77 | |||
78 | [B0] stw .d1t1 A1, *A4++ | ||
79 | || cmpltu .l2 12, B6, B0 | ||
80 | [B0] stw .d1t1 A5, *A4++ | ||
81 | || cmpltu .l2 8, B6, B0 | ||
82 | [B0] stw .d1t1 A7, *A4++ | ||
83 | || cmpltu .l2 4, B6, B0 | ||
84 | [B0] stw .d1t1 A8, *A4++ | ||
85 | || cmpltu .l2 0, B6, B0 | ||
86 | [B0] stw .d1t1 A9, *A4++ | ||
87 | |||
88 | ;; return happens here | ||
89 | ENDPROC(__c6xabi_strasgi) | ||
diff --git a/arch/c6x/lib/strasgi_64plus.S b/arch/c6x/lib/strasgi_64plus.S new file mode 100644 index 000000000000..c9fd159b5fa2 --- /dev/null +++ b/arch/c6x/lib/strasgi_64plus.S | |||
@@ -0,0 +1,39 @@ | |||
1 | ;; Copyright 2010 Free Software Foundation, Inc. | ||
2 | ;; Contributed by Bernd Schmidt <bernds@codesourcery.com>. | ||
3 | ;; | ||
4 | ;; This program is free software; you can redistribute it and/or modify | ||
5 | ;; it under the terms of the GNU General Public License as published by | ||
6 | ;; the Free Software Foundation; either version 2 of the License, or | ||
7 | ;; (at your option) any later version. | ||
8 | ;; | ||
9 | ;; This program is distributed in the hope that it will be useful, | ||
10 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | ;; GNU General Public License for more details. | ||
13 | ;; | ||
14 | ;; You should have received a copy of the GNU General Public License | ||
15 | ;; along with this program; if not, write to the Free Software | ||
16 | ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | #include <linux/linkage.h> | ||
19 | |||
20 | .text | ||
21 | |||
22 | ENTRY(__c6xabi_strasgi_64plus) | ||
23 | shru .s2x a6, 2, b31 | ||
24 | || mv .s1 a4, a30 | ||
25 | || mv .d2 b4, b30 | ||
26 | |||
27 | add .s2 -4, b31, b31 | ||
28 | |||
29 | sploopd 1 | ||
30 | || mvc .s2 b31, ilc | ||
31 | ldw .d2t2 *b30++, b31 | ||
32 | nop 4 | ||
33 | mv .s1x b31,a31 | ||
34 | spkernel 6, 0 | ||
35 | || stw .d1t1 a31, *a30++ | ||
36 | |||
37 | ret .s2 b3 | ||
38 | nop 5 | ||
39 | ENDPROC(__c6xabi_strasgi_64plus) | ||
diff --git a/arch/c6x/mm/Makefile b/arch/c6x/mm/Makefile new file mode 100644 index 000000000000..136a97576c61 --- /dev/null +++ b/arch/c6x/mm/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Makefile for the linux c6x-specific parts of the memory manager. | ||
3 | # | ||
4 | |||
5 | obj-y := init.o dma-coherent.o | ||
diff --git a/arch/c6x/mm/dma-coherent.c b/arch/c6x/mm/dma-coherent.c new file mode 100644 index 000000000000..4187e5180373 --- /dev/null +++ b/arch/c6x/mm/dma-coherent.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot <aurelien.jacquiot@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * DMA uncached mapping support. | ||
12 | * | ||
13 | * Using code pulled from ARM | ||
14 | * Copyright (C) 2000-2004 Russell King | ||
15 | * | ||
16 | */ | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/bitmap.h> | ||
19 | #include <linux/bitops.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/dma-mapping.h> | ||
23 | #include <linux/memblock.h> | ||
24 | |||
25 | #include <asm/page.h> | ||
26 | |||
27 | /* | ||
28 | * DMA coherent memory management, can be redefined using the memdma= | ||
29 | * kernel command line | ||
30 | */ | ||
31 | |||
32 | /* none by default */ | ||
33 | static phys_addr_t dma_base; | ||
34 | static u32 dma_size; | ||
35 | static u32 dma_pages; | ||
36 | |||
37 | static unsigned long *dma_bitmap; | ||
38 | |||
39 | /* bitmap lock */ | ||
40 | static DEFINE_SPINLOCK(dma_lock); | ||
41 | |||
42 | /* | ||
43 | * Return a DMA coherent and contiguous memory chunk from the DMA memory | ||
44 | */ | ||
45 | static inline u32 __alloc_dma_pages(int order) | ||
46 | { | ||
47 | unsigned long flags; | ||
48 | u32 pos; | ||
49 | |||
50 | spin_lock_irqsave(&dma_lock, flags); | ||
51 | pos = bitmap_find_free_region(dma_bitmap, dma_pages, order); | ||
52 | spin_unlock_irqrestore(&dma_lock, flags); | ||
53 | |||
54 | return dma_base + (pos << PAGE_SHIFT); | ||
55 | } | ||
56 | |||
57 | static void __free_dma_pages(u32 addr, int order) | ||
58 | { | ||
59 | unsigned long flags; | ||
60 | u32 pos = (addr - dma_base) >> PAGE_SHIFT; | ||
61 | |||
62 | if (addr < dma_base || (pos + (1 << order)) >= dma_pages) { | ||
63 | printk(KERN_ERR "%s: freeing outside range.\n", __func__); | ||
64 | BUG(); | ||
65 | } | ||
66 | |||
67 | spin_lock_irqsave(&dma_lock, flags); | ||
68 | bitmap_release_region(dma_bitmap, pos, order); | ||
69 | spin_unlock_irqrestore(&dma_lock, flags); | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Allocate DMA coherent memory space and return both the kernel | ||
74 | * virtual and DMA address for that space. | ||
75 | */ | ||
76 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
77 | dma_addr_t *handle, gfp_t gfp) | ||
78 | { | ||
79 | u32 paddr; | ||
80 | int order; | ||
81 | |||
82 | if (!dma_size || !size) | ||
83 | return NULL; | ||
84 | |||
85 | order = get_count_order(((size - 1) >> PAGE_SHIFT) + 1); | ||
86 | |||
87 | paddr = __alloc_dma_pages(order); | ||
88 | |||
89 | if (handle) | ||
90 | *handle = paddr; | ||
91 | |||
92 | if (!paddr) | ||
93 | return NULL; | ||
94 | |||
95 | return phys_to_virt(paddr); | ||
96 | } | ||
97 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
98 | |||
99 | /* | ||
100 | * Free DMA coherent memory as defined by the above mapping. | ||
101 | */ | ||
102 | void dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||
103 | dma_addr_t dma_handle) | ||
104 | { | ||
105 | int order; | ||
106 | |||
107 | if (!dma_size || !size) | ||
108 | return; | ||
109 | |||
110 | order = get_count_order(((size - 1) >> PAGE_SHIFT) + 1); | ||
111 | |||
112 | __free_dma_pages(virt_to_phys(vaddr), order); | ||
113 | } | ||
114 | EXPORT_SYMBOL(dma_free_coherent); | ||
115 | |||
116 | /* | ||
117 | * Initialise the coherent DMA memory allocator using the given uncached region. | ||
118 | */ | ||
119 | void __init coherent_mem_init(phys_addr_t start, u32 size) | ||
120 | { | ||
121 | phys_addr_t bitmap_phys; | ||
122 | |||
123 | if (!size) | ||
124 | return; | ||
125 | |||
126 | printk(KERN_INFO | ||
127 | "Coherent memory (DMA) region start=0x%x size=0x%x\n", | ||
128 | start, size); | ||
129 | |||
130 | dma_base = start; | ||
131 | dma_size = size; | ||
132 | |||
133 | /* allocate bitmap */ | ||
134 | dma_pages = dma_size >> PAGE_SHIFT; | ||
135 | if (dma_size & (PAGE_SIZE - 1)) | ||
136 | ++dma_pages; | ||
137 | |||
138 | bitmap_phys = memblock_alloc(BITS_TO_LONGS(dma_pages) * sizeof(long), | ||
139 | sizeof(long)); | ||
140 | |||
141 | dma_bitmap = phys_to_virt(bitmap_phys); | ||
142 | memset(dma_bitmap, 0, dma_pages * PAGE_SIZE); | ||
143 | } | ||
diff --git a/arch/c6x/mm/init.c b/arch/c6x/mm/init.c new file mode 100644 index 000000000000..89395f09648a --- /dev/null +++ b/arch/c6x/mm/init.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated | ||
5 | * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/swap.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/bootmem.h> | ||
15 | #ifdef CONFIG_BLK_DEV_RAM | ||
16 | #include <linux/blkdev.h> | ||
17 | #endif | ||
18 | #include <linux/initrd.h> | ||
19 | |||
20 | #include <asm/sections.h> | ||
21 | |||
22 | /* | ||
23 | * ZERO_PAGE is a special page that is used for zero-initialized | ||
24 | * data and COW. | ||
25 | */ | ||
26 | unsigned long empty_zero_page; | ||
27 | EXPORT_SYMBOL(empty_zero_page); | ||
28 | |||
29 | /* | ||
30 | * paging_init() continues the virtual memory environment setup which | ||
31 | * was begun by the code in arch/head.S. | ||
32 | * The parameters are pointers to where to stick the starting and ending | ||
33 | * addresses of available kernel virtual memory. | ||
34 | */ | ||
35 | void __init paging_init(void) | ||
36 | { | ||
37 | struct pglist_data *pgdat = NODE_DATA(0); | ||
38 | unsigned long zones_size[MAX_NR_ZONES] = {0, }; | ||
39 | |||
40 | empty_zero_page = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); | ||
41 | memset((void *)empty_zero_page, 0, PAGE_SIZE); | ||
42 | |||
43 | /* | ||
44 | * Set up user data space | ||
45 | */ | ||
46 | set_fs(KERNEL_DS); | ||
47 | |||
48 | /* | ||
49 | * Define zones | ||
50 | */ | ||
51 | zones_size[ZONE_NORMAL] = (memory_end - PAGE_OFFSET) >> PAGE_SHIFT; | ||
52 | pgdat->node_zones[ZONE_NORMAL].zone_start_pfn = | ||
53 | __pa(PAGE_OFFSET) >> PAGE_SHIFT; | ||
54 | |||
55 | free_area_init(zones_size); | ||
56 | } | ||
57 | |||
58 | void __init mem_init(void) | ||
59 | { | ||
60 | int codek, datak; | ||
61 | unsigned long tmp; | ||
62 | unsigned long len = memory_end - memory_start; | ||
63 | |||
64 | high_memory = (void *)(memory_end & PAGE_MASK); | ||
65 | |||
66 | /* this will put all memory onto the freelists */ | ||
67 | totalram_pages = free_all_bootmem(); | ||
68 | |||
69 | codek = (_etext - _stext) >> 10; | ||
70 | datak = (_end - _sdata) >> 10; | ||
71 | |||
72 | tmp = nr_free_pages() << PAGE_SHIFT; | ||
73 | printk(KERN_INFO "Memory: %luk/%luk RAM (%dk kernel code, %dk data)\n", | ||
74 | tmp >> 10, len >> 10, codek, datak); | ||
75 | } | ||
76 | |||
77 | #ifdef CONFIG_BLK_DEV_INITRD | ||
78 | void __init free_initrd_mem(unsigned long start, unsigned long end) | ||
79 | { | ||
80 | int pages = 0; | ||
81 | for (; start < end; start += PAGE_SIZE) { | ||
82 | ClearPageReserved(virt_to_page(start)); | ||
83 | init_page_count(virt_to_page(start)); | ||
84 | free_page(start); | ||
85 | totalram_pages++; | ||
86 | pages++; | ||
87 | } | ||
88 | printk(KERN_INFO "Freeing initrd memory: %luk freed\n", | ||
89 | (pages * PAGE_SIZE) >> 10); | ||
90 | } | ||
91 | #endif | ||
92 | |||
93 | void __init free_initmem(void) | ||
94 | { | ||
95 | unsigned long addr; | ||
96 | |||
97 | /* | ||
98 | * The following code should be cool even if these sections | ||
99 | * are not page aligned. | ||
100 | */ | ||
101 | addr = PAGE_ALIGN((unsigned long)(__init_begin)); | ||
102 | |||
103 | /* next to check that the page we free is not a partial page */ | ||
104 | for (; addr + PAGE_SIZE < (unsigned long)(__init_end); | ||
105 | addr += PAGE_SIZE) { | ||
106 | ClearPageReserved(virt_to_page(addr)); | ||
107 | init_page_count(virt_to_page(addr)); | ||
108 | free_page(addr); | ||
109 | totalram_pages++; | ||
110 | } | ||
111 | printk(KERN_INFO "Freeing unused kernel memory: %dK freed\n", | ||
112 | (int) ((addr - PAGE_ALIGN((long) &__init_begin)) >> 10)); | ||
113 | } | ||
diff --git a/arch/c6x/platforms/Kconfig b/arch/c6x/platforms/Kconfig new file mode 100644 index 000000000000..401ee678fd01 --- /dev/null +++ b/arch/c6x/platforms/Kconfig | |||
@@ -0,0 +1,16 @@ | |||
1 | |||
2 | config SOC_TMS320C6455 | ||
3 | bool "TMS320C6455" | ||
4 | default n | ||
5 | |||
6 | config SOC_TMS320C6457 | ||
7 | bool "TMS320C6457" | ||
8 | default n | ||
9 | |||
10 | config SOC_TMS320C6472 | ||
11 | bool "TMS320C6472" | ||
12 | default n | ||
13 | |||
14 | config SOC_TMS320C6474 | ||
15 | bool "TMS320C6474" | ||
16 | default n | ||
diff --git a/arch/c6x/platforms/Makefile b/arch/c6x/platforms/Makefile new file mode 100644 index 000000000000..9a95b9bca8d0 --- /dev/null +++ b/arch/c6x/platforms/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # Makefile for arch/c6x/platforms | ||
3 | # | ||
4 | # Copyright 2010, 2011 Texas Instruments Incorporated | ||
5 | # | ||
6 | |||
7 | obj-y = platform.o cache.o megamod-pic.o pll.o plldata.o timer64.o | ||
8 | obj-y += dscr.o | ||
9 | |||
10 | # SoC objects | ||
11 | obj-$(CONFIG_SOC_TMS320C6455) += emif.o | ||
12 | obj-$(CONFIG_SOC_TMS320C6457) += emif.o | ||
diff --git a/arch/c6x/platforms/cache.c b/arch/c6x/platforms/cache.c new file mode 100644 index 000000000000..86318a16a252 --- /dev/null +++ b/arch/c6x/platforms/cache.c | |||
@@ -0,0 +1,445 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
3 | * Author: Mark Salter <msalter@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #include <linux/of.h> | ||
10 | #include <linux/of_address.h> | ||
11 | #include <linux/io.h> | ||
12 | |||
13 | #include <asm/cache.h> | ||
14 | #include <asm/soc.h> | ||
15 | |||
16 | /* | ||
17 | * Internal Memory Control Registers for caches | ||
18 | */ | ||
19 | #define IMCR_CCFG 0x0000 | ||
20 | #define IMCR_L1PCFG 0x0020 | ||
21 | #define IMCR_L1PCC 0x0024 | ||
22 | #define IMCR_L1DCFG 0x0040 | ||
23 | #define IMCR_L1DCC 0x0044 | ||
24 | #define IMCR_L2ALLOC0 0x2000 | ||
25 | #define IMCR_L2ALLOC1 0x2004 | ||
26 | #define IMCR_L2ALLOC2 0x2008 | ||
27 | #define IMCR_L2ALLOC3 0x200c | ||
28 | #define IMCR_L2WBAR 0x4000 | ||
29 | #define IMCR_L2WWC 0x4004 | ||
30 | #define IMCR_L2WIBAR 0x4010 | ||
31 | #define IMCR_L2WIWC 0x4014 | ||
32 | #define IMCR_L2IBAR 0x4018 | ||
33 | #define IMCR_L2IWC 0x401c | ||
34 | #define IMCR_L1PIBAR 0x4020 | ||
35 | #define IMCR_L1PIWC 0x4024 | ||
36 | #define IMCR_L1DWIBAR 0x4030 | ||
37 | #define IMCR_L1DWIWC 0x4034 | ||
38 | #define IMCR_L1DWBAR 0x4040 | ||
39 | #define IMCR_L1DWWC 0x4044 | ||
40 | #define IMCR_L1DIBAR 0x4048 | ||
41 | #define IMCR_L1DIWC 0x404c | ||
42 | #define IMCR_L2WB 0x5000 | ||
43 | #define IMCR_L2WBINV 0x5004 | ||
44 | #define IMCR_L2INV 0x5008 | ||
45 | #define IMCR_L1PINV 0x5028 | ||
46 | #define IMCR_L1DWB 0x5040 | ||
47 | #define IMCR_L1DWBINV 0x5044 | ||
48 | #define IMCR_L1DINV 0x5048 | ||
49 | #define IMCR_MAR_BASE 0x8000 | ||
50 | #define IMCR_MAR96_111 0x8180 | ||
51 | #define IMCR_MAR128_191 0x8200 | ||
52 | #define IMCR_MAR224_239 0x8380 | ||
53 | #define IMCR_L2MPFAR 0xa000 | ||
54 | #define IMCR_L2MPFSR 0xa004 | ||
55 | #define IMCR_L2MPFCR 0xa008 | ||
56 | #define IMCR_L2MPLK0 0xa100 | ||
57 | #define IMCR_L2MPLK1 0xa104 | ||
58 | #define IMCR_L2MPLK2 0xa108 | ||
59 | #define IMCR_L2MPLK3 0xa10c | ||
60 | #define IMCR_L2MPLKCMD 0xa110 | ||
61 | #define IMCR_L2MPLKSTAT 0xa114 | ||
62 | #define IMCR_L2MPPA_BASE 0xa200 | ||
63 | #define IMCR_L1PMPFAR 0xa400 | ||
64 | #define IMCR_L1PMPFSR 0xa404 | ||
65 | #define IMCR_L1PMPFCR 0xa408 | ||
66 | #define IMCR_L1PMPLK0 0xa500 | ||
67 | #define IMCR_L1PMPLK1 0xa504 | ||
68 | #define IMCR_L1PMPLK2 0xa508 | ||
69 | #define IMCR_L1PMPLK3 0xa50c | ||
70 | #define IMCR_L1PMPLKCMD 0xa510 | ||
71 | #define IMCR_L1PMPLKSTAT 0xa514 | ||
72 | #define IMCR_L1PMPPA_BASE 0xa600 | ||
73 | #define IMCR_L1DMPFAR 0xac00 | ||
74 | #define IMCR_L1DMPFSR 0xac04 | ||
75 | #define IMCR_L1DMPFCR 0xac08 | ||
76 | #define IMCR_L1DMPLK0 0xad00 | ||
77 | #define IMCR_L1DMPLK1 0xad04 | ||
78 | #define IMCR_L1DMPLK2 0xad08 | ||
79 | #define IMCR_L1DMPLK3 0xad0c | ||
80 | #define IMCR_L1DMPLKCMD 0xad10 | ||
81 | #define IMCR_L1DMPLKSTAT 0xad14 | ||
82 | #define IMCR_L1DMPPA_BASE 0xae00 | ||
83 | #define IMCR_L2PDWAKE0 0xc040 | ||
84 | #define IMCR_L2PDWAKE1 0xc044 | ||
85 | #define IMCR_L2PDSLEEP0 0xc050 | ||
86 | #define IMCR_L2PDSLEEP1 0xc054 | ||
87 | #define IMCR_L2PDSTAT0 0xc060 | ||
88 | #define IMCR_L2PDSTAT1 0xc064 | ||
89 | |||
90 | /* | ||
91 | * CCFG register values and bits | ||
92 | */ | ||
93 | #define L2MODE_0K_CACHE 0x0 | ||
94 | #define L2MODE_32K_CACHE 0x1 | ||
95 | #define L2MODE_64K_CACHE 0x2 | ||
96 | #define L2MODE_128K_CACHE 0x3 | ||
97 | #define L2MODE_256K_CACHE 0x7 | ||
98 | |||
99 | #define L2PRIO_URGENT 0x0 | ||
100 | #define L2PRIO_HIGH 0x1 | ||
101 | #define L2PRIO_MEDIUM 0x2 | ||
102 | #define L2PRIO_LOW 0x3 | ||
103 | |||
104 | #define CCFG_ID 0x100 /* Invalidate L1P bit */ | ||
105 | #define CCFG_IP 0x200 /* Invalidate L1D bit */ | ||
106 | |||
107 | static void __iomem *cache_base; | ||
108 | |||
109 | /* | ||
110 | * L1 & L2 caches generic functions | ||
111 | */ | ||
112 | #define imcr_get(reg) soc_readl(cache_base + (reg)) | ||
113 | #define imcr_set(reg, value) \ | ||
114 | do { \ | ||
115 | soc_writel((value), cache_base + (reg)); \ | ||
116 | soc_readl(cache_base + (reg)); \ | ||
117 | } while (0) | ||
118 | |||
119 | static void cache_block_operation_wait(unsigned int wc_reg) | ||
120 | { | ||
121 | /* Wait for completion */ | ||
122 | while (imcr_get(wc_reg)) | ||
123 | cpu_relax(); | ||
124 | } | ||
125 | |||
126 | static DEFINE_SPINLOCK(cache_lock); | ||
127 | |||
128 | /* | ||
129 | * Generic function to perform a block cache operation as | ||
130 | * invalidate or writeback/invalidate | ||
131 | */ | ||
132 | static void cache_block_operation(unsigned int *start, | ||
133 | unsigned int *end, | ||
134 | unsigned int bar_reg, | ||
135 | unsigned int wc_reg) | ||
136 | { | ||
137 | unsigned long flags; | ||
138 | unsigned int wcnt = | ||
139 | (L2_CACHE_ALIGN_CNT((unsigned int) end) | ||
140 | - L2_CACHE_ALIGN_LOW((unsigned int) start)) >> 2; | ||
141 | unsigned int wc = 0; | ||
142 | |||
143 | for (; wcnt; wcnt -= wc, start += wc) { | ||
144 | loop: | ||
145 | spin_lock_irqsave(&cache_lock, flags); | ||
146 | |||
147 | /* | ||
148 | * If another cache operation is occuring | ||
149 | */ | ||
150 | if (unlikely(imcr_get(wc_reg))) { | ||
151 | spin_unlock_irqrestore(&cache_lock, flags); | ||
152 | |||
153 | /* Wait for previous operation completion */ | ||
154 | cache_block_operation_wait(wc_reg); | ||
155 | |||
156 | /* Try again */ | ||
157 | goto loop; | ||
158 | } | ||
159 | |||
160 | imcr_set(bar_reg, L2_CACHE_ALIGN_LOW((unsigned int) start)); | ||
161 | |||
162 | if (wcnt > 0xffff) | ||
163 | wc = 0xffff; | ||
164 | else | ||
165 | wc = wcnt; | ||
166 | |||
167 | /* Set word count value in the WC register */ | ||
168 | imcr_set(wc_reg, wc & 0xffff); | ||
169 | |||
170 | spin_unlock_irqrestore(&cache_lock, flags); | ||
171 | |||
172 | /* Wait for completion */ | ||
173 | cache_block_operation_wait(wc_reg); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | static void cache_block_operation_nowait(unsigned int *start, | ||
178 | unsigned int *end, | ||
179 | unsigned int bar_reg, | ||
180 | unsigned int wc_reg) | ||
181 | { | ||
182 | unsigned long flags; | ||
183 | unsigned int wcnt = | ||
184 | (L2_CACHE_ALIGN_CNT((unsigned int) end) | ||
185 | - L2_CACHE_ALIGN_LOW((unsigned int) start)) >> 2; | ||
186 | unsigned int wc = 0; | ||
187 | |||
188 | for (; wcnt; wcnt -= wc, start += wc) { | ||
189 | |||
190 | spin_lock_irqsave(&cache_lock, flags); | ||
191 | |||
192 | imcr_set(bar_reg, L2_CACHE_ALIGN_LOW((unsigned int) start)); | ||
193 | |||
194 | if (wcnt > 0xffff) | ||
195 | wc = 0xffff; | ||
196 | else | ||
197 | wc = wcnt; | ||
198 | |||
199 | /* Set word count value in the WC register */ | ||
200 | imcr_set(wc_reg, wc & 0xffff); | ||
201 | |||
202 | spin_unlock_irqrestore(&cache_lock, flags); | ||
203 | |||
204 | /* Don't wait for completion on last cache operation */ | ||
205 | if (wcnt > 0xffff) | ||
206 | cache_block_operation_wait(wc_reg); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * L1 caches management | ||
212 | */ | ||
213 | |||
214 | /* | ||
215 | * Disable L1 caches | ||
216 | */ | ||
217 | void L1_cache_off(void) | ||
218 | { | ||
219 | unsigned int dummy; | ||
220 | |||
221 | imcr_set(IMCR_L1PCFG, 0); | ||
222 | dummy = imcr_get(IMCR_L1PCFG); | ||
223 | |||
224 | imcr_set(IMCR_L1DCFG, 0); | ||
225 | dummy = imcr_get(IMCR_L1DCFG); | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Enable L1 caches | ||
230 | */ | ||
231 | void L1_cache_on(void) | ||
232 | { | ||
233 | unsigned int dummy; | ||
234 | |||
235 | imcr_set(IMCR_L1PCFG, 7); | ||
236 | dummy = imcr_get(IMCR_L1PCFG); | ||
237 | |||
238 | imcr_set(IMCR_L1DCFG, 7); | ||
239 | dummy = imcr_get(IMCR_L1DCFG); | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * L1P global-invalidate all | ||
244 | */ | ||
245 | void L1P_cache_global_invalidate(void) | ||
246 | { | ||
247 | unsigned int set = 1; | ||
248 | imcr_set(IMCR_L1PINV, set); | ||
249 | while (imcr_get(IMCR_L1PINV) & 1) | ||
250 | cpu_relax(); | ||
251 | } | ||
252 | |||
253 | /* | ||
254 | * L1D global-invalidate all | ||
255 | * | ||
256 | * Warning: this operation causes all updated data in L1D to | ||
257 | * be discarded rather than written back to the lower levels of | ||
258 | * memory | ||
259 | */ | ||
260 | void L1D_cache_global_invalidate(void) | ||
261 | { | ||
262 | unsigned int set = 1; | ||
263 | imcr_set(IMCR_L1DINV, set); | ||
264 | while (imcr_get(IMCR_L1DINV) & 1) | ||
265 | cpu_relax(); | ||
266 | } | ||
267 | |||
268 | void L1D_cache_global_writeback(void) | ||
269 | { | ||
270 | unsigned int set = 1; | ||
271 | imcr_set(IMCR_L1DWB, set); | ||
272 | while (imcr_get(IMCR_L1DWB) & 1) | ||
273 | cpu_relax(); | ||
274 | } | ||
275 | |||
276 | void L1D_cache_global_writeback_invalidate(void) | ||
277 | { | ||
278 | unsigned int set = 1; | ||
279 | imcr_set(IMCR_L1DWBINV, set); | ||
280 | while (imcr_get(IMCR_L1DWBINV) & 1) | ||
281 | cpu_relax(); | ||
282 | } | ||
283 | |||
284 | /* | ||
285 | * L2 caches management | ||
286 | */ | ||
287 | |||
288 | /* | ||
289 | * Set L2 operation mode | ||
290 | */ | ||
291 | void L2_cache_set_mode(unsigned int mode) | ||
292 | { | ||
293 | unsigned int ccfg = imcr_get(IMCR_CCFG); | ||
294 | |||
295 | /* Clear and set the L2MODE bits in CCFG */ | ||
296 | ccfg &= ~7; | ||
297 | ccfg |= (mode & 7); | ||
298 | imcr_set(IMCR_CCFG, ccfg); | ||
299 | ccfg = imcr_get(IMCR_CCFG); | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * L2 global-writeback and global-invalidate all | ||
304 | */ | ||
305 | void L2_cache_global_writeback_invalidate(void) | ||
306 | { | ||
307 | imcr_set(IMCR_L2WBINV, 1); | ||
308 | while (imcr_get(IMCR_L2WBINV)) | ||
309 | cpu_relax(); | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * L2 global-writeback all | ||
314 | */ | ||
315 | void L2_cache_global_writeback(void) | ||
316 | { | ||
317 | imcr_set(IMCR_L2WB, 1); | ||
318 | while (imcr_get(IMCR_L2WB)) | ||
319 | cpu_relax(); | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * Cacheability controls | ||
324 | */ | ||
325 | void enable_caching(unsigned long start, unsigned long end) | ||
326 | { | ||
327 | unsigned int mar = IMCR_MAR_BASE + ((start >> 24) << 2); | ||
328 | unsigned int mar_e = IMCR_MAR_BASE + ((end >> 24) << 2); | ||
329 | |||
330 | for (; mar <= mar_e; mar += 4) | ||
331 | imcr_set(mar, imcr_get(mar) | 1); | ||
332 | } | ||
333 | |||
334 | void disable_caching(unsigned long start, unsigned long end) | ||
335 | { | ||
336 | unsigned int mar = IMCR_MAR_BASE + ((start >> 24) << 2); | ||
337 | unsigned int mar_e = IMCR_MAR_BASE + ((end >> 24) << 2); | ||
338 | |||
339 | for (; mar <= mar_e; mar += 4) | ||
340 | imcr_set(mar, imcr_get(mar) & ~1); | ||
341 | } | ||
342 | |||
343 | |||
344 | /* | ||
345 | * L1 block operations | ||
346 | */ | ||
347 | void L1P_cache_block_invalidate(unsigned int start, unsigned int end) | ||
348 | { | ||
349 | cache_block_operation((unsigned int *) start, | ||
350 | (unsigned int *) end, | ||
351 | IMCR_L1PIBAR, IMCR_L1PIWC); | ||
352 | } | ||
353 | |||
354 | void L1D_cache_block_invalidate(unsigned int start, unsigned int end) | ||
355 | { | ||
356 | cache_block_operation((unsigned int *) start, | ||
357 | (unsigned int *) end, | ||
358 | IMCR_L1DIBAR, IMCR_L1DIWC); | ||
359 | } | ||
360 | |||
361 | void L1D_cache_block_writeback_invalidate(unsigned int start, unsigned int end) | ||
362 | { | ||
363 | cache_block_operation((unsigned int *) start, | ||
364 | (unsigned int *) end, | ||
365 | IMCR_L1DWIBAR, IMCR_L1DWIWC); | ||
366 | } | ||
367 | |||
368 | void L1D_cache_block_writeback(unsigned int start, unsigned int end) | ||
369 | { | ||
370 | cache_block_operation((unsigned int *) start, | ||
371 | (unsigned int *) end, | ||
372 | IMCR_L1DWBAR, IMCR_L1DWWC); | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * L2 block operations | ||
377 | */ | ||
378 | void L2_cache_block_invalidate(unsigned int start, unsigned int end) | ||
379 | { | ||
380 | cache_block_operation((unsigned int *) start, | ||
381 | (unsigned int *) end, | ||
382 | IMCR_L2IBAR, IMCR_L2IWC); | ||
383 | } | ||
384 | |||
385 | void L2_cache_block_writeback(unsigned int start, unsigned int end) | ||
386 | { | ||
387 | cache_block_operation((unsigned int *) start, | ||
388 | (unsigned int *) end, | ||
389 | IMCR_L2WBAR, IMCR_L2WWC); | ||
390 | } | ||
391 | |||
392 | void L2_cache_block_writeback_invalidate(unsigned int start, unsigned int end) | ||
393 | { | ||
394 | cache_block_operation((unsigned int *) start, | ||
395 | (unsigned int *) end, | ||
396 | IMCR_L2WIBAR, IMCR_L2WIWC); | ||
397 | } | ||
398 | |||
399 | void L2_cache_block_invalidate_nowait(unsigned int start, unsigned int end) | ||
400 | { | ||
401 | cache_block_operation_nowait((unsigned int *) start, | ||
402 | (unsigned int *) end, | ||
403 | IMCR_L2IBAR, IMCR_L2IWC); | ||
404 | } | ||
405 | |||
406 | void L2_cache_block_writeback_nowait(unsigned int start, unsigned int end) | ||
407 | { | ||
408 | cache_block_operation_nowait((unsigned int *) start, | ||
409 | (unsigned int *) end, | ||
410 | IMCR_L2WBAR, IMCR_L2WWC); | ||
411 | } | ||
412 | |||
413 | void L2_cache_block_writeback_invalidate_nowait(unsigned int start, | ||
414 | unsigned int end) | ||
415 | { | ||
416 | cache_block_operation_nowait((unsigned int *) start, | ||
417 | (unsigned int *) end, | ||
418 | IMCR_L2WIBAR, IMCR_L2WIWC); | ||
419 | } | ||
420 | |||
421 | |||
422 | /* | ||
423 | * L1 and L2 caches configuration | ||
424 | */ | ||
425 | void __init c6x_cache_init(void) | ||
426 | { | ||
427 | struct device_node *node; | ||
428 | |||
429 | node = of_find_compatible_node(NULL, NULL, "ti,c64x+cache"); | ||
430 | if (!node) | ||
431 | return; | ||
432 | |||
433 | cache_base = of_iomap(node, 0); | ||
434 | |||
435 | of_node_put(node); | ||
436 | |||
437 | if (!cache_base) | ||
438 | return; | ||
439 | |||
440 | /* Set L2 caches on the the whole L2 SRAM memory */ | ||
441 | L2_cache_set_mode(L2MODE_SIZE); | ||
442 | |||
443 | /* Enable L1 */ | ||
444 | L1_cache_on(); | ||
445 | } | ||
diff --git a/arch/c6x/platforms/dscr.c b/arch/c6x/platforms/dscr.c new file mode 100644 index 000000000000..f848a65ee646 --- /dev/null +++ b/arch/c6x/platforms/dscr.c | |||
@@ -0,0 +1,598 @@ | |||
1 | /* | ||
2 | * Device State Control Registers driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
5 | * Author: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * The Device State Control Registers (DSCR) provide SoC level control over | ||
14 | * a number of peripherals. Details vary considerably among the various SoC | ||
15 | * parts. In general, the DSCR block will provide one or more configuration | ||
16 | * registers often protected by a lock register. One or more key values must | ||
17 | * be written to a lock register in order to unlock the configuration register. | ||
18 | * The configuration register may be used to enable (and disable in some | ||
19 | * cases) SoC pin drivers, peripheral clock sources (internal or pin), etc. | ||
20 | * In some cases, a configuration register is write once or the individual | ||
21 | * bits are write once. That is, you may be able to enable a device, but | ||
22 | * will not be able to disable it. | ||
23 | * | ||
24 | * In addition to device configuration, the DSCR block may provide registers | ||
25 | * which are used to reset SoC peripherals, provide device ID information, | ||
26 | * provide MAC addresses, and other miscellaneous functions. | ||
27 | */ | ||
28 | |||
29 | #include <linux/of.h> | ||
30 | #include <linux/of_address.h> | ||
31 | #include <linux/of_platform.h> | ||
32 | #include <linux/module.h> | ||
33 | #include <linux/io.h> | ||
34 | #include <linux/delay.h> | ||
35 | #include <asm/soc.h> | ||
36 | #include <asm/dscr.h> | ||
37 | |||
38 | #define MAX_DEVSTATE_IDS 32 | ||
39 | #define MAX_DEVCTL_REGS 8 | ||
40 | #define MAX_DEVSTAT_REGS 8 | ||
41 | #define MAX_LOCKED_REGS 4 | ||
42 | #define MAX_SOC_EMACS 2 | ||
43 | |||
44 | struct rmii_reset_reg { | ||
45 | u32 reg; | ||
46 | u32 mask; | ||
47 | }; | ||
48 | |||
49 | /* | ||
50 | * Some registerd may be locked. In order to write to these | ||
51 | * registers, the key value must first be written to the lockreg. | ||
52 | */ | ||
53 | struct locked_reg { | ||
54 | u32 reg; /* offset from base */ | ||
55 | u32 lockreg; /* offset from base */ | ||
56 | u32 key; /* unlock key */ | ||
57 | }; | ||
58 | |||
59 | /* | ||
60 | * This describes a contiguous area of like control bits used to enable/disable | ||
61 | * SoC devices. Each controllable device is given an ID which is used by the | ||
62 | * individual device drivers to control the device state. These IDs start at | ||
63 | * zero and are assigned sequentially to the control bitfield ranges described | ||
64 | * by this structure. | ||
65 | */ | ||
66 | struct devstate_ctl_reg { | ||
67 | u32 reg; /* register holding the control bits */ | ||
68 | u8 start_id; /* start id of this range */ | ||
69 | u8 num_ids; /* number of devices in this range */ | ||
70 | u8 enable_only; /* bits are write-once to enable only */ | ||
71 | u8 enable; /* value used to enable device */ | ||
72 | u8 disable; /* value used to disable device */ | ||
73 | u8 shift; /* starting (rightmost) bit in range */ | ||
74 | u8 nbits; /* number of bits per device */ | ||
75 | }; | ||
76 | |||
77 | |||
78 | /* | ||
79 | * This describes a region of status bits indicating the state of | ||
80 | * various devices. This is used internally to wait for status | ||
81 | * change completion when enabling/disabling a device. Status is | ||
82 | * optional and not all device controls will have a corresponding | ||
83 | * status. | ||
84 | */ | ||
85 | struct devstate_stat_reg { | ||
86 | u32 reg; /* register holding the status bits */ | ||
87 | u8 start_id; /* start id of this range */ | ||
88 | u8 num_ids; /* number of devices in this range */ | ||
89 | u8 enable; /* value indicating enabled state */ | ||
90 | u8 disable; /* value indicating disabled state */ | ||
91 | u8 shift; /* starting (rightmost) bit in range */ | ||
92 | u8 nbits; /* number of bits per device */ | ||
93 | }; | ||
94 | |||
95 | struct devstate_info { | ||
96 | struct devstate_ctl_reg *ctl; | ||
97 | struct devstate_stat_reg *stat; | ||
98 | }; | ||
99 | |||
100 | /* These are callbacks to SOC-specific code. */ | ||
101 | struct dscr_ops { | ||
102 | void (*init)(struct device_node *node); | ||
103 | }; | ||
104 | |||
105 | struct dscr_regs { | ||
106 | spinlock_t lock; | ||
107 | void __iomem *base; | ||
108 | u32 kick_reg[2]; | ||
109 | u32 kick_key[2]; | ||
110 | struct locked_reg locked[MAX_LOCKED_REGS]; | ||
111 | struct devstate_info devstate_info[MAX_DEVSTATE_IDS]; | ||
112 | struct rmii_reset_reg rmii_resets[MAX_SOC_EMACS]; | ||
113 | struct devstate_ctl_reg devctl[MAX_DEVCTL_REGS]; | ||
114 | struct devstate_stat_reg devstat[MAX_DEVSTAT_REGS]; | ||
115 | }; | ||
116 | |||
117 | static struct dscr_regs dscr; | ||
118 | |||
119 | static struct locked_reg *find_locked_reg(u32 reg) | ||
120 | { | ||
121 | int i; | ||
122 | |||
123 | for (i = 0; i < MAX_LOCKED_REGS; i++) | ||
124 | if (dscr.locked[i].key && reg == dscr.locked[i].reg) | ||
125 | return &dscr.locked[i]; | ||
126 | return NULL; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Write to a register with one lock | ||
131 | */ | ||
132 | static void dscr_write_locked1(u32 reg, u32 val, | ||
133 | u32 lock, u32 key) | ||
134 | { | ||
135 | void __iomem *reg_addr = dscr.base + reg; | ||
136 | void __iomem *lock_addr = dscr.base + lock; | ||
137 | |||
138 | /* | ||
139 | * For some registers, the lock is relocked after a short number | ||
140 | * of cycles. We have to put the lock write and register write in | ||
141 | * the same fetch packet to meet this timing. The .align ensures | ||
142 | * the two stw instructions are in the same fetch packet. | ||
143 | */ | ||
144 | asm volatile ("b .s2 0f\n" | ||
145 | "nop 5\n" | ||
146 | " .align 5\n" | ||
147 | "0:\n" | ||
148 | "stw .D1T2 %3,*%2\n" | ||
149 | "stw .D1T2 %1,*%0\n" | ||
150 | : | ||
151 | : "a"(reg_addr), "b"(val), "a"(lock_addr), "b"(key) | ||
152 | ); | ||
153 | |||
154 | /* in case the hw doesn't reset the lock */ | ||
155 | soc_writel(0, lock_addr); | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Write to a register protected by two lock registers | ||
160 | */ | ||
161 | static void dscr_write_locked2(u32 reg, u32 val, | ||
162 | u32 lock0, u32 key0, | ||
163 | u32 lock1, u32 key1) | ||
164 | { | ||
165 | soc_writel(key0, dscr.base + lock0); | ||
166 | soc_writel(key1, dscr.base + lock1); | ||
167 | soc_writel(val, dscr.base + reg); | ||
168 | soc_writel(0, dscr.base + lock0); | ||
169 | soc_writel(0, dscr.base + lock1); | ||
170 | } | ||
171 | |||
172 | static void dscr_write(u32 reg, u32 val) | ||
173 | { | ||
174 | struct locked_reg *lock; | ||
175 | |||
176 | lock = find_locked_reg(reg); | ||
177 | if (lock) | ||
178 | dscr_write_locked1(reg, val, lock->lockreg, lock->key); | ||
179 | else if (dscr.kick_key[0]) | ||
180 | dscr_write_locked2(reg, val, dscr.kick_reg[0], dscr.kick_key[0], | ||
181 | dscr.kick_reg[1], dscr.kick_key[1]); | ||
182 | else | ||
183 | soc_writel(val, dscr.base + reg); | ||
184 | } | ||
185 | |||
186 | |||
187 | /* | ||
188 | * Drivers can use this interface to enable/disable SoC IP blocks. | ||
189 | */ | ||
190 | void dscr_set_devstate(int id, enum dscr_devstate_t state) | ||
191 | { | ||
192 | struct devstate_ctl_reg *ctl; | ||
193 | struct devstate_stat_reg *stat; | ||
194 | struct devstate_info *info; | ||
195 | u32 ctl_val, val; | ||
196 | int ctl_shift, ctl_mask; | ||
197 | unsigned long flags; | ||
198 | |||
199 | if (!dscr.base) | ||
200 | return; | ||
201 | |||
202 | if (id < 0 || id >= MAX_DEVSTATE_IDS) | ||
203 | return; | ||
204 | |||
205 | info = &dscr.devstate_info[id]; | ||
206 | ctl = info->ctl; | ||
207 | stat = info->stat; | ||
208 | |||
209 | if (ctl == NULL) | ||
210 | return; | ||
211 | |||
212 | ctl_shift = ctl->shift + ctl->nbits * (id - ctl->start_id); | ||
213 | ctl_mask = ((1 << ctl->nbits) - 1) << ctl_shift; | ||
214 | |||
215 | switch (state) { | ||
216 | case DSCR_DEVSTATE_ENABLED: | ||
217 | ctl_val = ctl->enable << ctl_shift; | ||
218 | break; | ||
219 | case DSCR_DEVSTATE_DISABLED: | ||
220 | if (ctl->enable_only) | ||
221 | return; | ||
222 | ctl_val = ctl->disable << ctl_shift; | ||
223 | break; | ||
224 | default: | ||
225 | return; | ||
226 | } | ||
227 | |||
228 | spin_lock_irqsave(&dscr.lock, flags); | ||
229 | |||
230 | val = soc_readl(dscr.base + ctl->reg); | ||
231 | val &= ~ctl_mask; | ||
232 | val |= ctl_val; | ||
233 | |||
234 | dscr_write(ctl->reg, val); | ||
235 | |||
236 | spin_unlock_irqrestore(&dscr.lock, flags); | ||
237 | |||
238 | if (!stat) | ||
239 | return; | ||
240 | |||
241 | ctl_shift = stat->shift + stat->nbits * (id - stat->start_id); | ||
242 | |||
243 | if (state == DSCR_DEVSTATE_ENABLED) | ||
244 | ctl_val = stat->enable; | ||
245 | else | ||
246 | ctl_val = stat->disable; | ||
247 | |||
248 | do { | ||
249 | val = soc_readl(dscr.base + stat->reg); | ||
250 | val >>= ctl_shift; | ||
251 | val &= ((1 << stat->nbits) - 1); | ||
252 | } while (val != ctl_val); | ||
253 | } | ||
254 | EXPORT_SYMBOL(dscr_set_devstate); | ||
255 | |||
256 | /* | ||
257 | * Drivers can use this to reset RMII module. | ||
258 | */ | ||
259 | void dscr_rmii_reset(int id, int assert) | ||
260 | { | ||
261 | struct rmii_reset_reg *r; | ||
262 | unsigned long flags; | ||
263 | u32 val; | ||
264 | |||
265 | if (id < 0 || id >= MAX_SOC_EMACS) | ||
266 | return; | ||
267 | |||
268 | r = &dscr.rmii_resets[id]; | ||
269 | if (r->mask == 0) | ||
270 | return; | ||
271 | |||
272 | spin_lock_irqsave(&dscr.lock, flags); | ||
273 | |||
274 | val = soc_readl(dscr.base + r->reg); | ||
275 | if (assert) | ||
276 | dscr_write(r->reg, val | r->mask); | ||
277 | else | ||
278 | dscr_write(r->reg, val & ~(r->mask)); | ||
279 | |||
280 | spin_unlock_irqrestore(&dscr.lock, flags); | ||
281 | } | ||
282 | EXPORT_SYMBOL(dscr_rmii_reset); | ||
283 | |||
284 | static void __init dscr_parse_devstat(struct device_node *node, | ||
285 | void __iomem *base) | ||
286 | { | ||
287 | u32 val; | ||
288 | int err; | ||
289 | |||
290 | err = of_property_read_u32_array(node, "ti,dscr-devstat", &val, 1); | ||
291 | if (!err) | ||
292 | c6x_devstat = soc_readl(base + val); | ||
293 | printk(KERN_INFO "DEVSTAT: %08x\n", c6x_devstat); | ||
294 | } | ||
295 | |||
296 | static void __init dscr_parse_silicon_rev(struct device_node *node, | ||
297 | void __iomem *base) | ||
298 | { | ||
299 | u32 vals[3]; | ||
300 | int err; | ||
301 | |||
302 | err = of_property_read_u32_array(node, "ti,dscr-silicon-rev", vals, 3); | ||
303 | if (!err) { | ||
304 | c6x_silicon_rev = soc_readl(base + vals[0]); | ||
305 | c6x_silicon_rev >>= vals[1]; | ||
306 | c6x_silicon_rev &= vals[2]; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * Some SoCs will have a pair of fuse registers which hold | ||
312 | * an ethernet MAC address. The "ti,dscr-mac-fuse-regs" | ||
313 | * property is a mapping from fuse register bytes to MAC | ||
314 | * address bytes. The expected format is: | ||
315 | * | ||
316 | * ti,dscr-mac-fuse-regs = <reg0 b3 b2 b1 b0 | ||
317 | * reg1 b3 b2 b1 b0> | ||
318 | * | ||
319 | * reg0 and reg1 are the offsets of the two fuse registers. | ||
320 | * b3-b0 positionally represent bytes within the fuse register. | ||
321 | * b3 is the most significant byte and b0 is the least. | ||
322 | * Allowable values for b3-b0 are: | ||
323 | * | ||
324 | * 0 = fuse register byte not used in MAC address | ||
325 | * 1-6 = index+1 into c6x_fuse_mac[] | ||
326 | */ | ||
327 | static void __init dscr_parse_mac_fuse(struct device_node *node, | ||
328 | void __iomem *base) | ||
329 | { | ||
330 | u32 vals[10], fuse; | ||
331 | int f, i, j, err; | ||
332 | |||
333 | err = of_property_read_u32_array(node, "ti,dscr-mac-fuse-regs", | ||
334 | vals, 10); | ||
335 | if (err) | ||
336 | return; | ||
337 | |||
338 | for (f = 0; f < 2; f++) { | ||
339 | fuse = soc_readl(base + vals[f * 5]); | ||
340 | for (j = (f * 5) + 1, i = 24; i >= 0; i -= 8, j++) | ||
341 | if (vals[j] && vals[j] <= 6) | ||
342 | c6x_fuse_mac[vals[j] - 1] = fuse >> i; | ||
343 | } | ||
344 | } | ||
345 | |||
346 | static void __init dscr_parse_rmii_resets(struct device_node *node, | ||
347 | void __iomem *base) | ||
348 | { | ||
349 | const __be32 *p; | ||
350 | int i, size; | ||
351 | |||
352 | /* look for RMII reset registers */ | ||
353 | p = of_get_property(node, "ti,dscr-rmii-resets", &size); | ||
354 | if (p) { | ||
355 | /* parse all the reg/mask pairs we can handle */ | ||
356 | size /= (sizeof(*p) * 2); | ||
357 | if (size > MAX_SOC_EMACS) | ||
358 | size = MAX_SOC_EMACS; | ||
359 | |||
360 | for (i = 0; i < size; i++) { | ||
361 | dscr.rmii_resets[i].reg = be32_to_cpup(p++); | ||
362 | dscr.rmii_resets[i].mask = be32_to_cpup(p++); | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | |||
367 | |||
368 | static void __init dscr_parse_privperm(struct device_node *node, | ||
369 | void __iomem *base) | ||
370 | { | ||
371 | u32 vals[2]; | ||
372 | int err; | ||
373 | |||
374 | err = of_property_read_u32_array(node, "ti,dscr-privperm", vals, 2); | ||
375 | if (err) | ||
376 | return; | ||
377 | dscr_write(vals[0], vals[1]); | ||
378 | } | ||
379 | |||
380 | /* | ||
381 | * SoCs may have "locked" DSCR registers which can only be written | ||
382 | * to only after writing a key value to a lock registers. These | ||
383 | * regisers can be described with the "ti,dscr-locked-regs" property. | ||
384 | * This property provides a list of register descriptions with each | ||
385 | * description consisting of three values. | ||
386 | * | ||
387 | * ti,dscr-locked-regs = <reg0 lockreg0 key0 | ||
388 | * ... | ||
389 | * regN lockregN keyN>; | ||
390 | * | ||
391 | * reg is the offset of the locked register | ||
392 | * lockreg is the offset of the lock register | ||
393 | * key is the unlock key written to lockreg | ||
394 | * | ||
395 | */ | ||
396 | static void __init dscr_parse_locked_regs(struct device_node *node, | ||
397 | void __iomem *base) | ||
398 | { | ||
399 | struct locked_reg *r; | ||
400 | const __be32 *p; | ||
401 | int i, size; | ||
402 | |||
403 | p = of_get_property(node, "ti,dscr-locked-regs", &size); | ||
404 | if (p) { | ||
405 | /* parse all the register descriptions we can handle */ | ||
406 | size /= (sizeof(*p) * 3); | ||
407 | if (size > MAX_LOCKED_REGS) | ||
408 | size = MAX_LOCKED_REGS; | ||
409 | |||
410 | for (i = 0; i < size; i++) { | ||
411 | r = &dscr.locked[i]; | ||
412 | |||
413 | r->reg = be32_to_cpup(p++); | ||
414 | r->lockreg = be32_to_cpup(p++); | ||
415 | r->key = be32_to_cpup(p++); | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * SoCs may have DSCR registers which are only write enabled after | ||
422 | * writing specific key values to two registers. The two key registers | ||
423 | * and the key values can be parsed from a "ti,dscr-kick-regs" | ||
424 | * propety with the following layout: | ||
425 | * | ||
426 | * ti,dscr-kick-regs = <kickreg0 key0 kickreg1 key1> | ||
427 | * | ||
428 | * kickreg is the offset of the "kick" register | ||
429 | * key is the value which unlocks writing for protected regs | ||
430 | */ | ||
431 | static void __init dscr_parse_kick_regs(struct device_node *node, | ||
432 | void __iomem *base) | ||
433 | { | ||
434 | u32 vals[4]; | ||
435 | int err; | ||
436 | |||
437 | err = of_property_read_u32_array(node, "ti,dscr-kick-regs", vals, 4); | ||
438 | if (!err) { | ||
439 | dscr.kick_reg[0] = vals[0]; | ||
440 | dscr.kick_key[0] = vals[1]; | ||
441 | dscr.kick_reg[1] = vals[2]; | ||
442 | dscr.kick_key[1] = vals[3]; | ||
443 | } | ||
444 | } | ||
445 | |||
446 | |||
447 | /* | ||
448 | * SoCs may provide controls to enable/disable individual IP blocks. These | ||
449 | * controls in the DSCR usually control pin drivers but also may control | ||
450 | * clocking and or resets. The device tree is used to describe the bitfields | ||
451 | * in registers used to control device state. The number of bits and their | ||
452 | * values may vary even within the same register. | ||
453 | * | ||
454 | * The layout of these bitfields is described by the ti,dscr-devstate-ctl-regs | ||
455 | * property. This property is a list where each element describes a contiguous | ||
456 | * range of control fields with like properties. Each element of the list | ||
457 | * consists of 7 cells with the following values: | ||
458 | * | ||
459 | * start_id num_ids reg enable disable start_bit nbits | ||
460 | * | ||
461 | * start_id is device id for the first device control in the range | ||
462 | * num_ids is the number of device controls in the range | ||
463 | * reg is the offset of the register holding the control bits | ||
464 | * enable is the value to enable a device | ||
465 | * disable is the value to disable a device (0xffffffff if cannot disable) | ||
466 | * start_bit is the bit number of the first bit in the range | ||
467 | * nbits is the number of bits per device control | ||
468 | */ | ||
469 | static void __init dscr_parse_devstate_ctl_regs(struct device_node *node, | ||
470 | void __iomem *base) | ||
471 | { | ||
472 | struct devstate_ctl_reg *r; | ||
473 | const __be32 *p; | ||
474 | int i, j, size; | ||
475 | |||
476 | p = of_get_property(node, "ti,dscr-devstate-ctl-regs", &size); | ||
477 | if (p) { | ||
478 | /* parse all the ranges we can handle */ | ||
479 | size /= (sizeof(*p) * 7); | ||
480 | if (size > MAX_DEVCTL_REGS) | ||
481 | size = MAX_DEVCTL_REGS; | ||
482 | |||
483 | for (i = 0; i < size; i++) { | ||
484 | r = &dscr.devctl[i]; | ||
485 | |||
486 | r->start_id = be32_to_cpup(p++); | ||
487 | r->num_ids = be32_to_cpup(p++); | ||
488 | r->reg = be32_to_cpup(p++); | ||
489 | r->enable = be32_to_cpup(p++); | ||
490 | r->disable = be32_to_cpup(p++); | ||
491 | if (r->disable == 0xffffffff) | ||
492 | r->enable_only = 1; | ||
493 | r->shift = be32_to_cpup(p++); | ||
494 | r->nbits = be32_to_cpup(p++); | ||
495 | |||
496 | for (j = r->start_id; | ||
497 | j < (r->start_id + r->num_ids); | ||
498 | j++) | ||
499 | dscr.devstate_info[j].ctl = r; | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * SoCs may provide status registers indicating the state (enabled/disabled) of | ||
506 | * devices on the SoC. The device tree is used to describe the bitfields in | ||
507 | * registers used to provide device status. The number of bits and their | ||
508 | * values used to provide status may vary even within the same register. | ||
509 | * | ||
510 | * The layout of these bitfields is described by the ti,dscr-devstate-stat-regs | ||
511 | * property. This property is a list where each element describes a contiguous | ||
512 | * range of status fields with like properties. Each element of the list | ||
513 | * consists of 7 cells with the following values: | ||
514 | * | ||
515 | * start_id num_ids reg enable disable start_bit nbits | ||
516 | * | ||
517 | * start_id is device id for the first device status in the range | ||
518 | * num_ids is the number of devices covered by the range | ||
519 | * reg is the offset of the register holding the status bits | ||
520 | * enable is the value indicating device is enabled | ||
521 | * disable is the value indicating device is disabled | ||
522 | * start_bit is the bit number of the first bit in the range | ||
523 | * nbits is the number of bits per device status | ||
524 | */ | ||
525 | static void __init dscr_parse_devstate_stat_regs(struct device_node *node, | ||
526 | void __iomem *base) | ||
527 | { | ||
528 | struct devstate_stat_reg *r; | ||
529 | const __be32 *p; | ||
530 | int i, j, size; | ||
531 | |||
532 | p = of_get_property(node, "ti,dscr-devstate-stat-regs", &size); | ||
533 | if (p) { | ||
534 | /* parse all the ranges we can handle */ | ||
535 | size /= (sizeof(*p) * 7); | ||
536 | if (size > MAX_DEVSTAT_REGS) | ||
537 | size = MAX_DEVSTAT_REGS; | ||
538 | |||
539 | for (i = 0; i < size; i++) { | ||
540 | r = &dscr.devstat[i]; | ||
541 | |||
542 | r->start_id = be32_to_cpup(p++); | ||
543 | r->num_ids = be32_to_cpup(p++); | ||
544 | r->reg = be32_to_cpup(p++); | ||
545 | r->enable = be32_to_cpup(p++); | ||
546 | r->disable = be32_to_cpup(p++); | ||
547 | r->shift = be32_to_cpup(p++); | ||
548 | r->nbits = be32_to_cpup(p++); | ||
549 | |||
550 | for (j = r->start_id; | ||
551 | j < (r->start_id + r->num_ids); | ||
552 | j++) | ||
553 | dscr.devstate_info[j].stat = r; | ||
554 | } | ||
555 | } | ||
556 | } | ||
557 | |||
558 | static struct of_device_id dscr_ids[] __initdata = { | ||
559 | { .compatible = "ti,c64x+dscr" }, | ||
560 | {} | ||
561 | }; | ||
562 | |||
563 | /* | ||
564 | * Probe for DSCR area. | ||
565 | * | ||
566 | * This has to be done early on in case timer or interrupt controller | ||
567 | * needs something. e.g. On C6455 SoC, timer must be enabled through | ||
568 | * DSCR before it is functional. | ||
569 | */ | ||
570 | void __init dscr_probe(void) | ||
571 | { | ||
572 | struct device_node *node; | ||
573 | void __iomem *base; | ||
574 | |||
575 | spin_lock_init(&dscr.lock); | ||
576 | |||
577 | node = of_find_matching_node(NULL, dscr_ids); | ||
578 | if (!node) | ||
579 | return; | ||
580 | |||
581 | base = of_iomap(node, 0); | ||
582 | if (!base) { | ||
583 | of_node_put(node); | ||
584 | return; | ||
585 | } | ||
586 | |||
587 | dscr.base = base; | ||
588 | |||
589 | dscr_parse_devstat(node, base); | ||
590 | dscr_parse_silicon_rev(node, base); | ||
591 | dscr_parse_mac_fuse(node, base); | ||
592 | dscr_parse_rmii_resets(node, base); | ||
593 | dscr_parse_locked_regs(node, base); | ||
594 | dscr_parse_kick_regs(node, base); | ||
595 | dscr_parse_devstate_ctl_regs(node, base); | ||
596 | dscr_parse_devstate_stat_regs(node, base); | ||
597 | dscr_parse_privperm(node, base); | ||
598 | } | ||
diff --git a/arch/c6x/platforms/emif.c b/arch/c6x/platforms/emif.c new file mode 100644 index 000000000000..8b564dec241d --- /dev/null +++ b/arch/c6x/platforms/emif.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * External Memory Interface | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
5 | * Author: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/of.h> | ||
12 | #include <linux/of_address.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <asm/soc.h> | ||
15 | #include <asm/dscr.h> | ||
16 | |||
17 | #define NUM_EMIFA_CHIP_ENABLES 4 | ||
18 | |||
19 | struct emifa_regs { | ||
20 | u32 midr; | ||
21 | u32 stat; | ||
22 | u32 reserved1[6]; | ||
23 | u32 bprio; | ||
24 | u32 reserved2[23]; | ||
25 | u32 cecfg[NUM_EMIFA_CHIP_ENABLES]; | ||
26 | u32 reserved3[4]; | ||
27 | u32 awcc; | ||
28 | u32 reserved4[7]; | ||
29 | u32 intraw; | ||
30 | u32 intmsk; | ||
31 | u32 intmskset; | ||
32 | u32 intmskclr; | ||
33 | }; | ||
34 | |||
35 | static struct of_device_id emifa_match[] __initdata = { | ||
36 | { .compatible = "ti,c64x+emifa" }, | ||
37 | {} | ||
38 | }; | ||
39 | |||
40 | /* | ||
41 | * Parse device tree for existence of an EMIF (External Memory Interface) | ||
42 | * and initialize it if found. | ||
43 | */ | ||
44 | static int __init c6x_emifa_init(void) | ||
45 | { | ||
46 | struct emifa_regs __iomem *regs; | ||
47 | struct device_node *node; | ||
48 | const __be32 *p; | ||
49 | u32 val; | ||
50 | int i, len, err; | ||
51 | |||
52 | node = of_find_matching_node(NULL, emifa_match); | ||
53 | if (!node) | ||
54 | return 0; | ||
55 | |||
56 | regs = of_iomap(node, 0); | ||
57 | if (!regs) | ||
58 | return 0; | ||
59 | |||
60 | /* look for a dscr-based enable for emifa pin buffers */ | ||
61 | err = of_property_read_u32_array(node, "ti,dscr-dev-enable", &val, 1); | ||
62 | if (!err) | ||
63 | dscr_set_devstate(val, DSCR_DEVSTATE_ENABLED); | ||
64 | |||
65 | /* set up the chip enables */ | ||
66 | p = of_get_property(node, "ti,emifa-ce-config", &len); | ||
67 | if (p) { | ||
68 | len /= sizeof(u32); | ||
69 | if (len > NUM_EMIFA_CHIP_ENABLES) | ||
70 | len = NUM_EMIFA_CHIP_ENABLES; | ||
71 | for (i = 0; i <= len; i++) | ||
72 | soc_writel(be32_to_cpup(&p[i]), ®s->cecfg[i]); | ||
73 | } | ||
74 | |||
75 | err = of_property_read_u32_array(node, "ti,emifa-burst-priority", &val, 1); | ||
76 | if (!err) | ||
77 | soc_writel(val, ®s->bprio); | ||
78 | |||
79 | err = of_property_read_u32_array(node, "ti,emifa-async-wait-control", &val, 1); | ||
80 | if (!err) | ||
81 | soc_writel(val, ®s->awcc); | ||
82 | |||
83 | iounmap(regs); | ||
84 | of_node_put(node); | ||
85 | return 0; | ||
86 | } | ||
87 | pure_initcall(c6x_emifa_init); | ||
diff --git a/arch/c6x/platforms/megamod-pic.c b/arch/c6x/platforms/megamod-pic.c new file mode 100644 index 000000000000..7c37a947fb1c --- /dev/null +++ b/arch/c6x/platforms/megamod-pic.c | |||
@@ -0,0 +1,349 @@ | |||
1 | /* | ||
2 | * Support for C64x+ Megamodule Interrupt Controller | ||
3 | * | ||
4 | * Copyright (C) 2010, 2011 Texas Instruments Incorporated | ||
5 | * Contributed by: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_irq.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <asm/soc.h> | ||
19 | #include <asm/megamod-pic.h> | ||
20 | |||
21 | #define NR_COMBINERS 4 | ||
22 | #define NR_MUX_OUTPUTS 12 | ||
23 | |||
24 | #define IRQ_UNMAPPED 0xffff | ||
25 | |||
26 | /* | ||
27 | * Megamodule Interrupt Controller register layout | ||
28 | */ | ||
29 | struct megamod_regs { | ||
30 | u32 evtflag[8]; | ||
31 | u32 evtset[8]; | ||
32 | u32 evtclr[8]; | ||
33 | u32 reserved0[8]; | ||
34 | u32 evtmask[8]; | ||
35 | u32 mevtflag[8]; | ||
36 | u32 expmask[8]; | ||
37 | u32 mexpflag[8]; | ||
38 | u32 intmux_unused; | ||
39 | u32 intmux[7]; | ||
40 | u32 reserved1[8]; | ||
41 | u32 aegmux[2]; | ||
42 | u32 reserved2[14]; | ||
43 | u32 intxstat; | ||
44 | u32 intxclr; | ||
45 | u32 intdmask; | ||
46 | u32 reserved3[13]; | ||
47 | u32 evtasrt; | ||
48 | }; | ||
49 | |||
50 | struct megamod_pic { | ||
51 | struct irq_host *irqhost; | ||
52 | struct megamod_regs __iomem *regs; | ||
53 | raw_spinlock_t lock; | ||
54 | |||
55 | /* hw mux mapping */ | ||
56 | unsigned int output_to_irq[NR_MUX_OUTPUTS]; | ||
57 | }; | ||
58 | |||
59 | static struct megamod_pic *mm_pic; | ||
60 | |||
61 | struct megamod_cascade_data { | ||
62 | struct megamod_pic *pic; | ||
63 | int index; | ||
64 | }; | ||
65 | |||
66 | static struct megamod_cascade_data cascade_data[NR_COMBINERS]; | ||
67 | |||
68 | static void mask_megamod(struct irq_data *data) | ||
69 | { | ||
70 | struct megamod_pic *pic = irq_data_get_irq_chip_data(data); | ||
71 | irq_hw_number_t src = irqd_to_hwirq(data); | ||
72 | u32 __iomem *evtmask = &pic->regs->evtmask[src / 32]; | ||
73 | |||
74 | raw_spin_lock(&pic->lock); | ||
75 | soc_writel(soc_readl(evtmask) | (1 << (src & 31)), evtmask); | ||
76 | raw_spin_unlock(&pic->lock); | ||
77 | } | ||
78 | |||
79 | static void unmask_megamod(struct irq_data *data) | ||
80 | { | ||
81 | struct megamod_pic *pic = irq_data_get_irq_chip_data(data); | ||
82 | irq_hw_number_t src = irqd_to_hwirq(data); | ||
83 | u32 __iomem *evtmask = &pic->regs->evtmask[src / 32]; | ||
84 | |||
85 | raw_spin_lock(&pic->lock); | ||
86 | soc_writel(soc_readl(evtmask) & ~(1 << (src & 31)), evtmask); | ||
87 | raw_spin_unlock(&pic->lock); | ||
88 | } | ||
89 | |||
90 | static struct irq_chip megamod_chip = { | ||
91 | .name = "megamod", | ||
92 | .irq_mask = mask_megamod, | ||
93 | .irq_unmask = unmask_megamod, | ||
94 | }; | ||
95 | |||
96 | static void megamod_irq_cascade(unsigned int irq, struct irq_desc *desc) | ||
97 | { | ||
98 | struct megamod_cascade_data *cascade; | ||
99 | struct megamod_pic *pic; | ||
100 | u32 events; | ||
101 | int n, idx; | ||
102 | |||
103 | cascade = irq_desc_get_handler_data(desc); | ||
104 | |||
105 | pic = cascade->pic; | ||
106 | idx = cascade->index; | ||
107 | |||
108 | while ((events = soc_readl(&pic->regs->mevtflag[idx])) != 0) { | ||
109 | n = __ffs(events); | ||
110 | |||
111 | irq = irq_linear_revmap(pic->irqhost, idx * 32 + n); | ||
112 | |||
113 | soc_writel(1 << n, &pic->regs->evtclr[idx]); | ||
114 | |||
115 | generic_handle_irq(irq); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | static int megamod_map(struct irq_host *h, unsigned int virq, | ||
120 | irq_hw_number_t hw) | ||
121 | { | ||
122 | struct megamod_pic *pic = h->host_data; | ||
123 | int i; | ||
124 | |||
125 | /* We shouldn't see a hwirq which is muxed to core controller */ | ||
126 | for (i = 0; i < NR_MUX_OUTPUTS; i++) | ||
127 | if (pic->output_to_irq[i] == hw) | ||
128 | return -1; | ||
129 | |||
130 | irq_set_chip_data(virq, pic); | ||
131 | irq_set_chip_and_handler(virq, &megamod_chip, handle_level_irq); | ||
132 | |||
133 | /* Set default irq type */ | ||
134 | irq_set_irq_type(virq, IRQ_TYPE_NONE); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int megamod_xlate(struct irq_host *h, struct device_node *ct, | ||
140 | const u32 *intspec, unsigned int intsize, | ||
141 | irq_hw_number_t *out_hwirq, unsigned int *out_type) | ||
142 | |||
143 | { | ||
144 | /* megamod intspecs must have 1 cell */ | ||
145 | BUG_ON(intsize != 1); | ||
146 | *out_hwirq = intspec[0]; | ||
147 | *out_type = IRQ_TYPE_NONE; | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static struct irq_host_ops megamod_host_ops = { | ||
152 | .map = megamod_map, | ||
153 | .xlate = megamod_xlate, | ||
154 | }; | ||
155 | |||
156 | static void __init set_megamod_mux(struct megamod_pic *pic, int src, int output) | ||
157 | { | ||
158 | int index, offset; | ||
159 | u32 val; | ||
160 | |||
161 | if (src < 0 || src >= (NR_COMBINERS * 32)) { | ||
162 | pic->output_to_irq[output] = IRQ_UNMAPPED; | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | /* four mappings per mux register */ | ||
167 | index = output / 4; | ||
168 | offset = (output & 3) * 8; | ||
169 | |||
170 | val = soc_readl(&pic->regs->intmux[index]); | ||
171 | val &= ~(0xff << offset); | ||
172 | val |= src << offset; | ||
173 | soc_writel(val, &pic->regs->intmux[index]); | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * Parse the MUX mapping, if one exists. | ||
178 | * | ||
179 | * The MUX map is an array of up to 12 cells; one for each usable core priority | ||
180 | * interrupt. The value of a given cell is the megamodule interrupt source | ||
181 | * which is to me MUXed to the output corresponding to the cell position | ||
182 | * withing the array. The first cell in the array corresponds to priority | ||
183 | * 4 and the last (12th) cell corresponds to priority 15. The allowed | ||
184 | * values are 4 - ((NR_COMBINERS * 32) - 1). Note that the combined interrupt | ||
185 | * sources (0 - 3) are not allowed to be mapped through this property. They | ||
186 | * are handled through the "interrupts" property. This allows us to use a | ||
187 | * value of zero as a "do not map" placeholder. | ||
188 | */ | ||
189 | static void __init parse_priority_map(struct megamod_pic *pic, | ||
190 | int *mapping, int size) | ||
191 | { | ||
192 | struct device_node *np = pic->irqhost->of_node; | ||
193 | const __be32 *map; | ||
194 | int i, maplen; | ||
195 | u32 val; | ||
196 | |||
197 | map = of_get_property(np, "ti,c64x+megamod-pic-mux", &maplen); | ||
198 | if (map) { | ||
199 | maplen /= 4; | ||
200 | if (maplen > size) | ||
201 | maplen = size; | ||
202 | |||
203 | for (i = 0; i < maplen; i++) { | ||
204 | val = be32_to_cpup(map); | ||
205 | if (val && val >= 4) | ||
206 | mapping[i] = val; | ||
207 | ++map; | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | |||
212 | static struct megamod_pic * __init init_megamod_pic(struct device_node *np) | ||
213 | { | ||
214 | struct megamod_pic *pic; | ||
215 | int i, irq; | ||
216 | int mapping[NR_MUX_OUTPUTS]; | ||
217 | |||
218 | pr_info("Initializing C64x+ Megamodule PIC\n"); | ||
219 | |||
220 | pic = kzalloc(sizeof(struct megamod_pic), GFP_KERNEL); | ||
221 | if (!pic) { | ||
222 | pr_err("%s: Could not alloc PIC structure.\n", np->full_name); | ||
223 | return NULL; | ||
224 | } | ||
225 | |||
226 | pic->irqhost = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, | ||
227 | NR_COMBINERS * 32, &megamod_host_ops, | ||
228 | IRQ_UNMAPPED); | ||
229 | if (!pic->irqhost) { | ||
230 | pr_err("%s: Could not alloc host.\n", np->full_name); | ||
231 | goto error_free; | ||
232 | } | ||
233 | |||
234 | pic->irqhost->host_data = pic; | ||
235 | |||
236 | raw_spin_lock_init(&pic->lock); | ||
237 | |||
238 | pic->regs = of_iomap(np, 0); | ||
239 | if (!pic->regs) { | ||
240 | pr_err("%s: Could not map registers.\n", np->full_name); | ||
241 | goto error_free; | ||
242 | } | ||
243 | |||
244 | /* Initialize MUX map */ | ||
245 | for (i = 0; i < ARRAY_SIZE(mapping); i++) | ||
246 | mapping[i] = IRQ_UNMAPPED; | ||
247 | |||
248 | parse_priority_map(pic, mapping, ARRAY_SIZE(mapping)); | ||
249 | |||
250 | /* | ||
251 | * We can have up to 12 interrupts cascading to the core controller. | ||
252 | * These cascades can be from the combined interrupt sources or for | ||
253 | * individual interrupt sources. The "interrupts" property only | ||
254 | * deals with the cascaded combined interrupts. The individual | ||
255 | * interrupts muxed to the core controller use the core controller | ||
256 | * as their interrupt parent. | ||
257 | */ | ||
258 | for (i = 0; i < NR_COMBINERS; i++) { | ||
259 | |||
260 | irq = irq_of_parse_and_map(np, i); | ||
261 | if (irq == NO_IRQ) | ||
262 | continue; | ||
263 | |||
264 | /* | ||
265 | * We count on the core priority interrupts (4 - 15) being | ||
266 | * direct mapped. Check that device tree provided something | ||
267 | * in that range. | ||
268 | */ | ||
269 | if (irq < 4 || irq >= NR_PRIORITY_IRQS) { | ||
270 | pr_err("%s: combiner-%d virq %d out of range!\n", | ||
271 | np->full_name, i, irq); | ||
272 | continue; | ||
273 | } | ||
274 | |||
275 | /* record the mapping */ | ||
276 | mapping[irq - 4] = i; | ||
277 | |||
278 | pr_debug("%s: combiner-%d cascading to virq %d\n", | ||
279 | np->full_name, i, irq); | ||
280 | |||
281 | cascade_data[i].pic = pic; | ||
282 | cascade_data[i].index = i; | ||
283 | |||
284 | /* mask and clear all events in combiner */ | ||
285 | soc_writel(~0, &pic->regs->evtmask[i]); | ||
286 | soc_writel(~0, &pic->regs->evtclr[i]); | ||
287 | |||
288 | irq_set_handler_data(irq, &cascade_data[i]); | ||
289 | irq_set_chained_handler(irq, megamod_irq_cascade); | ||
290 | } | ||
291 | |||
292 | /* Finally, set up the MUX registers */ | ||
293 | for (i = 0; i < NR_MUX_OUTPUTS; i++) { | ||
294 | if (mapping[i] != IRQ_UNMAPPED) { | ||
295 | pr_debug("%s: setting mux %d to priority %d\n", | ||
296 | np->full_name, mapping[i], i + 4); | ||
297 | set_megamod_mux(pic, mapping[i], i); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | return pic; | ||
302 | |||
303 | error_free: | ||
304 | kfree(pic); | ||
305 | |||
306 | return NULL; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Return next active event after ACK'ing it. | ||
311 | * Return -1 if no events active. | ||
312 | */ | ||
313 | static int get_exception(void) | ||
314 | { | ||
315 | int i, bit; | ||
316 | u32 mask; | ||
317 | |||
318 | for (i = 0; i < NR_COMBINERS; i++) { | ||
319 | mask = soc_readl(&mm_pic->regs->mexpflag[i]); | ||
320 | if (mask) { | ||
321 | bit = __ffs(mask); | ||
322 | soc_writel(1 << bit, &mm_pic->regs->evtclr[i]); | ||
323 | return (i * 32) + bit; | ||
324 | } | ||
325 | } | ||
326 | return -1; | ||
327 | } | ||
328 | |||
329 | static void assert_event(unsigned int val) | ||
330 | { | ||
331 | soc_writel(val, &mm_pic->regs->evtasrt); | ||
332 | } | ||
333 | |||
334 | void __init megamod_pic_init(void) | ||
335 | { | ||
336 | struct device_node *np; | ||
337 | |||
338 | np = of_find_compatible_node(NULL, NULL, "ti,c64x+megamod-pic"); | ||
339 | if (!np) | ||
340 | return; | ||
341 | |||
342 | mm_pic = init_megamod_pic(np); | ||
343 | of_node_put(np); | ||
344 | |||
345 | soc_ops.get_exception = get_exception; | ||
346 | soc_ops.assert_event = assert_event; | ||
347 | |||
348 | return; | ||
349 | } | ||
diff --git a/arch/c6x/platforms/platform.c b/arch/c6x/platforms/platform.c new file mode 100644 index 000000000000..26c1a355d600 --- /dev/null +++ b/arch/c6x/platforms/platform.c | |||
@@ -0,0 +1,17 @@ | |||
1 | /* | ||
2 | * Copyright 2011 Texas Instruments Incorporated | ||
3 | * | ||
4 | * This file is licensed under the terms of the GNU General Public License | ||
5 | * version 2. This program is licensed "as is" without any warranty of any | ||
6 | * kind, whether express or implied. | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/of_platform.h> | ||
11 | |||
12 | static int __init c6x_device_probe(void) | ||
13 | { | ||
14 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | ||
15 | return 0; | ||
16 | } | ||
17 | core_initcall(c6x_device_probe); | ||
diff --git a/arch/c6x/platforms/pll.c b/arch/c6x/platforms/pll.c new file mode 100644 index 000000000000..3aa898f7ce4d --- /dev/null +++ b/arch/c6x/platforms/pll.c | |||
@@ -0,0 +1,444 @@ | |||
1 | /* | ||
2 | * Clock and PLL control for C64x+ devices | ||
3 | * | ||
4 | * Copyright (C) 2010, 2011 Texas Instruments. | ||
5 | * Contributed by: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * Copied heavily from arm/mach-davinci/clock.c, so: | ||
8 | * | ||
9 | * Copyright (C) 2006-2007 Texas Instruments. | ||
10 | * Copyright (C) 2008-2009 Deep Root Systems, LLC | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/clkdev.h> | ||
20 | #include <linux/clk.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/err.h> | ||
23 | |||
24 | #include <asm/clock.h> | ||
25 | #include <asm/soc.h> | ||
26 | |||
27 | static LIST_HEAD(clocks); | ||
28 | static DEFINE_MUTEX(clocks_mutex); | ||
29 | static DEFINE_SPINLOCK(clockfw_lock); | ||
30 | |||
31 | static void __clk_enable(struct clk *clk) | ||
32 | { | ||
33 | if (clk->parent) | ||
34 | __clk_enable(clk->parent); | ||
35 | clk->usecount++; | ||
36 | } | ||
37 | |||
38 | static void __clk_disable(struct clk *clk) | ||
39 | { | ||
40 | if (WARN_ON(clk->usecount == 0)) | ||
41 | return; | ||
42 | --clk->usecount; | ||
43 | |||
44 | if (clk->parent) | ||
45 | __clk_disable(clk->parent); | ||
46 | } | ||
47 | |||
48 | int clk_enable(struct clk *clk) | ||
49 | { | ||
50 | unsigned long flags; | ||
51 | |||
52 | if (clk == NULL || IS_ERR(clk)) | ||
53 | return -EINVAL; | ||
54 | |||
55 | spin_lock_irqsave(&clockfw_lock, flags); | ||
56 | __clk_enable(clk); | ||
57 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | EXPORT_SYMBOL(clk_enable); | ||
62 | |||
63 | void clk_disable(struct clk *clk) | ||
64 | { | ||
65 | unsigned long flags; | ||
66 | |||
67 | if (clk == NULL || IS_ERR(clk)) | ||
68 | return; | ||
69 | |||
70 | spin_lock_irqsave(&clockfw_lock, flags); | ||
71 | __clk_disable(clk); | ||
72 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
73 | } | ||
74 | EXPORT_SYMBOL(clk_disable); | ||
75 | |||
76 | unsigned long clk_get_rate(struct clk *clk) | ||
77 | { | ||
78 | if (clk == NULL || IS_ERR(clk)) | ||
79 | return -EINVAL; | ||
80 | |||
81 | return clk->rate; | ||
82 | } | ||
83 | EXPORT_SYMBOL(clk_get_rate); | ||
84 | |||
85 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
86 | { | ||
87 | if (clk == NULL || IS_ERR(clk)) | ||
88 | return -EINVAL; | ||
89 | |||
90 | if (clk->round_rate) | ||
91 | return clk->round_rate(clk, rate); | ||
92 | |||
93 | return clk->rate; | ||
94 | } | ||
95 | EXPORT_SYMBOL(clk_round_rate); | ||
96 | |||
97 | /* Propagate rate to children */ | ||
98 | static void propagate_rate(struct clk *root) | ||
99 | { | ||
100 | struct clk *clk; | ||
101 | |||
102 | list_for_each_entry(clk, &root->children, childnode) { | ||
103 | if (clk->recalc) | ||
104 | clk->rate = clk->recalc(clk); | ||
105 | propagate_rate(clk); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
110 | { | ||
111 | unsigned long flags; | ||
112 | int ret = -EINVAL; | ||
113 | |||
114 | if (clk == NULL || IS_ERR(clk)) | ||
115 | return ret; | ||
116 | |||
117 | if (clk->set_rate) | ||
118 | ret = clk->set_rate(clk, rate); | ||
119 | |||
120 | spin_lock_irqsave(&clockfw_lock, flags); | ||
121 | if (ret == 0) { | ||
122 | if (clk->recalc) | ||
123 | clk->rate = clk->recalc(clk); | ||
124 | propagate_rate(clk); | ||
125 | } | ||
126 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
127 | |||
128 | return ret; | ||
129 | } | ||
130 | EXPORT_SYMBOL(clk_set_rate); | ||
131 | |||
132 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
133 | { | ||
134 | unsigned long flags; | ||
135 | |||
136 | if (clk == NULL || IS_ERR(clk)) | ||
137 | return -EINVAL; | ||
138 | |||
139 | /* Cannot change parent on enabled clock */ | ||
140 | if (WARN_ON(clk->usecount)) | ||
141 | return -EINVAL; | ||
142 | |||
143 | mutex_lock(&clocks_mutex); | ||
144 | clk->parent = parent; | ||
145 | list_del_init(&clk->childnode); | ||
146 | list_add(&clk->childnode, &clk->parent->children); | ||
147 | mutex_unlock(&clocks_mutex); | ||
148 | |||
149 | spin_lock_irqsave(&clockfw_lock, flags); | ||
150 | if (clk->recalc) | ||
151 | clk->rate = clk->recalc(clk); | ||
152 | propagate_rate(clk); | ||
153 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | EXPORT_SYMBOL(clk_set_parent); | ||
158 | |||
159 | int clk_register(struct clk *clk) | ||
160 | { | ||
161 | if (clk == NULL || IS_ERR(clk)) | ||
162 | return -EINVAL; | ||
163 | |||
164 | if (WARN(clk->parent && !clk->parent->rate, | ||
165 | "CLK: %s parent %s has no rate!\n", | ||
166 | clk->name, clk->parent->name)) | ||
167 | return -EINVAL; | ||
168 | |||
169 | mutex_lock(&clocks_mutex); | ||
170 | list_add_tail(&clk->node, &clocks); | ||
171 | if (clk->parent) | ||
172 | list_add_tail(&clk->childnode, &clk->parent->children); | ||
173 | mutex_unlock(&clocks_mutex); | ||
174 | |||
175 | /* If rate is already set, use it */ | ||
176 | if (clk->rate) | ||
177 | return 0; | ||
178 | |||
179 | /* Else, see if there is a way to calculate it */ | ||
180 | if (clk->recalc) | ||
181 | clk->rate = clk->recalc(clk); | ||
182 | |||
183 | /* Otherwise, default to parent rate */ | ||
184 | else if (clk->parent) | ||
185 | clk->rate = clk->parent->rate; | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | EXPORT_SYMBOL(clk_register); | ||
190 | |||
191 | void clk_unregister(struct clk *clk) | ||
192 | { | ||
193 | if (clk == NULL || IS_ERR(clk)) | ||
194 | return; | ||
195 | |||
196 | mutex_lock(&clocks_mutex); | ||
197 | list_del(&clk->node); | ||
198 | list_del(&clk->childnode); | ||
199 | mutex_unlock(&clocks_mutex); | ||
200 | } | ||
201 | EXPORT_SYMBOL(clk_unregister); | ||
202 | |||
203 | |||
204 | static u32 pll_read(struct pll_data *pll, int reg) | ||
205 | { | ||
206 | return soc_readl(pll->base + reg); | ||
207 | } | ||
208 | |||
209 | static unsigned long clk_sysclk_recalc(struct clk *clk) | ||
210 | { | ||
211 | u32 v, plldiv = 0; | ||
212 | struct pll_data *pll; | ||
213 | unsigned long rate = clk->rate; | ||
214 | |||
215 | if (WARN_ON(!clk->parent)) | ||
216 | return rate; | ||
217 | |||
218 | rate = clk->parent->rate; | ||
219 | |||
220 | /* the parent must be a PLL */ | ||
221 | if (WARN_ON(!clk->parent->pll_data)) | ||
222 | return rate; | ||
223 | |||
224 | pll = clk->parent->pll_data; | ||
225 | |||
226 | /* If pre-PLL, source clock is before the multiplier and divider(s) */ | ||
227 | if (clk->flags & PRE_PLL) | ||
228 | rate = pll->input_rate; | ||
229 | |||
230 | if (!clk->div) { | ||
231 | pr_debug("%s: (no divider) rate = %lu KHz\n", | ||
232 | clk->name, rate / 1000); | ||
233 | return rate; | ||
234 | } | ||
235 | |||
236 | if (clk->flags & FIXED_DIV_PLL) { | ||
237 | rate /= clk->div; | ||
238 | pr_debug("%s: (fixed divide by %d) rate = %lu KHz\n", | ||
239 | clk->name, clk->div, rate / 1000); | ||
240 | return rate; | ||
241 | } | ||
242 | |||
243 | v = pll_read(pll, clk->div); | ||
244 | if (v & PLLDIV_EN) | ||
245 | plldiv = (v & PLLDIV_RATIO_MASK) + 1; | ||
246 | |||
247 | if (plldiv == 0) | ||
248 | plldiv = 1; | ||
249 | |||
250 | rate /= plldiv; | ||
251 | |||
252 | pr_debug("%s: (divide by %d) rate = %lu KHz\n", | ||
253 | clk->name, plldiv, rate / 1000); | ||
254 | |||
255 | return rate; | ||
256 | } | ||
257 | |||
258 | static unsigned long clk_leafclk_recalc(struct clk *clk) | ||
259 | { | ||
260 | if (WARN_ON(!clk->parent)) | ||
261 | return clk->rate; | ||
262 | |||
263 | pr_debug("%s: (parent %s) rate = %lu KHz\n", | ||
264 | clk->name, clk->parent->name, clk->parent->rate / 1000); | ||
265 | |||
266 | return clk->parent->rate; | ||
267 | } | ||
268 | |||
269 | static unsigned long clk_pllclk_recalc(struct clk *clk) | ||
270 | { | ||
271 | u32 ctrl, mult = 0, prediv = 0, postdiv = 0; | ||
272 | u8 bypass; | ||
273 | struct pll_data *pll = clk->pll_data; | ||
274 | unsigned long rate = clk->rate; | ||
275 | |||
276 | if (clk->flags & FIXED_RATE_PLL) | ||
277 | return rate; | ||
278 | |||
279 | ctrl = pll_read(pll, PLLCTL); | ||
280 | rate = pll->input_rate = clk->parent->rate; | ||
281 | |||
282 | if (ctrl & PLLCTL_PLLEN) | ||
283 | bypass = 0; | ||
284 | else | ||
285 | bypass = 1; | ||
286 | |||
287 | if (pll->flags & PLL_HAS_MUL) { | ||
288 | mult = pll_read(pll, PLLM); | ||
289 | mult = (mult & PLLM_PLLM_MASK) + 1; | ||
290 | } | ||
291 | if (pll->flags & PLL_HAS_PRE) { | ||
292 | prediv = pll_read(pll, PLLPRE); | ||
293 | if (prediv & PLLDIV_EN) | ||
294 | prediv = (prediv & PLLDIV_RATIO_MASK) + 1; | ||
295 | else | ||
296 | prediv = 0; | ||
297 | } | ||
298 | if (pll->flags & PLL_HAS_POST) { | ||
299 | postdiv = pll_read(pll, PLLPOST); | ||
300 | if (postdiv & PLLDIV_EN) | ||
301 | postdiv = (postdiv & PLLDIV_RATIO_MASK) + 1; | ||
302 | else | ||
303 | postdiv = 1; | ||
304 | } | ||
305 | |||
306 | if (!bypass) { | ||
307 | if (prediv) | ||
308 | rate /= prediv; | ||
309 | if (mult) | ||
310 | rate *= mult; | ||
311 | if (postdiv) | ||
312 | rate /= postdiv; | ||
313 | |||
314 | pr_debug("PLL%d: input = %luMHz, pre[%d] mul[%d] post[%d] " | ||
315 | "--> %luMHz output.\n", | ||
316 | pll->num, clk->parent->rate / 1000000, | ||
317 | prediv, mult, postdiv, rate / 1000000); | ||
318 | } else | ||
319 | pr_debug("PLL%d: input = %luMHz, bypass mode.\n", | ||
320 | pll->num, clk->parent->rate / 1000000); | ||
321 | |||
322 | return rate; | ||
323 | } | ||
324 | |||
325 | |||
326 | static void __init __init_clk(struct clk *clk) | ||
327 | { | ||
328 | INIT_LIST_HEAD(&clk->node); | ||
329 | INIT_LIST_HEAD(&clk->children); | ||
330 | INIT_LIST_HEAD(&clk->childnode); | ||
331 | |||
332 | if (!clk->recalc) { | ||
333 | |||
334 | /* Check if clock is a PLL */ | ||
335 | if (clk->pll_data) | ||
336 | clk->recalc = clk_pllclk_recalc; | ||
337 | |||
338 | /* Else, if it is a PLL-derived clock */ | ||
339 | else if (clk->flags & CLK_PLL) | ||
340 | clk->recalc = clk_sysclk_recalc; | ||
341 | |||
342 | /* Otherwise, it is a leaf clock (PSC clock) */ | ||
343 | else if (clk->parent) | ||
344 | clk->recalc = clk_leafclk_recalc; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | void __init c6x_clks_init(struct clk_lookup *clocks) | ||
349 | { | ||
350 | struct clk_lookup *c; | ||
351 | struct clk *clk; | ||
352 | size_t num_clocks = 0; | ||
353 | |||
354 | for (c = clocks; c->clk; c++) { | ||
355 | clk = c->clk; | ||
356 | |||
357 | __init_clk(clk); | ||
358 | clk_register(clk); | ||
359 | num_clocks++; | ||
360 | |||
361 | /* Turn on clocks that Linux doesn't otherwise manage */ | ||
362 | if (clk->flags & ALWAYS_ENABLED) | ||
363 | clk_enable(clk); | ||
364 | } | ||
365 | |||
366 | clkdev_add_table(clocks, num_clocks); | ||
367 | } | ||
368 | |||
369 | #ifdef CONFIG_DEBUG_FS | ||
370 | |||
371 | #include <linux/debugfs.h> | ||
372 | #include <linux/seq_file.h> | ||
373 | |||
374 | #define CLKNAME_MAX 10 /* longest clock name */ | ||
375 | #define NEST_DELTA 2 | ||
376 | #define NEST_MAX 4 | ||
377 | |||
378 | static void | ||
379 | dump_clock(struct seq_file *s, unsigned nest, struct clk *parent) | ||
380 | { | ||
381 | char *state; | ||
382 | char buf[CLKNAME_MAX + NEST_DELTA * NEST_MAX]; | ||
383 | struct clk *clk; | ||
384 | unsigned i; | ||
385 | |||
386 | if (parent->flags & CLK_PLL) | ||
387 | state = "pll"; | ||
388 | else | ||
389 | state = ""; | ||
390 | |||
391 | /* <nest spaces> name <pad to end> */ | ||
392 | memset(buf, ' ', sizeof(buf) - 1); | ||
393 | buf[sizeof(buf) - 1] = 0; | ||
394 | i = strlen(parent->name); | ||
395 | memcpy(buf + nest, parent->name, | ||
396 | min(i, (unsigned)(sizeof(buf) - 1 - nest))); | ||
397 | |||
398 | seq_printf(s, "%s users=%2d %-3s %9ld Hz\n", | ||
399 | buf, parent->usecount, state, clk_get_rate(parent)); | ||
400 | /* REVISIT show device associations too */ | ||
401 | |||
402 | /* cost is now small, but not linear... */ | ||
403 | list_for_each_entry(clk, &parent->children, childnode) { | ||
404 | dump_clock(s, nest + NEST_DELTA, clk); | ||
405 | } | ||
406 | } | ||
407 | |||
408 | static int c6x_ck_show(struct seq_file *m, void *v) | ||
409 | { | ||
410 | struct clk *clk; | ||
411 | |||
412 | /* | ||
413 | * Show clock tree; We trust nonzero usecounts equate to PSC enables... | ||
414 | */ | ||
415 | mutex_lock(&clocks_mutex); | ||
416 | list_for_each_entry(clk, &clocks, node) | ||
417 | if (!clk->parent) | ||
418 | dump_clock(m, 0, clk); | ||
419 | mutex_unlock(&clocks_mutex); | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | static int c6x_ck_open(struct inode *inode, struct file *file) | ||
425 | { | ||
426 | return single_open(file, c6x_ck_show, NULL); | ||
427 | } | ||
428 | |||
429 | static const struct file_operations c6x_ck_operations = { | ||
430 | .open = c6x_ck_open, | ||
431 | .read = seq_read, | ||
432 | .llseek = seq_lseek, | ||
433 | .release = single_release, | ||
434 | }; | ||
435 | |||
436 | static int __init c6x_clk_debugfs_init(void) | ||
437 | { | ||
438 | debugfs_create_file("c6x_clocks", S_IFREG | S_IRUGO, NULL, NULL, | ||
439 | &c6x_ck_operations); | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | device_initcall(c6x_clk_debugfs_init); | ||
444 | #endif /* CONFIG_DEBUG_FS */ | ||
diff --git a/arch/c6x/platforms/plldata.c b/arch/c6x/platforms/plldata.c new file mode 100644 index 000000000000..2cfd6f42968f --- /dev/null +++ b/arch/c6x/platforms/plldata.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * Port on Texas Instruments TMS320C6x architecture | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Incorporated | ||
5 | * Author: Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/ioport.h> | ||
16 | #include <linux/clkdev.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_address.h> | ||
19 | |||
20 | #include <asm/clock.h> | ||
21 | #include <asm/setup.h> | ||
22 | #include <asm/irq.h> | ||
23 | |||
24 | /* | ||
25 | * Common SoC clock support. | ||
26 | */ | ||
27 | |||
28 | /* Default input for PLL1 */ | ||
29 | struct clk clkin1 = { | ||
30 | .name = "clkin1", | ||
31 | .node = LIST_HEAD_INIT(clkin1.node), | ||
32 | .children = LIST_HEAD_INIT(clkin1.children), | ||
33 | .childnode = LIST_HEAD_INIT(clkin1.childnode), | ||
34 | }; | ||
35 | |||
36 | struct pll_data c6x_soc_pll1 = { | ||
37 | .num = 1, | ||
38 | .sysclks = { | ||
39 | { | ||
40 | .name = "pll1", | ||
41 | .parent = &clkin1, | ||
42 | .pll_data = &c6x_soc_pll1, | ||
43 | .flags = CLK_PLL, | ||
44 | }, | ||
45 | { | ||
46 | .name = "pll1_sysclk1", | ||
47 | .parent = &c6x_soc_pll1.sysclks[0], | ||
48 | .flags = CLK_PLL, | ||
49 | }, | ||
50 | { | ||
51 | .name = "pll1_sysclk2", | ||
52 | .parent = &c6x_soc_pll1.sysclks[0], | ||
53 | .flags = CLK_PLL, | ||
54 | }, | ||
55 | { | ||
56 | .name = "pll1_sysclk3", | ||
57 | .parent = &c6x_soc_pll1.sysclks[0], | ||
58 | .flags = CLK_PLL, | ||
59 | }, | ||
60 | { | ||
61 | .name = "pll1_sysclk4", | ||
62 | .parent = &c6x_soc_pll1.sysclks[0], | ||
63 | .flags = CLK_PLL, | ||
64 | }, | ||
65 | { | ||
66 | .name = "pll1_sysclk5", | ||
67 | .parent = &c6x_soc_pll1.sysclks[0], | ||
68 | .flags = CLK_PLL, | ||
69 | }, | ||
70 | { | ||
71 | .name = "pll1_sysclk6", | ||
72 | .parent = &c6x_soc_pll1.sysclks[0], | ||
73 | .flags = CLK_PLL, | ||
74 | }, | ||
75 | { | ||
76 | .name = "pll1_sysclk7", | ||
77 | .parent = &c6x_soc_pll1.sysclks[0], | ||
78 | .flags = CLK_PLL, | ||
79 | }, | ||
80 | { | ||
81 | .name = "pll1_sysclk8", | ||
82 | .parent = &c6x_soc_pll1.sysclks[0], | ||
83 | .flags = CLK_PLL, | ||
84 | }, | ||
85 | { | ||
86 | .name = "pll1_sysclk9", | ||
87 | .parent = &c6x_soc_pll1.sysclks[0], | ||
88 | .flags = CLK_PLL, | ||
89 | }, | ||
90 | { | ||
91 | .name = "pll1_sysclk10", | ||
92 | .parent = &c6x_soc_pll1.sysclks[0], | ||
93 | .flags = CLK_PLL, | ||
94 | }, | ||
95 | { | ||
96 | .name = "pll1_sysclk11", | ||
97 | .parent = &c6x_soc_pll1.sysclks[0], | ||
98 | .flags = CLK_PLL, | ||
99 | }, | ||
100 | { | ||
101 | .name = "pll1_sysclk12", | ||
102 | .parent = &c6x_soc_pll1.sysclks[0], | ||
103 | .flags = CLK_PLL, | ||
104 | }, | ||
105 | { | ||
106 | .name = "pll1_sysclk13", | ||
107 | .parent = &c6x_soc_pll1.sysclks[0], | ||
108 | .flags = CLK_PLL, | ||
109 | }, | ||
110 | { | ||
111 | .name = "pll1_sysclk14", | ||
112 | .parent = &c6x_soc_pll1.sysclks[0], | ||
113 | .flags = CLK_PLL, | ||
114 | }, | ||
115 | { | ||
116 | .name = "pll1_sysclk15", | ||
117 | .parent = &c6x_soc_pll1.sysclks[0], | ||
118 | .flags = CLK_PLL, | ||
119 | }, | ||
120 | { | ||
121 | .name = "pll1_sysclk16", | ||
122 | .parent = &c6x_soc_pll1.sysclks[0], | ||
123 | .flags = CLK_PLL, | ||
124 | }, | ||
125 | }, | ||
126 | }; | ||
127 | |||
128 | /* CPU core clock */ | ||
129 | struct clk c6x_core_clk = { | ||
130 | .name = "core", | ||
131 | }; | ||
132 | |||
133 | /* miscellaneous IO clocks */ | ||
134 | struct clk c6x_i2c_clk = { | ||
135 | .name = "i2c", | ||
136 | }; | ||
137 | |||
138 | struct clk c6x_watchdog_clk = { | ||
139 | .name = "watchdog", | ||
140 | }; | ||
141 | |||
142 | struct clk c6x_mcbsp1_clk = { | ||
143 | .name = "mcbsp1", | ||
144 | }; | ||
145 | |||
146 | struct clk c6x_mcbsp2_clk = { | ||
147 | .name = "mcbsp2", | ||
148 | }; | ||
149 | |||
150 | struct clk c6x_mdio_clk = { | ||
151 | .name = "mdio", | ||
152 | }; | ||
153 | |||
154 | |||
155 | #ifdef CONFIG_SOC_TMS320C6455 | ||
156 | static struct clk_lookup c6455_clks[] = { | ||
157 | CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]), | ||
158 | CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]), | ||
159 | CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]), | ||
160 | CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]), | ||
161 | CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]), | ||
162 | CLK(NULL, "core", &c6x_core_clk), | ||
163 | CLK("i2c_davinci.1", NULL, &c6x_i2c_clk), | ||
164 | CLK("watchdog", NULL, &c6x_watchdog_clk), | ||
165 | CLK("2c81800.mdio", NULL, &c6x_mdio_clk), | ||
166 | CLK("", NULL, NULL) | ||
167 | }; | ||
168 | |||
169 | |||
170 | static void __init c6455_setup_clocks(struct device_node *node) | ||
171 | { | ||
172 | struct pll_data *pll = &c6x_soc_pll1; | ||
173 | struct clk *sysclks = pll->sysclks; | ||
174 | |||
175 | pll->flags = PLL_HAS_PRE | PLL_HAS_MUL; | ||
176 | |||
177 | sysclks[2].flags |= FIXED_DIV_PLL; | ||
178 | sysclks[2].div = 3; | ||
179 | sysclks[3].flags |= FIXED_DIV_PLL; | ||
180 | sysclks[3].div = 6; | ||
181 | sysclks[4].div = PLLDIV4; | ||
182 | sysclks[5].div = PLLDIV5; | ||
183 | |||
184 | c6x_core_clk.parent = &sysclks[0]; | ||
185 | c6x_i2c_clk.parent = &sysclks[3]; | ||
186 | c6x_watchdog_clk.parent = &sysclks[3]; | ||
187 | c6x_mdio_clk.parent = &sysclks[3]; | ||
188 | |||
189 | c6x_clks_init(c6455_clks); | ||
190 | } | ||
191 | #endif /* CONFIG_SOC_TMS320C6455 */ | ||
192 | |||
193 | #ifdef CONFIG_SOC_TMS320C6457 | ||
194 | static struct clk_lookup c6457_clks[] = { | ||
195 | CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]), | ||
196 | CLK(NULL, "pll1_sysclk1", &c6x_soc_pll1.sysclks[1]), | ||
197 | CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]), | ||
198 | CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]), | ||
199 | CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]), | ||
200 | CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]), | ||
201 | CLK(NULL, "core", &c6x_core_clk), | ||
202 | CLK("i2c_davinci.1", NULL, &c6x_i2c_clk), | ||
203 | CLK("watchdog", NULL, &c6x_watchdog_clk), | ||
204 | CLK("2c81800.mdio", NULL, &c6x_mdio_clk), | ||
205 | CLK("", NULL, NULL) | ||
206 | }; | ||
207 | |||
208 | static void __init c6457_setup_clocks(struct device_node *node) | ||
209 | { | ||
210 | struct pll_data *pll = &c6x_soc_pll1; | ||
211 | struct clk *sysclks = pll->sysclks; | ||
212 | |||
213 | pll->flags = PLL_HAS_MUL | PLL_HAS_POST; | ||
214 | |||
215 | sysclks[1].flags |= FIXED_DIV_PLL; | ||
216 | sysclks[1].div = 1; | ||
217 | sysclks[2].flags |= FIXED_DIV_PLL; | ||
218 | sysclks[2].div = 3; | ||
219 | sysclks[3].flags |= FIXED_DIV_PLL; | ||
220 | sysclks[3].div = 6; | ||
221 | sysclks[4].div = PLLDIV4; | ||
222 | sysclks[5].div = PLLDIV5; | ||
223 | |||
224 | c6x_core_clk.parent = &sysclks[1]; | ||
225 | c6x_i2c_clk.parent = &sysclks[3]; | ||
226 | c6x_watchdog_clk.parent = &sysclks[5]; | ||
227 | c6x_mdio_clk.parent = &sysclks[5]; | ||
228 | |||
229 | c6x_clks_init(c6457_clks); | ||
230 | } | ||
231 | #endif /* CONFIG_SOC_TMS320C6455 */ | ||
232 | |||
233 | #ifdef CONFIG_SOC_TMS320C6472 | ||
234 | static struct clk_lookup c6472_clks[] = { | ||
235 | CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]), | ||
236 | CLK(NULL, "pll1_sysclk1", &c6x_soc_pll1.sysclks[1]), | ||
237 | CLK(NULL, "pll1_sysclk2", &c6x_soc_pll1.sysclks[2]), | ||
238 | CLK(NULL, "pll1_sysclk3", &c6x_soc_pll1.sysclks[3]), | ||
239 | CLK(NULL, "pll1_sysclk4", &c6x_soc_pll1.sysclks[4]), | ||
240 | CLK(NULL, "pll1_sysclk5", &c6x_soc_pll1.sysclks[5]), | ||
241 | CLK(NULL, "pll1_sysclk6", &c6x_soc_pll1.sysclks[6]), | ||
242 | CLK(NULL, "pll1_sysclk7", &c6x_soc_pll1.sysclks[7]), | ||
243 | CLK(NULL, "pll1_sysclk8", &c6x_soc_pll1.sysclks[8]), | ||
244 | CLK(NULL, "pll1_sysclk9", &c6x_soc_pll1.sysclks[9]), | ||
245 | CLK(NULL, "pll1_sysclk10", &c6x_soc_pll1.sysclks[10]), | ||
246 | CLK(NULL, "core", &c6x_core_clk), | ||
247 | CLK("i2c_davinci.1", NULL, &c6x_i2c_clk), | ||
248 | CLK("watchdog", NULL, &c6x_watchdog_clk), | ||
249 | CLK("2c81800.mdio", NULL, &c6x_mdio_clk), | ||
250 | CLK("", NULL, NULL) | ||
251 | }; | ||
252 | |||
253 | /* assumptions used for delay loop calculations */ | ||
254 | #define MIN_CLKIN1_KHz 15625 | ||
255 | #define MAX_CORE_KHz 700000 | ||
256 | #define MIN_PLLOUT_KHz MIN_CLKIN1_KHz | ||
257 | |||
258 | static void __init c6472_setup_clocks(struct device_node *node) | ||
259 | { | ||
260 | struct pll_data *pll = &c6x_soc_pll1; | ||
261 | struct clk *sysclks = pll->sysclks; | ||
262 | int i; | ||
263 | |||
264 | pll->flags = PLL_HAS_MUL; | ||
265 | |||
266 | for (i = 1; i <= 6; i++) { | ||
267 | sysclks[i].flags |= FIXED_DIV_PLL; | ||
268 | sysclks[i].div = 1; | ||
269 | } | ||
270 | |||
271 | sysclks[7].flags |= FIXED_DIV_PLL; | ||
272 | sysclks[7].div = 3; | ||
273 | sysclks[8].flags |= FIXED_DIV_PLL; | ||
274 | sysclks[8].div = 6; | ||
275 | sysclks[9].flags |= FIXED_DIV_PLL; | ||
276 | sysclks[9].div = 2; | ||
277 | sysclks[10].div = PLLDIV10; | ||
278 | |||
279 | c6x_core_clk.parent = &sysclks[get_coreid() + 1]; | ||
280 | c6x_i2c_clk.parent = &sysclks[8]; | ||
281 | c6x_watchdog_clk.parent = &sysclks[8]; | ||
282 | c6x_mdio_clk.parent = &sysclks[5]; | ||
283 | |||
284 | c6x_clks_init(c6472_clks); | ||
285 | } | ||
286 | #endif /* CONFIG_SOC_TMS320C6472 */ | ||
287 | |||
288 | |||
289 | #ifdef CONFIG_SOC_TMS320C6474 | ||
290 | static struct clk_lookup c6474_clks[] = { | ||
291 | CLK(NULL, "pll1", &c6x_soc_pll1.sysclks[0]), | ||
292 | CLK(NULL, "pll1_sysclk7", &c6x_soc_pll1.sysclks[7]), | ||
293 | CLK(NULL, "pll1_sysclk9", &c6x_soc_pll1.sysclks[9]), | ||
294 | CLK(NULL, "pll1_sysclk10", &c6x_soc_pll1.sysclks[10]), | ||
295 | CLK(NULL, "pll1_sysclk11", &c6x_soc_pll1.sysclks[11]), | ||
296 | CLK(NULL, "pll1_sysclk12", &c6x_soc_pll1.sysclks[12]), | ||
297 | CLK(NULL, "pll1_sysclk13", &c6x_soc_pll1.sysclks[13]), | ||
298 | CLK(NULL, "core", &c6x_core_clk), | ||
299 | CLK("i2c_davinci.1", NULL, &c6x_i2c_clk), | ||
300 | CLK("mcbsp.1", NULL, &c6x_mcbsp1_clk), | ||
301 | CLK("mcbsp.2", NULL, &c6x_mcbsp2_clk), | ||
302 | CLK("watchdog", NULL, &c6x_watchdog_clk), | ||
303 | CLK("2c81800.mdio", NULL, &c6x_mdio_clk), | ||
304 | CLK("", NULL, NULL) | ||
305 | }; | ||
306 | |||
307 | static void __init c6474_setup_clocks(struct device_node *node) | ||
308 | { | ||
309 | struct pll_data *pll = &c6x_soc_pll1; | ||
310 | struct clk *sysclks = pll->sysclks; | ||
311 | |||
312 | pll->flags = PLL_HAS_MUL; | ||
313 | |||
314 | sysclks[7].flags |= FIXED_DIV_PLL; | ||
315 | sysclks[7].div = 1; | ||
316 | sysclks[9].flags |= FIXED_DIV_PLL; | ||
317 | sysclks[9].div = 3; | ||
318 | sysclks[10].flags |= FIXED_DIV_PLL; | ||
319 | sysclks[10].div = 6; | ||
320 | |||
321 | sysclks[11].div = PLLDIV11; | ||
322 | |||
323 | sysclks[12].flags |= FIXED_DIV_PLL; | ||
324 | sysclks[12].div = 2; | ||
325 | |||
326 | sysclks[13].div = PLLDIV13; | ||
327 | |||
328 | c6x_core_clk.parent = &sysclks[7]; | ||
329 | c6x_i2c_clk.parent = &sysclks[10]; | ||
330 | c6x_watchdog_clk.parent = &sysclks[10]; | ||
331 | c6x_mcbsp1_clk.parent = &sysclks[10]; | ||
332 | c6x_mcbsp2_clk.parent = &sysclks[10]; | ||
333 | |||
334 | c6x_clks_init(c6474_clks); | ||
335 | } | ||
336 | #endif /* CONFIG_SOC_TMS320C6474 */ | ||
337 | |||
338 | static struct of_device_id c6x_clkc_match[] __initdata = { | ||
339 | #ifdef CONFIG_SOC_TMS320C6455 | ||
340 | { .compatible = "ti,c6455-pll", .data = c6455_setup_clocks }, | ||
341 | #endif | ||
342 | #ifdef CONFIG_SOC_TMS320C6457 | ||
343 | { .compatible = "ti,c6457-pll", .data = c6457_setup_clocks }, | ||
344 | #endif | ||
345 | #ifdef CONFIG_SOC_TMS320C6472 | ||
346 | { .compatible = "ti,c6472-pll", .data = c6472_setup_clocks }, | ||
347 | #endif | ||
348 | #ifdef CONFIG_SOC_TMS320C6474 | ||
349 | { .compatible = "ti,c6474-pll", .data = c6474_setup_clocks }, | ||
350 | #endif | ||
351 | { .compatible = "ti,c64x+pll" }, | ||
352 | {} | ||
353 | }; | ||
354 | |||
355 | void __init c64x_setup_clocks(void) | ||
356 | { | ||
357 | void (*__setup_clocks)(struct device_node *np); | ||
358 | struct pll_data *pll = &c6x_soc_pll1; | ||
359 | struct device_node *node; | ||
360 | const struct of_device_id *id; | ||
361 | int err; | ||
362 | u32 val; | ||
363 | |||
364 | node = of_find_matching_node(NULL, c6x_clkc_match); | ||
365 | if (!node) | ||
366 | return; | ||
367 | |||
368 | pll->base = of_iomap(node, 0); | ||
369 | if (!pll->base) | ||
370 | goto out; | ||
371 | |||
372 | err = of_property_read_u32(node, "clock-frequency", &val); | ||
373 | if (err || val == 0) { | ||
374 | pr_err("%s: no clock-frequency found! Using %dMHz\n", | ||
375 | node->full_name, (int)val / 1000000); | ||
376 | val = 25000000; | ||
377 | } | ||
378 | clkin1.rate = val; | ||
379 | |||
380 | err = of_property_read_u32(node, "ti,c64x+pll-bypass-delay", &val); | ||
381 | if (err) | ||
382 | val = 5000; | ||
383 | pll->bypass_delay = val; | ||
384 | |||
385 | err = of_property_read_u32(node, "ti,c64x+pll-reset-delay", &val); | ||
386 | if (err) | ||
387 | val = 30000; | ||
388 | pll->reset_delay = val; | ||
389 | |||
390 | err = of_property_read_u32(node, "ti,c64x+pll-lock-delay", &val); | ||
391 | if (err) | ||
392 | val = 30000; | ||
393 | pll->lock_delay = val; | ||
394 | |||
395 | /* id->data is a pointer to SoC-specific setup */ | ||
396 | id = of_match_node(c6x_clkc_match, node); | ||
397 | if (id && id->data) { | ||
398 | __setup_clocks = id->data; | ||
399 | __setup_clocks(node); | ||
400 | } | ||
401 | |||
402 | out: | ||
403 | of_node_put(node); | ||
404 | } | ||
diff --git a/arch/c6x/platforms/timer64.c b/arch/c6x/platforms/timer64.c new file mode 100644 index 000000000000..03c03c249191 --- /dev/null +++ b/arch/c6x/platforms/timer64.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010, 2011 Texas Instruments Incorporated | ||
3 | * Contributed by: Mark Salter (msalter@redhat.com) | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/clockchips.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/of_irq.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <asm/soc.h> | ||
17 | #include <asm/dscr.h> | ||
18 | #include <asm/timer64.h> | ||
19 | |||
20 | struct timer_regs { | ||
21 | u32 reserved0; | ||
22 | u32 emumgt; | ||
23 | u32 reserved1; | ||
24 | u32 reserved2; | ||
25 | u32 cntlo; | ||
26 | u32 cnthi; | ||
27 | u32 prdlo; | ||
28 | u32 prdhi; | ||
29 | u32 tcr; | ||
30 | u32 tgcr; | ||
31 | u32 wdtcr; | ||
32 | }; | ||
33 | |||
34 | static struct timer_regs __iomem *timer; | ||
35 | |||
36 | #define TCR_TSTATLO 0x001 | ||
37 | #define TCR_INVOUTPLO 0x002 | ||
38 | #define TCR_INVINPLO 0x004 | ||
39 | #define TCR_CPLO 0x008 | ||
40 | #define TCR_ENAMODELO_ONCE 0x040 | ||
41 | #define TCR_ENAMODELO_CONT 0x080 | ||
42 | #define TCR_ENAMODELO_MASK 0x0c0 | ||
43 | #define TCR_PWIDLO_MASK 0x030 | ||
44 | #define TCR_CLKSRCLO 0x100 | ||
45 | #define TCR_TIENLO 0x200 | ||
46 | #define TCR_TSTATHI (0x001 << 16) | ||
47 | #define TCR_INVOUTPHI (0x002 << 16) | ||
48 | #define TCR_CPHI (0x008 << 16) | ||
49 | #define TCR_PWIDHI_MASK (0x030 << 16) | ||
50 | #define TCR_ENAMODEHI_ONCE (0x040 << 16) | ||
51 | #define TCR_ENAMODEHI_CONT (0x080 << 16) | ||
52 | #define TCR_ENAMODEHI_MASK (0x0c0 << 16) | ||
53 | |||
54 | #define TGCR_TIMLORS 0x001 | ||
55 | #define TGCR_TIMHIRS 0x002 | ||
56 | #define TGCR_TIMMODE_UD32 0x004 | ||
57 | #define TGCR_TIMMODE_WDT64 0x008 | ||
58 | #define TGCR_TIMMODE_CD32 0x00c | ||
59 | #define TGCR_TIMMODE_MASK 0x00c | ||
60 | #define TGCR_PSCHI_MASK (0x00f << 8) | ||
61 | #define TGCR_TDDRHI_MASK (0x00f << 12) | ||
62 | |||
63 | /* | ||
64 | * Timer clocks are divided down from the CPU clock | ||
65 | * The divisor is in the EMUMGTCLKSPD register | ||
66 | */ | ||
67 | #define TIMER_DIVISOR \ | ||
68 | ((soc_readl(&timer->emumgt) & (0xf << 16)) >> 16) | ||
69 | |||
70 | #define TIMER64_RATE (c6x_core_freq / TIMER_DIVISOR) | ||
71 | |||
72 | #define TIMER64_MODE_DISABLED 0 | ||
73 | #define TIMER64_MODE_ONE_SHOT TCR_ENAMODELO_ONCE | ||
74 | #define TIMER64_MODE_PERIODIC TCR_ENAMODELO_CONT | ||
75 | |||
76 | static int timer64_mode; | ||
77 | static int timer64_devstate_id = -1; | ||
78 | |||
79 | static void timer64_config(unsigned long period) | ||
80 | { | ||
81 | u32 tcr = soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK; | ||
82 | |||
83 | soc_writel(tcr, &timer->tcr); | ||
84 | soc_writel(period - 1, &timer->prdlo); | ||
85 | soc_writel(0, &timer->cntlo); | ||
86 | tcr |= timer64_mode; | ||
87 | soc_writel(tcr, &timer->tcr); | ||
88 | } | ||
89 | |||
90 | static void timer64_enable(void) | ||
91 | { | ||
92 | u32 val; | ||
93 | |||
94 | if (timer64_devstate_id >= 0) | ||
95 | dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED); | ||
96 | |||
97 | /* disable timer, reset count */ | ||
98 | soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); | ||
99 | soc_writel(0, &timer->prdlo); | ||
100 | |||
101 | /* use internal clock and 1 cycle pulse width */ | ||
102 | val = soc_readl(&timer->tcr); | ||
103 | soc_writel(val & ~(TCR_CLKSRCLO | TCR_PWIDLO_MASK), &timer->tcr); | ||
104 | |||
105 | /* dual 32-bit unchained mode */ | ||
106 | val = soc_readl(&timer->tgcr) & ~TGCR_TIMMODE_MASK; | ||
107 | soc_writel(val, &timer->tgcr); | ||
108 | soc_writel(val | (TGCR_TIMLORS | TGCR_TIMMODE_UD32), &timer->tgcr); | ||
109 | } | ||
110 | |||
111 | static void timer64_disable(void) | ||
112 | { | ||
113 | /* disable timer, reset count */ | ||
114 | soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); | ||
115 | soc_writel(0, &timer->prdlo); | ||
116 | |||
117 | if (timer64_devstate_id >= 0) | ||
118 | dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_DISABLED); | ||
119 | } | ||
120 | |||
121 | static int next_event(unsigned long delta, | ||
122 | struct clock_event_device *evt) | ||
123 | { | ||
124 | timer64_config(delta); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static void set_clock_mode(enum clock_event_mode mode, | ||
129 | struct clock_event_device *evt) | ||
130 | { | ||
131 | switch (mode) { | ||
132 | case CLOCK_EVT_MODE_PERIODIC: | ||
133 | timer64_enable(); | ||
134 | timer64_mode = TIMER64_MODE_PERIODIC; | ||
135 | timer64_config(TIMER64_RATE / HZ); | ||
136 | break; | ||
137 | case CLOCK_EVT_MODE_ONESHOT: | ||
138 | timer64_enable(); | ||
139 | timer64_mode = TIMER64_MODE_ONE_SHOT; | ||
140 | break; | ||
141 | case CLOCK_EVT_MODE_UNUSED: | ||
142 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
143 | timer64_mode = TIMER64_MODE_DISABLED; | ||
144 | timer64_disable(); | ||
145 | break; | ||
146 | case CLOCK_EVT_MODE_RESUME: | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | static struct clock_event_device t64_clockevent_device = { | ||
152 | .name = "TIMER64_EVT32_TIMER", | ||
153 | .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, | ||
154 | .rating = 200, | ||
155 | .set_mode = set_clock_mode, | ||
156 | .set_next_event = next_event, | ||
157 | }; | ||
158 | |||
159 | static irqreturn_t timer_interrupt(int irq, void *dev_id) | ||
160 | { | ||
161 | struct clock_event_device *cd = &t64_clockevent_device; | ||
162 | |||
163 | cd->event_handler(cd); | ||
164 | |||
165 | return IRQ_HANDLED; | ||
166 | } | ||
167 | |||
168 | static struct irqaction timer_iact = { | ||
169 | .name = "timer", | ||
170 | .flags = IRQF_TIMER, | ||
171 | .handler = timer_interrupt, | ||
172 | .dev_id = &t64_clockevent_device, | ||
173 | }; | ||
174 | |||
175 | void __init timer64_init(void) | ||
176 | { | ||
177 | struct clock_event_device *cd = &t64_clockevent_device; | ||
178 | struct device_node *np, *first = NULL; | ||
179 | u32 val; | ||
180 | int err, found = 0; | ||
181 | |||
182 | for_each_compatible_node(np, NULL, "ti,c64x+timer64") { | ||
183 | err = of_property_read_u32(np, "ti,core-mask", &val); | ||
184 | if (!err) { | ||
185 | if (val & (1 << get_coreid())) { | ||
186 | found = 1; | ||
187 | break; | ||
188 | } | ||
189 | } else if (!first) | ||
190 | first = np; | ||
191 | } | ||
192 | if (!found) { | ||
193 | /* try first one with no core-mask */ | ||
194 | if (first) | ||
195 | np = of_node_get(first); | ||
196 | else { | ||
197 | pr_debug("Cannot find ti,c64x+timer64 timer.\n"); | ||
198 | return; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | timer = of_iomap(np, 0); | ||
203 | if (!timer) { | ||
204 | pr_debug("%s: Cannot map timer registers.\n", np->full_name); | ||
205 | goto out; | ||
206 | } | ||
207 | pr_debug("%s: Timer registers=%p.\n", np->full_name, timer); | ||
208 | |||
209 | cd->irq = irq_of_parse_and_map(np, 0); | ||
210 | if (cd->irq == NO_IRQ) { | ||
211 | pr_debug("%s: Cannot find interrupt.\n", np->full_name); | ||
212 | iounmap(timer); | ||
213 | goto out; | ||
214 | } | ||
215 | |||
216 | /* If there is a device state control, save the ID. */ | ||
217 | err = of_property_read_u32(np, "ti,dscr-dev-enable", &val); | ||
218 | if (!err) { | ||
219 | timer64_devstate_id = val; | ||
220 | |||
221 | /* | ||
222 | * It is necessary to enable the timer block here because | ||
223 | * the TIMER_DIVISOR macro needs to read a timer register | ||
224 | * to get the divisor. | ||
225 | */ | ||
226 | dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED); | ||
227 | } | ||
228 | |||
229 | pr_debug("%s: Timer irq=%d.\n", np->full_name, cd->irq); | ||
230 | |||
231 | clockevents_calc_mult_shift(cd, c6x_core_freq / TIMER_DIVISOR, 5); | ||
232 | |||
233 | cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); | ||
234 | cd->min_delta_ns = clockevent_delta2ns(250, cd); | ||
235 | |||
236 | cd->cpumask = cpumask_of(smp_processor_id()); | ||
237 | |||
238 | clockevents_register_device(cd); | ||
239 | setup_irq(cd->irq, &timer_iact); | ||
240 | |||
241 | out: | ||
242 | of_node_put(np); | ||
243 | return; | ||
244 | } | ||
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index 912088773a69..c2cf2eda0626 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h | |||
@@ -327,7 +327,7 @@ static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size) | |||
327 | #define ioremap_wc ioremap_nocache | 327 | #define ioremap_wc ioremap_nocache |
328 | #endif | 328 | #endif |
329 | 329 | ||
330 | static inline void iounmap(void *addr) | 330 | static inline void iounmap(void __iomem *addr) |
331 | { | 331 | { |
332 | } | 332 | } |
333 | #endif /* CONFIG_MMU */ | 333 | #endif /* CONFIG_MMU */ |
diff --git a/include/asm-generic/page.h b/include/asm-generic/page.h index 351889d1de19..37d1fe28960a 100644 --- a/include/asm-generic/page.h +++ b/include/asm-generic/page.h | |||
@@ -71,10 +71,14 @@ extern unsigned long memory_end; | |||
71 | #define PAGE_OFFSET (0) | 71 | #define PAGE_OFFSET (0) |
72 | #endif | 72 | #endif |
73 | 73 | ||
74 | #ifndef ARCH_PFN_OFFSET | ||
75 | #define ARCH_PFN_OFFSET (PAGE_OFFSET >> PAGE_SHIFT) | ||
76 | #endif | ||
77 | |||
74 | #ifndef __ASSEMBLY__ | 78 | #ifndef __ASSEMBLY__ |
75 | 79 | ||
76 | #define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET)) | 80 | #define __va(x) ((void *)((unsigned long) (x))) |
77 | #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) | 81 | #define __pa(x) ((unsigned long) (x)) |
78 | 82 | ||
79 | #define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) | 83 | #define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) |
80 | #define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT) | 84 | #define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT) |
@@ -86,7 +90,7 @@ extern unsigned long memory_end; | |||
86 | #define page_to_phys(page) ((dma_addr_t)page_to_pfn(page) << PAGE_SHIFT) | 90 | #define page_to_phys(page) ((dma_addr_t)page_to_pfn(page) << PAGE_SHIFT) |
87 | #endif | 91 | #endif |
88 | 92 | ||
89 | #define pfn_valid(pfn) ((pfn) < max_mapnr) | 93 | #define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && ((pfn) - ARCH_PFN_OFFSET) < max_mapnr) |
90 | 94 | ||
91 | #define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ | 95 | #define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ |
92 | ((void *)(kaddr) < (void *)memory_end)) | 96 | ((void *)(kaddr) < (void *)memory_end)) |
diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h index ac68c999b6c2..9788568f7978 100644 --- a/include/asm-generic/uaccess.h +++ b/include/asm-generic/uaccess.h | |||
@@ -289,9 +289,14 @@ strncpy_from_user(char *dst, const char __user *src, long count) | |||
289 | * Return 0 on exception, a value greater than N if too long | 289 | * Return 0 on exception, a value greater than N if too long |
290 | */ | 290 | */ |
291 | #ifndef __strnlen_user | 291 | #ifndef __strnlen_user |
292 | #define __strnlen_user strnlen | 292 | #define __strnlen_user(s, n) (strnlen((s), (n)) + 1) |
293 | #endif | 293 | #endif |
294 | 294 | ||
295 | /* | ||
296 | * Unlike strnlen, strnlen_user includes the nul terminator in | ||
297 | * its returned count. Callers should check for a returned value | ||
298 | * greater than N as an indication the string is too long. | ||
299 | */ | ||
295 | static inline long strnlen_user(const char __user *src, long n) | 300 | static inline long strnlen_user(const char __user *src, long n) |
296 | { | 301 | { |
297 | if (!access_ok(VERIFY_READ, src, 1)) | 302 | if (!access_ok(VERIFY_READ, src, 1)) |
diff --git a/include/linux/elf-em.h b/include/linux/elf-em.h index 18bea78fe47b..8e2b7bac4378 100644 --- a/include/linux/elf-em.h +++ b/include/linux/elf-em.h | |||
@@ -33,6 +33,7 @@ | |||
33 | #define EM_H8_300 46 /* Renesas H8/300,300H,H8S */ | 33 | #define EM_H8_300 46 /* Renesas H8/300,300H,H8S */ |
34 | #define EM_MN10300 89 /* Panasonic/MEI MN10300, AM33 */ | 34 | #define EM_MN10300 89 /* Panasonic/MEI MN10300, AM33 */ |
35 | #define EM_BLACKFIN 106 /* ADI Blackfin Processor */ | 35 | #define EM_BLACKFIN 106 /* ADI Blackfin Processor */ |
36 | #define EM_TI_C6000 140 /* TI C6X DSPs */ | ||
36 | #define EM_FRV 0x5441 /* Fujitsu FR-V */ | 37 | #define EM_FRV 0x5441 /* Fujitsu FR-V */ |
37 | #define EM_AVR32 0x18ad /* Atmel AVR32 */ | 38 | #define EM_AVR32 0x18ad /* Atmel AVR32 */ |
38 | 39 | ||