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