blob: 6b93d048205ee03991f9465af3f565999cc7386b [file] [log] [blame]
Naveen Ramarajcc4ec152012-05-14 09:55:29 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/slab.h>
16#include <linux/mm.h>
17#include <linux/rbtree.h>
18#include <linux/genalloc.h>
19#include <linux/of.h>
20#include <linux/io.h>
21#include <linux/platform_device.h>
22#include <linux/debugfs.h>
23#include <linux/seq_file.h>
24#include <linux/delay.h>
25#include <linux/interrupt.h>
26#include <linux/wait.h>
27#include <linux/sched.h>
28#include <mach/ocmem_priv.h>
29
30#define RDM_MAX_ENTRIES 32
31#define RDM_MAX_CLIENTS 2
32
33/* Data Mover Parameters */
34#define DM_BLOCK_128 0x0
35#define DM_BLOCK_256 0x1
36#define DM_BR_ID_LPASS 0x0
37#define DM_BR_ID_GPS 0x1
38
39#define DM_INTR_CLR (0x8)
40#define DM_INTR_MASK (0xC)
41#define DM_GEN_STATUS (0x10)
42#define DM_STATUS (0x14)
43#define DM_CTRL (0x1000)
44#define DM_TBL_BASE (0x1010)
45#define DM_TBL_IDX(x) ((x) * 0x18)
46#define DM_TBL_n(x) (DM_TBL_BASE + (DM_TBL_IDX(x)))
47#define DM_TBL_n_offset(x) DM_TBL_n(x)
48#define DM_TBL_n_size(x) (DM_TBL_n(x)+0x4)
49#define DM_TBL_n_paddr(x) (DM_TBL_n(x)+0x8)
50#define DM_TBL_n_ctrl(x) (DM_TBL_n(x)+0x10)
51
52#define BR_CTRL (0x0)
53#define BR_CLIENT_BASE (0x4)
54#define BR_CLIENT_n_IDX(x) ((x) * 0x4)
55#define BR_CLIENT_n_ctrl(x) (BR_CLIENT_BASE + (BR_CLIENT_n_IDX(x)))
56#define BR_STATUS (0x14)
57/* 16 entries per client are supported */
58/* Use entries 0 - 15 for client0 */
59#define BR_CLIENT0_MASK (0x1000)
60/* Use entries 16- 31 for client1 */
61#define BR_CLIENT1_MASK (0x2010)
62
63#define BR_TBL_BASE (0x40)
64#define BR_TBL_IDX(x) ((x) * 0x18)
65#define BR_TBL_n(x) (BR_TBL_BASE + (BR_TBL_IDX(x)))
66#define BR_TBL_n_offset(x) BR_TBL_n(x)
67#define BR_TBL_n_size(x) (BR_TBL_n(x)+0x4)
68#define BR_TBL_n_paddr(x) (BR_TBL_n(x)+0x8)
69#define BR_TBL_n_ctrl(x) (BR_TBL_n(x)+0x10)
70
71/* Constants and Shifts */
72#define BR_TBL_ENTRY_ENABLE 0x1
73#define BR_TBL_START 0x0
74#define BR_TBL_END 0x8
75#define BR_RW_SHIFT 0x2
76
77#define DM_TBL_START 0x10
78#define DM_TBL_END 0x18
79#define DM_CLIENT_SHIFT 0x8
80#define DM_BR_ID_SHIFT 0x4
81#define DM_BR_BLK_SHIFT 0x1
82#define DM_DIR_SHIFT 0x0
83
84#define DM_DONE 0x1
85#define DM_INTR_ENABLE 0x0
86#define DM_INTR_DISABLE 0x1
87
88static void *br_base;
89static void *dm_base;
90
91static atomic_t dm_pending;
92static wait_queue_head_t dm_wq;
93/* Shadow tables for debug purposes */
94struct ocmem_br_table {
95 unsigned int offset;
96 unsigned int size;
97 unsigned int ddr_low;
98 unsigned int ddr_high;
99 unsigned int ctrl;
100} br_table[RDM_MAX_ENTRIES];
101
102/* DM Table replicates an entire BR table */
103/* Note: There are more than 1 BRs in the system */
104struct ocmem_dm_table {
105 unsigned int offset;
106 unsigned int size;
107 unsigned int ddr_low;
108 unsigned int ddr_high;
109 unsigned int ctrl;
110} dm_table[RDM_MAX_ENTRIES];
111
112/* Wrapper that will shadow these values later */
113static int ocmem_read(void *at)
114{
115 return readl_relaxed(at);
116}
117
118/* Wrapper that will shadow these values later */
119static int ocmem_write(unsigned long val, void *at)
120{
121 writel_relaxed(val, at);
122 return 0;
123}
124
125static inline int client_ctrl_id(int id)
126{
127 return (id == OCMEM_SENSORS) ? 1 : 0;
128}
129
130static inline int client_slot_start(int id)
131{
132
133 return client_ctrl_id(id) * 16;
134}
135
136static irqreturn_t ocmem_dm_irq_handler(int irq, void *dev_id)
137{
138 atomic_set(&dm_pending, 0);
139 ocmem_write(DM_INTR_DISABLE, dm_base + DM_INTR_CLR);
140 wake_up_interruptible(&dm_wq);
141 return IRQ_HANDLED;
142}
143
144/* Lock during transfers */
145int ocmem_rdm_transfer(int id, struct ocmem_map_list *clist,
146 unsigned long start, int direction)
147{
148 int num_chunks = clist->num_chunks;
149 int slot = client_slot_start(id);
150 int table_start = 0;
151 int table_end = 0;
152 int br_ctrl = 0;
153 int br_id = 0;
154 int dm_ctrl = 0;
155 int i = 0;
156 int j = 0;
157 int status = 0;
158
159 for (i = 0, j = slot; i < num_chunks; i++, j++) {
160
161 struct ocmem_chunk *chunk = &clist->chunks[i];
162 int sz = chunk->size;
163 int paddr = chunk->ddr_paddr;
164 int tbl_n_ctrl = 0;
165
166 tbl_n_ctrl |= BR_TBL_ENTRY_ENABLE;
167 if (chunk->ro)
168 tbl_n_ctrl |= (1 << BR_RW_SHIFT);
169
170 /* Table Entry n of BR and DM */
171 ocmem_write(start, br_base + BR_TBL_n_offset(j));
172 ocmem_write(sz, br_base + BR_TBL_n_size(j));
173 ocmem_write(paddr, br_base + BR_TBL_n_paddr(j));
174 ocmem_write(tbl_n_ctrl, br_base + BR_TBL_n_ctrl(j));
175
176 ocmem_write(start, dm_base + DM_TBL_n_offset(j));
177 ocmem_write(sz, dm_base + DM_TBL_n_size(j));
178 ocmem_write(paddr, dm_base + DM_TBL_n_paddr(j));
179 ocmem_write(tbl_n_ctrl, dm_base + DM_TBL_n_ctrl(j));
180
181 start += sz;
182 }
183
184 br_id = client_ctrl_id(id);
185 table_start = slot;
186 table_end = slot + num_chunks - 1;
187 br_ctrl |= (table_start << BR_TBL_START);
188 br_ctrl |= (table_end << BR_TBL_END);
189
190 ocmem_write(br_ctrl, (br_base + BR_CLIENT_n_ctrl(br_id)));
191 /* Enable BR */
192 ocmem_write(0x1, br_base + BR_CTRL);
193
194 /* Compute DM Control Value */
195 dm_ctrl |= (table_start << DM_TBL_START);
196 dm_ctrl |= (table_end << DM_TBL_END);
197
198 dm_ctrl |= (DM_BR_ID_LPASS << DM_BR_ID_SHIFT);
199 dm_ctrl |= (DM_BLOCK_256 << DM_BR_BLK_SHIFT);
200 dm_ctrl |= (direction << DM_DIR_SHIFT);
201
202 status = ocmem_read(dm_base + DM_STATUS);
203 pr_debug("Transfer status before %x\n", status);
204 atomic_set(&dm_pending, 1);
205 /* Trigger DM */
206 ocmem_write(dm_ctrl, dm_base + DM_CTRL);
207 pr_debug("ocmem: rdm: dm_ctrl %x br_ctrl %x\n", dm_ctrl, br_ctrl);
208
209 wait_event_interruptible(dm_wq,
210 atomic_read(&dm_pending) == 0);
211
212 return 0;
213}
214
215int ocmem_rdm_init(struct platform_device *pdev)
216{
217
218 struct ocmem_plat_data *pdata = NULL;
219 int rc = 0;
220
221 pdata = platform_get_drvdata(pdev);
222
223 br_base = pdata->br_base;
224 dm_base = pdata->dm_base;
225
226 rc = devm_request_irq(&pdev->dev, pdata->dm_irq, ocmem_dm_irq_handler,
227 IRQF_TRIGGER_RISING, "ocmem_dm_irq", pdata);
228
229 if (rc) {
230 dev_err(&pdev->dev, "Failed to request dm irq");
231 return -EINVAL;
232 }
233
234 init_waitqueue_head(&dm_wq);
235 /* enable dm interrupts */
236 ocmem_write(DM_INTR_ENABLE, dm_base + DM_INTR_MASK);
237 return 0;
238}