linux/drivers/video/sh_mobile_meram.c
<<
>>
Prefs
   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/device.h>
  13#include <linux/err.h>
  14#include <linux/export.h>
  15#include <linux/genalloc.h>
  16#include <linux/io.h>
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/platform_device.h>
  20#include <linux/pm_runtime.h>
  21#include <linux/slab.h>
  22
  23#include <video/sh_mobile_meram.h>
  24
  25/* -----------------------------------------------------------------------------
  26 * MERAM registers
  27 */
  28
  29#define MEVCR1                  0x4
  30#define MEVCR1_RST              (1 << 31)
  31#define MEVCR1_WD               (1 << 30)
  32#define MEVCR1_AMD1             (1 << 29)
  33#define MEVCR1_AMD0             (1 << 28)
  34#define MEQSEL1                 0x40
  35#define MEQSEL2                 0x44
  36
  37#define MExxCTL                 0x400
  38#define MExxCTL_BV              (1 << 31)
  39#define MExxCTL_BSZ_SHIFT       28
  40#define MExxCTL_MSAR_MASK       (0x7ff << MExxCTL_MSAR_SHIFT)
  41#define MExxCTL_MSAR_SHIFT      16
  42#define MExxCTL_NXT_MASK        (0x1f << MExxCTL_NXT_SHIFT)
  43#define MExxCTL_NXT_SHIFT       11
  44#define MExxCTL_WD1             (1 << 10)
  45#define MExxCTL_WD0             (1 << 9)
  46#define MExxCTL_WS              (1 << 8)
  47#define MExxCTL_CB              (1 << 7)
  48#define MExxCTL_WBF             (1 << 6)
  49#define MExxCTL_WF              (1 << 5)
  50#define MExxCTL_RF              (1 << 4)
  51#define MExxCTL_CM              (1 << 3)
  52#define MExxCTL_MD_READ         (1 << 0)
  53#define MExxCTL_MD_WRITE        (2 << 0)
  54#define MExxCTL_MD_ICB_WB       (3 << 0)
  55#define MExxCTL_MD_ICB          (4 << 0)
  56#define MExxCTL_MD_FB           (7 << 0)
  57#define MExxCTL_MD_MASK         (7 << 0)
  58#define MExxBSIZE               0x404
  59#define MExxBSIZE_RCNT_SHIFT    28
  60#define MExxBSIZE_YSZM1_SHIFT   16
  61#define MExxBSIZE_XSZM1_SHIFT   0
  62#define MExxMNCF                0x408
  63#define MExxMNCF_KWBNM_SHIFT    28
  64#define MExxMNCF_KRBNM_SHIFT    24
  65#define MExxMNCF_BNM_SHIFT      16
  66#define MExxMNCF_XBV            (1 << 15)
  67#define MExxMNCF_CPL_YCBCR444   (1 << 12)
  68#define MExxMNCF_CPL_YCBCR420   (2 << 12)
  69#define MExxMNCF_CPL_YCBCR422   (3 << 12)
  70#define MExxMNCF_CPL_MSK        (3 << 12)
  71#define MExxMNCF_BL             (1 << 2)
  72#define MExxMNCF_LNM_SHIFT      0
  73#define MExxSARA                0x410
  74#define MExxSARB                0x414
  75#define MExxSBSIZE              0x418
  76#define MExxSBSIZE_HDV          (1 << 31)
  77#define MExxSBSIZE_HSZ16        (0 << 28)
  78#define MExxSBSIZE_HSZ32        (1 << 28)
  79#define MExxSBSIZE_HSZ64        (2 << 28)
  80#define MExxSBSIZE_HSZ128       (3 << 28)
  81#define MExxSBSIZE_SBSIZZ_SHIFT 0
  82
  83#define MERAM_MExxCTL_VAL(next, addr)   \
  84        ((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \
  85         (((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK))
  86#define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
  87        (((rcnt) << MExxBSIZE_RCNT_SHIFT) | \
  88         ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
  89         ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
  90
  91static const unsigned long common_regs[] = {
  92        MEVCR1,
  93        MEQSEL1,
  94        MEQSEL2,
  95};
  96#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
  97
  98static const unsigned long icb_regs[] = {
  99        MExxCTL,
 100        MExxBSIZE,
 101        MExxMNCF,
 102        MExxSARA,
 103        MExxSARB,
 104        MExxSBSIZE,
 105};
 106#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
 107
 108/*
 109 * sh_mobile_meram_icb - MERAM ICB information
 110 * @regs: Registers cache
 111 * @index: ICB index
 112 * @offset: MERAM block offset
 113 * @size: MERAM block size in KiB
 114 * @cache_unit: Bytes to cache per ICB
 115 * @pixelformat: Video pixel format of the data stored in the ICB
 116 * @current_reg: Which of Start Address Register A (0) or B (1) is in use
 117 */
 118struct sh_mobile_meram_icb {
 119        unsigned long regs[ICB_REGS_SIZE];
 120        unsigned int index;
 121        unsigned long offset;
 122        unsigned int size;
 123
 124        unsigned int cache_unit;
 125        unsigned int pixelformat;
 126        unsigned int current_reg;
 127};
 128
 129#define MERAM_ICB_NUM                   32
 130
 131struct sh_mobile_meram_fb_plane {
 132        struct sh_mobile_meram_icb *marker;
 133        struct sh_mobile_meram_icb *cache;
 134};
 135
 136struct sh_mobile_meram_fb_cache {
 137        unsigned int nplanes;
 138        struct sh_mobile_meram_fb_plane planes[2];
 139};
 140
 141/*
 142 * sh_mobile_meram_priv - MERAM device
 143 * @base: Registers base address
 144 * @meram: MERAM physical address
 145 * @regs: Registers cache
 146 * @lock: Protects used_icb and icbs
 147 * @used_icb: Bitmask of used ICBs
 148 * @icbs: ICBs
 149 * @pool: Allocation pool to manage the MERAM
 150 */
 151struct sh_mobile_meram_priv {
 152        void __iomem *base;
 153        unsigned long meram;
 154        unsigned long regs[MERAM_REGS_SIZE];
 155
 156        struct mutex lock;
 157        unsigned long used_icb;
 158        struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
 159
 160        struct gen_pool *pool;
 161};
 162
 163/* settings */
 164#define MERAM_GRANULARITY               1024
 165#define MERAM_SEC_LINE                  15
 166#define MERAM_LINE_WIDTH                2048
 167
 168/* -----------------------------------------------------------------------------
 169 * Registers access
 170 */
 171
 172#define MERAM_ICB_OFFSET(base, idx, off)        ((base) + (off) + (idx) * 0x20)
 173
 174static inline void meram_write_icb(void __iomem *base, unsigned int idx,
 175                                   unsigned int off, unsigned long val)
 176{
 177        iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
 178}
 179
 180static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
 181                                           unsigned int off)
 182{
 183        return ioread32(MERAM_ICB_OFFSET(base, idx, off));
 184}
 185
 186static inline void meram_write_reg(void __iomem *base, unsigned int off,
 187                                   unsigned long val)
 188{
 189        iowrite32(val, base + off);
 190}
 191
 192static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
 193{
 194        return ioread32(base + off);
 195}
 196
 197/* -----------------------------------------------------------------------------
 198 * MERAM allocation and free
 199 */
 200
 201static unsigned long meram_alloc(struct sh_mobile_meram_priv *priv, size_t size)
 202{
 203        return gen_pool_alloc(priv->pool, size);
 204}
 205
 206static void meram_free(struct sh_mobile_meram_priv *priv, unsigned long mem,
 207                       size_t size)
 208{
 209        gen_pool_free(priv->pool, mem, size);
 210}
 211
 212/* -----------------------------------------------------------------------------
 213 * LCDC cache planes allocation, init, cleanup and free
 214 */
 215
 216/* Allocate ICBs and MERAM for a plane. */
 217static int meram_plane_alloc(struct sh_mobile_meram_priv *priv,
 218                             struct sh_mobile_meram_fb_plane *plane,
 219                             size_t size)
 220{
 221        unsigned long mem;
 222        unsigned long idx;
 223
 224        idx = find_first_zero_bit(&priv->used_icb, 28);
 225        if (idx == 28)
 226                return -ENOMEM;
 227        plane->cache = &priv->icbs[idx];
 228
 229        idx = find_next_zero_bit(&priv->used_icb, 32, 28);
 230        if (idx == 32)
 231                return -ENOMEM;
 232        plane->marker = &priv->icbs[idx];
 233
 234        mem = meram_alloc(priv, size * 1024);
 235        if (mem == 0)
 236                return -ENOMEM;
 237
 238        __set_bit(plane->marker->index, &priv->used_icb);
 239        __set_bit(plane->cache->index, &priv->used_icb);
 240
 241        plane->marker->offset = mem - priv->meram;
 242        plane->marker->size = size;
 243
 244        return 0;
 245}
 246
 247/* Free ICBs and MERAM for a plane. */
 248static void meram_plane_free(struct sh_mobile_meram_priv *priv,
 249                             struct sh_mobile_meram_fb_plane *plane)
 250{
 251        meram_free(priv, priv->meram + plane->marker->offset,
 252                   plane->marker->size * 1024);
 253
 254        __clear_bit(plane->marker->index, &priv->used_icb);
 255        __clear_bit(plane->cache->index, &priv->used_icb);
 256}
 257
 258/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
 259static int is_nvcolor(int cspace)
 260{
 261        if (cspace == SH_MOBILE_MERAM_PF_NV ||
 262            cspace == SH_MOBILE_MERAM_PF_NV24)
 263                return 1;
 264        return 0;
 265}
 266
 267/* Set the next address to fetch. */
 268static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
 269                                struct sh_mobile_meram_fb_cache *cache,
 270                                unsigned long base_addr_y,
 271                                unsigned long base_addr_c)
 272{
 273        struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
 274        unsigned long target;
 275
 276        icb->current_reg ^= 1;
 277        target = icb->current_reg ? MExxSARB : MExxSARA;
 278
 279        /* set the next address to fetch */
 280        meram_write_icb(priv->base, cache->planes[0].cache->index, target,
 281                        base_addr_y);
 282        meram_write_icb(priv->base, cache->planes[0].marker->index, target,
 283                        base_addr_y + cache->planes[0].marker->cache_unit);
 284
 285        if (cache->nplanes == 2) {
 286                meram_write_icb(priv->base, cache->planes[1].cache->index,
 287                                target, base_addr_c);
 288                meram_write_icb(priv->base, cache->planes[1].marker->index,
 289                                target, base_addr_c +
 290                                cache->planes[1].marker->cache_unit);
 291        }
 292}
 293
 294/* Get the next ICB address. */
 295static void
 296meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
 297                        struct sh_mobile_meram_fb_cache *cache,
 298                        unsigned long *icb_addr_y, unsigned long *icb_addr_c)
 299{
 300        struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
 301        unsigned long icb_offset;
 302
 303        if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
 304                icb_offset = 0x80000000 | (icb->current_reg << 29);
 305        else
 306                icb_offset = 0xc0000000 | (icb->current_reg << 23);
 307
 308        *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
 309        if (cache->nplanes == 2)
 310                *icb_addr_c = icb_offset
 311                            | (cache->planes[1].marker->index << 24);
 312}
 313
 314#define MERAM_CALC_BYTECOUNT(x, y) \
 315        (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
 316
 317/* Initialize MERAM. */
 318static int meram_plane_init(struct sh_mobile_meram_priv *priv,
 319                            struct sh_mobile_meram_fb_plane *plane,
 320                            unsigned int xres, unsigned int yres,
 321                            unsigned int *out_pitch)
 322{
 323        struct sh_mobile_meram_icb *marker = plane->marker;
 324        unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
 325        unsigned long bnm;
 326        unsigned int lcdc_pitch;
 327        unsigned int xpitch;
 328        unsigned int line_cnt;
 329        unsigned int save_lines;
 330
 331        /* adjust pitch to 1024, 2048, 4096 or 8192 */
 332        lcdc_pitch = (xres - 1) | 1023;
 333        lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 1);
 334        lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 2);
 335        lcdc_pitch += 1;
 336
 337        /* derive settings */
 338        if (lcdc_pitch == 8192 && yres >= 1024) {
 339                lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
 340                line_cnt = total_byte_count >> 11;
 341                *out_pitch = xres;
 342                save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
 343                save_lines *= MERAM_SEC_LINE;
 344        } else {
 345                xpitch = xres;
 346                line_cnt = yres;
 347                *out_pitch = lcdc_pitch;
 348                save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
 349                save_lines &= 0xff;
 350        }
 351        bnm = (save_lines - 1) << 16;
 352
 353        /* TODO: we better to check if we have enough MERAM buffer size */
 354
 355        /* set up ICB */
 356        meram_write_icb(priv->base, plane->cache->index,  MExxBSIZE,
 357                        MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
 358        meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
 359                        MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
 360
 361        meram_write_icb(priv->base, plane->cache->index,  MExxMNCF, bnm);
 362        meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
 363
 364        meram_write_icb(priv->base, plane->cache->index,  MExxSBSIZE, xpitch);
 365        meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
 366
 367        /* save a cache unit size */
 368        plane->cache->cache_unit = xres * save_lines;
 369        plane->marker->cache_unit = xres * save_lines;
 370
 371        /*
 372         * Set MERAM for framebuffer
 373         *
 374         * we also chain the cache_icb and the marker_icb.
 375         * we also split the allocated MERAM buffer between two ICBs.
 376         */
 377        meram_write_icb(priv->base, plane->cache->index, MExxCTL,
 378                        MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
 379                        | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
 380                        MExxCTL_MD_FB);
 381        meram_write_icb(priv->base, plane->marker->index, MExxCTL,
 382                        MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
 383                                          plane->marker->size / 2) |
 384                        MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
 385                        MExxCTL_MD_FB);
 386
 387        return 0;
 388}
 389
 390static void meram_plane_cleanup(struct sh_mobile_meram_priv *priv,
 391                                struct sh_mobile_meram_fb_plane *plane)
 392{
 393        /* disable ICB */
 394        meram_write_icb(priv->base, plane->cache->index,  MExxCTL,
 395                        MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
 396        meram_write_icb(priv->base, plane->marker->index, MExxCTL,
 397                        MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
 398
 399        plane->cache->cache_unit = 0;
 400        plane->marker->cache_unit = 0;
 401}
 402
 403/* -----------------------------------------------------------------------------
 404 * MERAM operations
 405 */
 406
 407unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *pdata,
 408                                    size_t size)
 409{
 410        struct sh_mobile_meram_priv *priv = pdata->priv;
 411
 412        return meram_alloc(priv, size);
 413}
 414EXPORT_SYMBOL_GPL(sh_mobile_meram_alloc);
 415
 416void sh_mobile_meram_free(struct sh_mobile_meram_info *pdata, unsigned long mem,
 417                          size_t size)
 418{
 419        struct sh_mobile_meram_priv *priv = pdata->priv;
 420
 421        meram_free(priv, mem, size);
 422}
 423EXPORT_SYMBOL_GPL(sh_mobile_meram_free);
 424
 425/* Allocate memory for the ICBs and mark them as used. */
 426static struct sh_mobile_meram_fb_cache *
 427meram_cache_alloc(struct sh_mobile_meram_priv *priv,
 428                  const struct sh_mobile_meram_cfg *cfg,
 429                  int pixelformat)
 430{
 431        unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
 432        struct sh_mobile_meram_fb_cache *cache;
 433        int ret;
 434
 435        cache = kzalloc(sizeof(*cache), GFP_KERNEL);
 436        if (cache == NULL)
 437                return ERR_PTR(-ENOMEM);
 438
 439        cache->nplanes = nplanes;
 440
 441        ret = meram_plane_alloc(priv, &cache->planes[0],
 442                                cfg->icb[0].meram_size);
 443        if (ret < 0)
 444                goto error;
 445
 446        cache->planes[0].marker->current_reg = 1;
 447        cache->planes[0].marker->pixelformat = pixelformat;
 448
 449        if (cache->nplanes == 1)
 450                return cache;
 451
 452        ret = meram_plane_alloc(priv, &cache->planes[1],
 453                                cfg->icb[1].meram_size);
 454        if (ret < 0) {
 455                meram_plane_free(priv, &cache->planes[0]);
 456                goto error;
 457        }
 458
 459        return cache;
 460
 461error:
 462        kfree(cache);
 463        return ERR_PTR(-ENOMEM);
 464}
 465
 466void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *pdata,
 467                                  const struct sh_mobile_meram_cfg *cfg,
 468                                  unsigned int xres, unsigned int yres,
 469                                  unsigned int pixelformat, unsigned int *pitch)
 470{
 471        struct sh_mobile_meram_fb_cache *cache;
 472        struct sh_mobile_meram_priv *priv = pdata->priv;
 473        struct platform_device *pdev = pdata->pdev;
 474        unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
 475        unsigned int out_pitch;
 476
 477        if (priv == NULL)
 478                return ERR_PTR(-ENODEV);
 479
 480        if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
 481            pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
 482            pixelformat != SH_MOBILE_MERAM_PF_RGB)
 483                return ERR_PTR(-EINVAL);
 484
 485        dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
 486                !pixelformat ? "yuv" : "rgb");
 487
 488        /* we can't handle wider than 8192px */
 489        if (xres > 8192) {
 490                dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
 491                return ERR_PTR(-EINVAL);
 492        }
 493
 494        if (cfg->icb[0].meram_size == 0)
 495                return ERR_PTR(-EINVAL);
 496
 497        if (nplanes == 2 && cfg->icb[1].meram_size == 0)
 498                return ERR_PTR(-EINVAL);
 499
 500        mutex_lock(&priv->lock);
 501
 502        /* We now register the ICBs and allocate the MERAM regions. */
 503        cache = meram_cache_alloc(priv, cfg, pixelformat);
 504        if (IS_ERR(cache)) {
 505                dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
 506                        PTR_ERR(cache));
 507                goto err;
 508        }
 509
 510        /* initialize MERAM */
 511        meram_plane_init(priv, &cache->planes[0], xres, yres, &out_pitch);
 512        *pitch = out_pitch;
 513        if (pixelformat == SH_MOBILE_MERAM_PF_NV)
 514                meram_plane_init(priv, &cache->planes[1],
 515                                 xres, (yres + 1) / 2, &out_pitch);
 516        else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
 517                meram_plane_init(priv, &cache->planes[1],
 518                                 2 * xres, (yres + 1) / 2, &out_pitch);
 519
 520err:
 521        mutex_unlock(&priv->lock);
 522        return cache;
 523}
 524EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_alloc);
 525
 526void
 527sh_mobile_meram_cache_free(struct sh_mobile_meram_info *pdata, void *data)
 528{
 529        struct sh_mobile_meram_fb_cache *cache = data;
 530        struct sh_mobile_meram_priv *priv = pdata->priv;
 531
 532        mutex_lock(&priv->lock);
 533
 534        /* Cleanup and free. */
 535        meram_plane_cleanup(priv, &cache->planes[0]);
 536        meram_plane_free(priv, &cache->planes[0]);
 537
 538        if (cache->nplanes == 2) {
 539                meram_plane_cleanup(priv, &cache->planes[1]);
 540                meram_plane_free(priv, &cache->planes[1]);
 541        }
 542
 543        kfree(cache);
 544
 545        mutex_unlock(&priv->lock);
 546}
 547EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_free);
 548
 549void
 550sh_mobile_meram_cache_update(struct sh_mobile_meram_info *pdata, void *data,
 551                             unsigned long base_addr_y,
 552                             unsigned long base_addr_c,
 553                             unsigned long *icb_addr_y,
 554                             unsigned long *icb_addr_c)
 555{
 556        struct sh_mobile_meram_fb_cache *cache = data;
 557        struct sh_mobile_meram_priv *priv = pdata->priv;
 558
 559        mutex_lock(&priv->lock);
 560
 561        meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
 562        meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
 563
 564        mutex_unlock(&priv->lock);
 565}
 566EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_update);
 567
 568/* -----------------------------------------------------------------------------
 569 * Power management
 570 */
 571
 572static int sh_mobile_meram_suspend(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 < MERAM_REGS_SIZE; i++)
 579                priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
 580
 581        for (i = 0; i < 32; i++) {
 582                if (!test_bit(i, &priv->used_icb))
 583                        continue;
 584                for (j = 0; j < ICB_REGS_SIZE; j++) {
 585                        priv->icbs[i].regs[j] =
 586                                meram_read_icb(priv->base, i, icb_regs[j]);
 587                        /* Reset ICB on resume */
 588                        if (icb_regs[j] == MExxCTL)
 589                                priv->icbs[i].regs[j] |=
 590                                        MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
 591                }
 592        }
 593        return 0;
 594}
 595
 596static int sh_mobile_meram_resume(struct device *dev)
 597{
 598        struct platform_device *pdev = to_platform_device(dev);
 599        struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
 600        unsigned int i, j;
 601
 602        for (i = 0; i < 32; i++) {
 603                if (!test_bit(i, &priv->used_icb))
 604                        continue;
 605                for (j = 0; j < ICB_REGS_SIZE; j++)
 606                        meram_write_icb(priv->base, i, icb_regs[j],
 607                                        priv->icbs[i].regs[j]);
 608        }
 609
 610        for (i = 0; i < MERAM_REGS_SIZE; i++)
 611                meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
 612        return 0;
 613}
 614
 615static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops,
 616                            sh_mobile_meram_suspend,
 617                            sh_mobile_meram_resume, NULL);
 618
 619/* -----------------------------------------------------------------------------
 620 * Probe/remove and driver init/exit
 621 */
 622
 623static int sh_mobile_meram_probe(struct platform_device *pdev)
 624{
 625        struct sh_mobile_meram_priv *priv;
 626        struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
 627        struct resource *regs;
 628        struct resource *meram;
 629        unsigned int i;
 630        int error;
 631
 632        if (!pdata) {
 633                dev_err(&pdev->dev, "no platform data defined\n");
 634                return -EINVAL;
 635        }
 636
 637        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 638        meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 639        if (regs == NULL || meram == NULL) {
 640                dev_err(&pdev->dev, "cannot get platform resources\n");
 641                return -ENOENT;
 642        }
 643
 644        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 645        if (!priv) {
 646                dev_err(&pdev->dev, "cannot allocate device data\n");
 647                return -ENOMEM;
 648        }
 649
 650        /* Initialize private data. */
 651        mutex_init(&priv->lock);
 652        priv->used_icb = pdata->reserved_icbs;
 653
 654        for (i = 0; i < MERAM_ICB_NUM; ++i)
 655                priv->icbs[i].index = i;
 656
 657        pdata->priv = priv;
 658        pdata->pdev = pdev;
 659
 660        /* Request memory regions and remap the registers. */
 661        if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
 662                dev_err(&pdev->dev, "MERAM registers region already claimed\n");
 663                error = -EBUSY;
 664                goto err_req_regs;
 665        }
 666
 667        if (!request_mem_region(meram->start, resource_size(meram),
 668                                pdev->name)) {
 669                dev_err(&pdev->dev, "MERAM memory region already claimed\n");
 670                error = -EBUSY;
 671                goto err_req_meram;
 672        }
 673
 674        priv->base = ioremap_nocache(regs->start, resource_size(regs));
 675        if (!priv->base) {
 676                dev_err(&pdev->dev, "ioremap failed\n");
 677                error = -EFAULT;
 678                goto err_ioremap;
 679        }
 680
 681        priv->meram = meram->start;
 682
 683        /* Create and initialize the MERAM memory pool. */
 684        priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
 685        if (priv->pool == NULL) {
 686                error = -ENOMEM;
 687                goto err_genpool;
 688        }
 689
 690        error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
 691                             -1);
 692        if (error < 0)
 693                goto err_genpool;
 694
 695        /* initialize ICB addressing mode */
 696        if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
 697                meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
 698
 699        platform_set_drvdata(pdev, priv);
 700        pm_runtime_enable(&pdev->dev);
 701
 702        dev_info(&pdev->dev, "sh_mobile_meram initialized.");
 703
 704        return 0;
 705
 706err_genpool:
 707        if (priv->pool)
 708                gen_pool_destroy(priv->pool);
 709        iounmap(priv->base);
 710err_ioremap:
 711        release_mem_region(meram->start, resource_size(meram));
 712err_req_meram:
 713        release_mem_region(regs->start, resource_size(regs));
 714err_req_regs:
 715        mutex_destroy(&priv->lock);
 716        kfree(priv);
 717
 718        return error;
 719}
 720
 721
 722static int sh_mobile_meram_remove(struct platform_device *pdev)
 723{
 724        struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
 725        struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 726        struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 727
 728        pm_runtime_disable(&pdev->dev);
 729
 730        gen_pool_destroy(priv->pool);
 731
 732        iounmap(priv->base);
 733        release_mem_region(meram->start, resource_size(meram));
 734        release_mem_region(regs->start, resource_size(regs));
 735
 736        mutex_destroy(&priv->lock);
 737
 738        kfree(priv);
 739
 740        return 0;
 741}
 742
 743static struct platform_driver sh_mobile_meram_driver = {
 744        .driver = {
 745                .name           = "sh_mobile_meram",
 746                .owner          = THIS_MODULE,
 747                .pm             = &sh_mobile_meram_dev_pm_ops,
 748        },
 749        .probe          = sh_mobile_meram_probe,
 750        .remove         = sh_mobile_meram_remove,
 751};
 752
 753module_platform_driver(sh_mobile_meram_driver);
 754
 755MODULE_DESCRIPTION("SuperH Mobile MERAM driver");
 756MODULE_AUTHOR("Damian Hobson-Garcia / Takanari Hayama");
 757MODULE_LICENSE("GPL v2");
 758