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