1 /*
2  * SuperH Mobile MERAM Driver for SuperH Mobile LCDC Driver
3  *
4  * Copyright (c) 2011	Damian Hobson-Garcia <dhobsong@igel.co.jp>
5  *                      Takanari Hayama <taki@igel.co.jp>
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License.  See the file "COPYING" in the main directory of this archive
9  * for more details.
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/device.h>
15 #include <linux/pm_runtime.h>
16 #include <linux/io.h>
17 #include <linux/slab.h>
18 #include <linux/platform_device.h>
19 #include <video/sh_mobile_meram.h>
20 
21 /* meram registers */
22 #define MEVCR1			0x4
23 #define MEVCR1_RST		(1 << 31)
24 #define MEVCR1_WD		(1 << 30)
25 #define MEVCR1_AMD1		(1 << 29)
26 #define MEVCR1_AMD0		(1 << 28)
27 #define MEQSEL1			0x40
28 #define MEQSEL2			0x44
29 
30 #define MExxCTL			0x400
31 #define MExxCTL_BV		(1 << 31)
32 #define MExxCTL_BSZ_SHIFT	28
33 #define MExxCTL_MSAR_MASK	(0x7ff << MExxCTL_MSAR_SHIFT)
34 #define MExxCTL_MSAR_SHIFT	16
35 #define MExxCTL_NXT_MASK	(0x1f << MExxCTL_NXT_SHIFT)
36 #define MExxCTL_NXT_SHIFT	11
37 #define MExxCTL_WD1		(1 << 10)
38 #define MExxCTL_WD0		(1 << 9)
39 #define MExxCTL_WS		(1 << 8)
40 #define MExxCTL_CB		(1 << 7)
41 #define MExxCTL_WBF		(1 << 6)
42 #define MExxCTL_WF		(1 << 5)
43 #define MExxCTL_RF		(1 << 4)
44 #define MExxCTL_CM		(1 << 3)
45 #define MExxCTL_MD_READ		(1 << 0)
46 #define MExxCTL_MD_WRITE	(2 << 0)
47 #define MExxCTL_MD_ICB_WB	(3 << 0)
48 #define MExxCTL_MD_ICB		(4 << 0)
49 #define MExxCTL_MD_FB		(7 << 0)
50 #define MExxCTL_MD_MASK		(7 << 0)
51 #define MExxBSIZE		0x404
52 #define MExxBSIZE_RCNT_SHIFT	28
53 #define MExxBSIZE_YSZM1_SHIFT	16
54 #define MExxBSIZE_XSZM1_SHIFT	0
55 #define MExxMNCF		0x408
56 #define MExxMNCF_KWBNM_SHIFT	28
57 #define MExxMNCF_KRBNM_SHIFT	24
58 #define MExxMNCF_BNM_SHIFT	16
59 #define MExxMNCF_XBV		(1 << 15)
60 #define MExxMNCF_CPL_YCBCR444	(1 << 12)
61 #define MExxMNCF_CPL_YCBCR420	(2 << 12)
62 #define MExxMNCF_CPL_YCBCR422	(3 << 12)
63 #define MExxMNCF_CPL_MSK	(3 << 12)
64 #define MExxMNCF_BL		(1 << 2)
65 #define MExxMNCF_LNM_SHIFT	0
66 #define MExxSARA		0x410
67 #define MExxSARB		0x414
68 #define MExxSBSIZE		0x418
69 #define MExxSBSIZE_HDV		(1 << 31)
70 #define MExxSBSIZE_HSZ16	(0 << 28)
71 #define MExxSBSIZE_HSZ32	(1 << 28)
72 #define MExxSBSIZE_HSZ64	(2 << 28)
73 #define MExxSBSIZE_HSZ128	(3 << 28)
74 #define MExxSBSIZE_SBSIZZ_SHIFT	0
75 
76 #define MERAM_MExxCTL_VAL(next, addr)	\
77 	((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \
78 	 (((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK))
79 #define	MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
80 	(((rcnt) << MExxBSIZE_RCNT_SHIFT) | \
81 	 ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
82 	 ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
83 
84 #define SH_MOBILE_MERAM_ICB_NUM		32
85 
86 static unsigned long common_regs[] = {
87 	MEVCR1,
88 	MEQSEL1,
89 	MEQSEL2,
90 };
91 #define CMN_REGS_SIZE ARRAY_SIZE(common_regs)
92 
93 static unsigned long icb_regs[] = {
94 	MExxCTL,
95 	MExxBSIZE,
96 	MExxMNCF,
97 	MExxSARA,
98 	MExxSARB,
99 	MExxSBSIZE,
100 };
101 #define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
102 
103 struct sh_mobile_meram_priv {
104 	void __iomem	*base;
105 	struct mutex	lock;
106 	unsigned long	used_icb;
107 	int		used_meram_cache_regions;
108 	unsigned long	used_meram_cache[SH_MOBILE_MERAM_ICB_NUM];
109 	unsigned long	cmn_saved_regs[CMN_REGS_SIZE];
110 	unsigned long	icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM];
111 };
112 
113 /* settings */
114 #define MERAM_SEC_LINE 15
115 #define MERAM_LINE_WIDTH 2048
116 
117 /*
118  * MERAM/ICB access functions
119  */
120 
121 #define MERAM_ICB_OFFSET(base, idx, off)	((base) + (off) + (idx) * 0x20)
122 
meram_write_icb(void __iomem * base,int idx,int off,unsigned long val)123 static inline void meram_write_icb(void __iomem *base, int idx, int off,
124 	unsigned long val)
125 {
126 	iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
127 }
128 
meram_read_icb(void __iomem * base,int idx,int off)129 static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off)
130 {
131 	return ioread32(MERAM_ICB_OFFSET(base, idx, off));
132 }
133 
meram_write_reg(void __iomem * base,int off,unsigned long val)134 static inline void meram_write_reg(void __iomem *base, int off,
135 		unsigned long val)
136 {
137 	iowrite32(val, base + off);
138 }
139 
meram_read_reg(void __iomem * base,int off)140 static inline unsigned long meram_read_reg(void __iomem *base, int off)
141 {
142 	return ioread32(base + off);
143 }
144 
145 /*
146  * register ICB
147  */
148 
149 #define MERAM_CACHE_START(p)	 ((p) >> 16)
150 #define MERAM_CACHE_END(p)	 ((p) & 0xffff)
151 #define MERAM_CACHE_SET(o, s)	 ((((o) & 0xffff) << 16) | \
152 				  (((o) + (s) - 1) & 0xffff))
153 
154 /*
155  * check if there's no overlaps in MERAM allocation.
156  */
157 
meram_check_overlap(struct sh_mobile_meram_priv * priv,struct sh_mobile_meram_icb * new)158 static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
159 				      struct sh_mobile_meram_icb *new)
160 {
161 	int i;
162 	int used_start, used_end, meram_start, meram_end;
163 
164 	/* valid ICB? */
165 	if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f)
166 		return 1;
167 
168 	if (test_bit(new->marker_icb, &priv->used_icb) ||
169 			test_bit(new->cache_icb,  &priv->used_icb))
170 		return  1;
171 
172 	for (i = 0; i < priv->used_meram_cache_regions; i++) {
173 		used_start = MERAM_CACHE_START(priv->used_meram_cache[i]);
174 		used_end   = MERAM_CACHE_END(priv->used_meram_cache[i]);
175 		meram_start = new->meram_offset;
176 		meram_end   = new->meram_offset + new->meram_size;
177 
178 		if ((meram_start >= used_start && meram_start < used_end) ||
179 			(meram_end > used_start && meram_end < used_end))
180 			return 1;
181 	}
182 
183 	return 0;
184 }
185 
186 /*
187  * mark the specified ICB as used
188  */
189 
meram_mark(struct sh_mobile_meram_priv * priv,struct sh_mobile_meram_icb * new)190 static inline void meram_mark(struct sh_mobile_meram_priv *priv,
191 			      struct sh_mobile_meram_icb *new)
192 {
193 	int n;
194 
195 	if (new->marker_icb < 0 || new->cache_icb < 0)
196 		return;
197 
198 	__set_bit(new->marker_icb, &priv->used_icb);
199 	__set_bit(new->cache_icb, &priv->used_icb);
200 
201 	n = priv->used_meram_cache_regions;
202 
203 	priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset,
204 						    new->meram_size);
205 
206 	priv->used_meram_cache_regions++;
207 }
208 
209 /*
210  * unmark the specified ICB as used
211  */
212 
meram_unmark(struct sh_mobile_meram_priv * priv,struct sh_mobile_meram_icb * icb)213 static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
214 				struct sh_mobile_meram_icb *icb)
215 {
216 	int i;
217 	unsigned long pattern;
218 
219 	if (icb->marker_icb < 0 || icb->cache_icb < 0)
220 		return;
221 
222 	__clear_bit(icb->marker_icb, &priv->used_icb);
223 	__clear_bit(icb->cache_icb, &priv->used_icb);
224 
225 	pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size);
226 	for (i = 0; i < priv->used_meram_cache_regions; i++) {
227 		if (priv->used_meram_cache[i] == pattern) {
228 			while (i < priv->used_meram_cache_regions - 1) {
229 				priv->used_meram_cache[i] =
230 					priv->used_meram_cache[i + 1] ;
231 				i++;
232 			}
233 			priv->used_meram_cache[i] = 0;
234 			priv->used_meram_cache_regions--;
235 			break;
236 		}
237 	}
238 }
239 
240 /*
241  * is this a YCbCr(NV12, NV16 or NV24) colorspace
242  */
is_nvcolor(int cspace)243 static inline int is_nvcolor(int cspace)
244 {
245 	if (cspace == SH_MOBILE_MERAM_PF_NV ||
246 			cspace == SH_MOBILE_MERAM_PF_NV24)
247 		return 1;
248 	return 0;
249 }
250 
251 /*
252  * set the next address to fetch
253  */
meram_set_next_addr(struct sh_mobile_meram_priv * priv,struct sh_mobile_meram_cfg * cfg,unsigned long base_addr_y,unsigned long base_addr_c)254 static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
255 				       struct sh_mobile_meram_cfg *cfg,
256 				       unsigned long base_addr_y,
257 				       unsigned long base_addr_c)
258 {
259 	unsigned long target;
260 
261 	target = (cfg->current_reg) ? MExxSARA : MExxSARB;
262 	cfg->current_reg ^= 1;
263 
264 	/* set the next address to fetch */
265 	meram_write_icb(priv->base, cfg->icb[0].cache_icb,  target,
266 			base_addr_y);
267 	meram_write_icb(priv->base, cfg->icb[0].marker_icb, target,
268 			base_addr_y + cfg->icb[0].cache_unit);
269 
270 	if (is_nvcolor(cfg->pixelformat)) {
271 		meram_write_icb(priv->base, cfg->icb[1].cache_icb,  target,
272 				base_addr_c);
273 		meram_write_icb(priv->base, cfg->icb[1].marker_icb, target,
274 				base_addr_c + cfg->icb[1].cache_unit);
275 	}
276 }
277 
278 /*
279  * get the next ICB address
280  */
meram_get_next_icb_addr(struct sh_mobile_meram_info * pdata,struct sh_mobile_meram_cfg * cfg,unsigned long * icb_addr_y,unsigned long * icb_addr_c)281 static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
282 					   struct sh_mobile_meram_cfg *cfg,
283 					   unsigned long *icb_addr_y,
284 					   unsigned long *icb_addr_c)
285 {
286 	unsigned long icb_offset;
287 
288 	if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
289 		icb_offset = 0x80000000 | (cfg->current_reg << 29);
290 	else
291 		icb_offset = 0xc0000000 | (cfg->current_reg << 23);
292 
293 	*icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24);
294 	if (is_nvcolor(cfg->pixelformat))
295 		*icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);
296 }
297 
298 #define MERAM_CALC_BYTECOUNT(x, y) \
299 	(((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
300 
301 /*
302  * initialize MERAM
303  */
304 
meram_init(struct sh_mobile_meram_priv * priv,struct sh_mobile_meram_icb * icb,int xres,int yres,int * out_pitch)305 static int meram_init(struct sh_mobile_meram_priv *priv,
306 		      struct sh_mobile_meram_icb *icb,
307 		      int xres, int yres, int *out_pitch)
308 {
309 	unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
310 	unsigned long bnm;
311 	int lcdc_pitch, xpitch, line_cnt;
312 	int save_lines;
313 
314 	/* adjust pitch to 1024, 2048, 4096 or 8192 */
315 	lcdc_pitch = (xres - 1) | 1023;
316 	lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 1);
317 	lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 2);
318 	lcdc_pitch += 1;
319 
320 	/* derive settings */
321 	if (lcdc_pitch == 8192 && yres >= 1024) {
322 		lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
323 		line_cnt = total_byte_count >> 11;
324 		*out_pitch = xres;
325 		save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE);
326 		save_lines *= MERAM_SEC_LINE;
327 	} else {
328 		xpitch = xres;
329 		line_cnt = yres;
330 		*out_pitch = lcdc_pitch;
331 		save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2;
332 		save_lines &= 0xff;
333 	}
334 	bnm = (save_lines - 1) << 16;
335 
336 	/* TODO: we better to check if we have enough MERAM buffer size */
337 
338 	/* set up ICB */
339 	meram_write_icb(priv->base, icb->cache_icb,  MExxBSIZE,
340 			MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
341 	meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE,
342 			MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
343 
344 	meram_write_icb(priv->base, icb->cache_icb,  MExxMNCF, bnm);
345 	meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm);
346 
347 	meram_write_icb(priv->base, icb->cache_icb,  MExxSBSIZE, xpitch);
348 	meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch);
349 
350 	/* save a cache unit size */
351 	icb->cache_unit = xres * save_lines;
352 
353 	/*
354 	 * Set MERAM for framebuffer
355 	 *
356 	 * we also chain the cache_icb and the marker_icb.
357 	 * we also split the allocated MERAM buffer between two ICBs.
358 	 */
359 	meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
360 			MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) |
361 			MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
362 			MExxCTL_MD_FB);
363 	meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
364 			MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset +
365 					  icb->meram_size / 2) |
366 			MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
367 			MExxCTL_MD_FB);
368 
369 	return 0;
370 }
371 
meram_deinit(struct sh_mobile_meram_priv * priv,struct sh_mobile_meram_icb * icb)372 static void meram_deinit(struct sh_mobile_meram_priv *priv,
373 			struct sh_mobile_meram_icb *icb)
374 {
375 	/* disable ICB */
376 	meram_write_icb(priv->base, icb->cache_icb,  MExxCTL,
377 			MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
378 	meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
379 			MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
380 	icb->cache_unit = 0;
381 }
382 
383 /*
384  * register the ICB
385  */
386 
sh_mobile_meram_register(struct sh_mobile_meram_info * pdata,struct sh_mobile_meram_cfg * cfg,int xres,int yres,int pixelformat,unsigned long base_addr_y,unsigned long base_addr_c,unsigned long * icb_addr_y,unsigned long * icb_addr_c,int * pitch)387 static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
388 				    struct sh_mobile_meram_cfg *cfg,
389 				    int xres, int yres, int pixelformat,
390 				    unsigned long base_addr_y,
391 				    unsigned long base_addr_c,
392 				    unsigned long *icb_addr_y,
393 				    unsigned long *icb_addr_c,
394 				    int *pitch)
395 {
396 	struct platform_device *pdev;
397 	struct sh_mobile_meram_priv *priv;
398 	int n, out_pitch;
399 	int error = 0;
400 
401 	if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
402 		return -EINVAL;
403 
404 	if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
405 	    pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
406 	    pixelformat != SH_MOBILE_MERAM_PF_RGB)
407 		return -EINVAL;
408 
409 	priv = pdata->priv;
410 	pdev = pdata->pdev;
411 
412 	dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)",
413 		xres, yres, (!pixelformat) ? "yuv" : "rgb",
414 		base_addr_y, base_addr_c);
415 
416 	/* we can't handle wider than 8192px */
417 	if (xres > 8192) {
418 		dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
419 		return -EINVAL;
420 	}
421 
422 	/* do we have at least one ICB config? */
423 	if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
424 		dev_err(&pdev->dev, "at least one ICB is required.");
425 		return -EINVAL;
426 	}
427 
428 	mutex_lock(&priv->lock);
429 
430 	if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) {
431 		dev_err(&pdev->dev, "no more ICB available.");
432 		error = -EINVAL;
433 		goto err;
434 	}
435 
436 	/* make sure that there's no overlaps */
437 	if (meram_check_overlap(priv, &cfg->icb[0])) {
438 		dev_err(&pdev->dev, "conflicting config detected.");
439 		error = -EINVAL;
440 		goto err;
441 	}
442 	n = 1;
443 
444 	/* do the same if we have the second ICB set */
445 	if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
446 		if (meram_check_overlap(priv, &cfg->icb[1])) {
447 			dev_err(&pdev->dev, "conflicting config detected.");
448 			error = -EINVAL;
449 			goto err;
450 		}
451 		n = 2;
452 	}
453 
454 	if (is_nvcolor(pixelformat) && n != 2) {
455 		dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
456 		error =  -EINVAL;
457 		goto err;
458 	}
459 
460 	/* we now register the ICB */
461 	cfg->pixelformat = pixelformat;
462 	meram_mark(priv, &cfg->icb[0]);
463 	if (is_nvcolor(pixelformat))
464 		meram_mark(priv, &cfg->icb[1]);
465 
466 	/* initialize MERAM */
467 	meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
468 	*pitch = out_pitch;
469 	if (pixelformat == SH_MOBILE_MERAM_PF_NV)
470 		meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2,
471 			&out_pitch);
472 	else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
473 		meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2,
474 			&out_pitch);
475 
476 	cfg->current_reg = 1;
477 	meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
478 	meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
479 
480 	dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
481 		*icb_addr_y, *icb_addr_c);
482 
483 err:
484 	mutex_unlock(&priv->lock);
485 	return error;
486 }
487 
sh_mobile_meram_unregister(struct sh_mobile_meram_info * pdata,struct sh_mobile_meram_cfg * cfg)488 static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
489 				      struct sh_mobile_meram_cfg *cfg)
490 {
491 	struct sh_mobile_meram_priv *priv;
492 
493 	if (!pdata || !pdata->priv || !cfg)
494 		return -EINVAL;
495 
496 	priv = pdata->priv;
497 
498 	mutex_lock(&priv->lock);
499 
500 	/* deinit & unmark */
501 	if (is_nvcolor(cfg->pixelformat)) {
502 		meram_deinit(priv, &cfg->icb[1]);
503 		meram_unmark(priv, &cfg->icb[1]);
504 	}
505 	meram_deinit(priv, &cfg->icb[0]);
506 	meram_unmark(priv, &cfg->icb[0]);
507 
508 	mutex_unlock(&priv->lock);
509 
510 	return 0;
511 }
512 
sh_mobile_meram_update(struct sh_mobile_meram_info * pdata,struct sh_mobile_meram_cfg * cfg,unsigned long base_addr_y,unsigned long base_addr_c,unsigned long * icb_addr_y,unsigned long * icb_addr_c)513 static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
514 				  struct sh_mobile_meram_cfg *cfg,
515 				  unsigned long base_addr_y,
516 				  unsigned long base_addr_c,
517 				  unsigned long *icb_addr_y,
518 				  unsigned long *icb_addr_c)
519 {
520 	struct sh_mobile_meram_priv *priv;
521 
522 	if (!pdata || !pdata->priv || !cfg)
523 		return -EINVAL;
524 
525 	priv = pdata->priv;
526 
527 	mutex_lock(&priv->lock);
528 
529 	meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
530 	meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
531 
532 	mutex_unlock(&priv->lock);
533 
534 	return 0;
535 }
536 
sh_mobile_meram_runtime_suspend(struct device * dev)537 static int sh_mobile_meram_runtime_suspend(struct device *dev)
538 {
539 	struct platform_device *pdev = to_platform_device(dev);
540 	struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
541 	int k, j;
542 
543 	for (k = 0; k < CMN_REGS_SIZE; k++)
544 		priv->cmn_saved_regs[k] = meram_read_reg(priv->base,
545 			common_regs[k]);
546 
547 	for (j = 0; j < 32; j++) {
548 		if (!test_bit(j, &priv->used_icb))
549 			continue;
550 		for (k = 0; k < ICB_REGS_SIZE; k++) {
551 			priv->icb_saved_regs[j * ICB_REGS_SIZE + k] =
552 				meram_read_icb(priv->base, j, icb_regs[k]);
553 			/* Reset ICB on resume */
554 			if (icb_regs[k] == MExxCTL)
555 				priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |=
556 					MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
557 		}
558 	}
559 	return 0;
560 }
561 
sh_mobile_meram_runtime_resume(struct device * dev)562 static int sh_mobile_meram_runtime_resume(struct device *dev)
563 {
564 	struct platform_device *pdev = to_platform_device(dev);
565 	struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
566 	int k, j;
567 
568 	for (j = 0; j < 32; j++) {
569 		if (!test_bit(j, &priv->used_icb))
570 			continue;
571 		for (k = 0; k < ICB_REGS_SIZE; k++) {
572 			meram_write_icb(priv->base, j, icb_regs[k],
573 			priv->icb_saved_regs[j * ICB_REGS_SIZE + k]);
574 		}
575 	}
576 
577 	for (k = 0; k < CMN_REGS_SIZE; k++)
578 		meram_write_reg(priv->base, common_regs[k],
579 			priv->cmn_saved_regs[k]);
580 	return 0;
581 }
582 
583 static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = {
584 	.runtime_suspend = sh_mobile_meram_runtime_suspend,
585 	.runtime_resume = sh_mobile_meram_runtime_resume,
586 };
587 
588 static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
589 	.module			= THIS_MODULE,
590 	.meram_register		= sh_mobile_meram_register,
591 	.meram_unregister	= sh_mobile_meram_unregister,
592 	.meram_update		= sh_mobile_meram_update,
593 };
594 
595 /*
596  * initialize MERAM
597  */
598 
599 static int sh_mobile_meram_remove(struct platform_device *pdev);
600 
sh_mobile_meram_probe(struct platform_device * pdev)601 static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
602 {
603 	struct sh_mobile_meram_priv *priv;
604 	struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
605 	struct resource *res;
606 	int error;
607 
608 	if (!pdata) {
609 		dev_err(&pdev->dev, "no platform data defined\n");
610 		return -EINVAL;
611 	}
612 
613 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
614 	if (!res) {
615 		dev_err(&pdev->dev, "cannot get platform resources\n");
616 		return -ENOENT;
617 	}
618 
619 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
620 	if (!priv) {
621 		dev_err(&pdev->dev, "cannot allocate device data\n");
622 		return -ENOMEM;
623 	}
624 
625 	platform_set_drvdata(pdev, priv);
626 
627 	/* initialize private data */
628 	mutex_init(&priv->lock);
629 	priv->base = ioremap_nocache(res->start, resource_size(res));
630 	if (!priv->base) {
631 		dev_err(&pdev->dev, "ioremap failed\n");
632 		error = -EFAULT;
633 		goto err;
634 	}
635 	pdata->ops = &sh_mobile_meram_ops;
636 	pdata->priv = priv;
637 	pdata->pdev = pdev;
638 
639 	/* initialize ICB addressing mode */
640 	if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
641 		meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
642 
643 	pm_runtime_enable(&pdev->dev);
644 
645 	dev_info(&pdev->dev, "sh_mobile_meram initialized.");
646 
647 	return 0;
648 
649 err:
650 	sh_mobile_meram_remove(pdev);
651 
652 	return error;
653 }
654 
655 
sh_mobile_meram_remove(struct platform_device * pdev)656 static int sh_mobile_meram_remove(struct platform_device *pdev)
657 {
658 	struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
659 
660 	pm_runtime_disable(&pdev->dev);
661 
662 	if (priv->base)
663 		iounmap(priv->base);
664 
665 	mutex_destroy(&priv->lock);
666 
667 	kfree(priv);
668 
669 	return 0;
670 }
671 
672 static struct platform_driver sh_mobile_meram_driver = {
673 	.driver	= {
674 		.name		= "sh_mobile_meram",
675 		.owner		= THIS_MODULE,
676 		.pm		= &sh_mobile_meram_dev_pm_ops,
677 	},
678 	.probe		= sh_mobile_meram_probe,
679 	.remove		= sh_mobile_meram_remove,
680 };
681 
682 module_platform_driver(sh_mobile_meram_driver);
683 
684 MODULE_DESCRIPTION("SuperH Mobile MERAM driver");
685 MODULE_AUTHOR("Damian Hobson-Garcia / Takanari Hayama");
686 MODULE_LICENSE("GPL v2");
687