FSMC 驱动解析

FSMC是STA2x65的一个控制模块,全名Flexible Static Memory Controller (FSMC)

The Flexible Static Controller (FSMC) supports, with two chip selects
● ROM
● Static RAM
● NOR type flash memories, not multiplexed
● NOR type flash memories, multiplexed
It also supports, with two additional separate chip selects:
● NAND type flash memories, SLC small or large page
● NAND type flash memories, MLC
For NAND type of memories, the FSMC has been enhanced to implement an error correction in
hardware, based on the Bose-Chaudhuri-Hocquenghem (BCH) code, able to correct up to 8-bit
over 512 bytes+syndrome. The BCH code will calculate, in hardware, the syndrome only. The
actual correction will be implemented through S/W intervention.

FSMC 相关的驱动代码在 arch/arm/mach-cartesio/fsmc.c 中。

1. init/exit:

static int __init cartesio_fsmc_init(void)
{
        return amba_driver_register(&cartesio_fsmc_driver);
}

static void __exit cartesio_fsmc_exit(void)
{
        amba_driver_unregister(&cartesio_fsmc_driver);
}

arch_initcall(cartesio_fsmc_init);
module_exit(cartesio_fsmc_exit);

MODULE_AUTHOR("STMicroelectronics");
MODULE_DESCRIPTION("Flexible Static Memory Controller driver");
MODULE_LICENSE("GPL");

static struct amba_driver cartesio_fsmc_driver = {
        .drv = {
                .name   = "fsmc",
                .owner  = THIS_MODULE,
        },
        .probe          = cartesio_fsmc_probe,
        .remove         = cartesio_fsmc_remove,
        .suspend        = cartesio_fsmc_suspend,
        .resume         = cartesio_fsmc_resume,
        .id_table       = cartesio_fsmc_ids,
};
static struct amba_id cartesio_fsmc_ids[] = {
        {
                .id     = 0x00080090,
                .mask   = 0x000FFFFF,
        },
        { 0, 0 },
};
amba_driver:

struct amba_driver {
        struct device_driver    drv;
        int                     (*probe)(struct amba_device *, struct amba_id *);
        int                     (*remove)(struct amba_device *);
        void                    (*shutdown)(struct amba_device *);
        int                     (*suspend)(struct amba_device *, pm_message_t);
        int                     (*resume)(struct amba_device *);
        struct amba_id          *id_table;
};
amba_id:

struct amba_id {
        unsigned int            id;
        unsigned int            mask;
        void                    *data;
};
2. cartesio_fsmc_probe():

static int cartesio_fsmc_probe(struct amba_device *dev, struct amba_id *id)
{
        struct fsmc_platform_data *plat = dev->dev.platform_data;
        struct cartesio_fsmc_info *info;
        int i, ret;

        /* must have platform data */
        if (!plat) {
                dev_err(&dev->dev, "missing platform data\n");
                return -EINVAL;
        }

        ret = amba_request_regions(dev, NULL);
        if (ret < 0)
                return -EBUSY;

        info = kzalloc(sizeof(struct cartesio_fsmc_info), GFP_KERNEL);
        if (info == NULL) {
                dev_err(&dev->dev, "%s, not enough memory\n", __func__);
                amba_release_regions(dev);
                return -ENOMEM;
        }

        amba_set_drvdata(dev, info);

        /* initialize fsmc_info */
        info->base = ioremap(dev->res.start, dev->res.end - dev->res.start + 1);

        for (i = 0; i < FSMC_BANK_NUM; i++) {
                writel(plat->bank[i].control, info->base + FSMC_BCR(i));
                writel(plat->bank[i].timing,  info->base + FSMC_BTR(i));
        }

        for (i = 0; i < FSMC_DEVICE_NUM; i++) {
                writel(plat->device[i].control,   info->base + FSMC_PCR(i));
                writel(plat->device[i].cm_timing, info->base + FSMC_PMEM(i));
                writel(plat->device[i].am_timing, info->base + FSMC_PATT(i));
                writel(plat->device[i].io_timing, info->base + FSMC_PIO(i));
        }

        dev_info(&dev->dev, "started at 0x%p\n", info->base);
        return 0;
}
fsmc_platform_data:

/*
 * Interface for FSMC driver configuration
 */
struct fsmc_platform_data {
        struct bank_cfg bank[FSMC_BANK_NUM];
        struct device_cfg device[FSMC_DEVICE_NUM];
};
/*
 * Number of SRAM/NOR-Flash banks supported by FSMC
 */
#define FSMC_BANK_NUM           3

/*
 * Number of PC-Card/NAND-Flash devices supported by FSMC
 */
#define FSMC_DEVICE_NUM         2
bank_cfg:

/*
 * SRAM/NOR-Flash bank configuration
 */
struct bank_cfg {
        u32 control;
        u32 timing;
};
device_cfg:

/*
 * PC-Card/NAND-Flash device configuration
 */
struct device_cfg {
        u32 control;
        u32 cm_timing;  /* Common Memory Space Timing    */
        u32 am_timing;  /* Attribute Memory Space Timing */
        u32 io_timing;  /* I/O Space Timing              */
};

cartesio_fsmc_info:

/*
 * FSMC driver context
 */
struct cartesio_fsmc_info {
        void __iomem *base;

        struct bank_cfg bank[FSMC_BANK_NUM];
        struct device_cfg device[FSMC_DEVICE_NUM];
};
3. cartesio_fsmc_remove:

static int cartesio_fsmc_remove(struct amba_device *dev)
{
        struct cartesio_fsmc_info *info;

        info = amba_get_drvdata(dev);

        iounmap(info->base);
        amba_release_regions(dev);
        kfree(info);

        dev_info(&dev->dev, "module stopped and unloaded\n");
        return 0;
}
4. PM: suspend/resume:

#ifdef CONFIG_PM
static int cartesio_fsmc_suspend(struct amba_device *dev, pm_message_t state)
{
        struct cartesio_fsmc_info *info;
        int i;

        info = amba_get_drvdata(dev);

        for (i = 0; i < FSMC_BANK_NUM; i++) {
                info->bank[i].control = readl(info->base + FSMC_BCR(i));
                info->bank[i].timing  = readl(info->base + FSMC_BTR(i));
        }

        for (i = 0; i < FSMC_DEVICE_NUM; i++) {
                info->device[i].control   = readl(info->base + FSMC_PCR(i));
                info->device[i].cm_timing = readl(info->base + FSMC_PMEM(i));
                info->device[i].am_timing = readl(info->base + FSMC_PATT(i));
                info->device[i].io_timing = readl(info->base + FSMC_PIO(i));
        }

        return 0;
}

static int cartesio_fsmc_resume(struct amba_device *dev)
{
        struct cartesio_fsmc_info *info;
        int i;

        info = amba_get_drvdata(dev);

        for (i = 0; i < FSMC_BANK_NUM; i++) {
                writel(info->bank[i].control, info->base + FSMC_BCR(i));
                writel(info->bank[i].timing , info->base + FSMC_BTR(i));
        }

        for (i = 0; i < FSMC_DEVICE_NUM; i++) {
                writel(info->device[i].control,   info->base + FSMC_PCR(i));
                writel(info->device[i].cm_timing, info->base + FSMC_PMEM(i));
                writel(info->device[i].am_timing, info->base + FSMC_PATT(i));
                writel(info->device[i].io_timing, info->base + FSMC_PIO(i));
        }

        return 0;
}
#else
#define cartesio_fsmc_suspend   NULL
#define cartesio_fsmc_resume    NULL
#endif
5. other functions:

fsmc_device_enable:

void fsmc_device_enable(struct device *dev, int device)
{
        struct cartesio_fsmc_info *info;
        struct amba_device *adev;
        u32 val;

        if (device >= FSMC_DEVICE_NUM) {
                dev_err(dev, "device ID is out of range: %d\n", device);
                return;
        }

        adev = container_of(dev, struct amba_device, dev);
        info = amba_get_drvdata(adev);

        val = readl(info->base + FSMC_PCR(device));
        val |= FSMC_PBKEN;
        writel(val, info->base + FSMC_PCR(device));
}
EXPORT_SYMBOL(fsmc_device_enable);
fsmc_set_ecc:

void fsmc_set_ecc(struct device *dev, int device, enum ecc_state state)
{
#ifdef CONFIG_BCH_8
        struct cartesio_fsmc_info *info;
        struct amba_device *adev;
        u32 val;

        if (device >= FSMC_DEVICE_NUM) {
                dev_err(dev, "device ID is out of range: %d\n", device);
                return;
        }

        adev = container_of(dev, struct amba_device, dev);
        info = amba_get_drvdata(adev);

        val = readl(info->base + FSMC_PCR(device));
        switch (state) {
        case ECC_PAGE_512:
                val &= ~FSMC_PECCP_256;
                break;
        case ECC_PAGE_256:
                val |= FSMC_PECCP_256;
                break;
        case ECC_RESET:
                /* disable hardware ECC and then re-enable to reset */
                val &= ~FSMC_PECCEN;
                writel(val, info->base + FSMC_PCR(device));
                break;
        }
        val |= FSMC_PECCEN;
        writel(val, info->base + FSMC_PCR(device));
#endif
}
EXPORT_SYMBOL(fsmc_set_ecc);
fsmc_get_parity:

int fsmc_get_parity(struct device *dev, int device, uint8_t *ecc_code)
{
        struct cartesio_fsmc_info *info;
        struct amba_device *adev;
        u32 val;

        if (device >= FSMC_DEVICE_NUM) {
                dev_err(dev, "device ID out of range: %d\n", device);
                return -ENODEV;
        }

        adev = container_of(dev, struct amba_device, dev);
        info = amba_get_drvdata(adev);

#ifdef CONFIG_BCH_8
        do {
                val = readl(info->base + FSMC_STATUS(device));
        } while (!(val & STATUS_ECCRDY));

        val = readl(info->base + FSMC_ECCR(device));
        ecc_code[0] = (val >>  0) & 0xFF;
        ecc_code[1] = (val >>  8) & 0xFF;
        ecc_code[2] = (val >> 16) & 0xFF;
        ecc_code[3] = (val >> 24) & 0xFF;

        val = readl(info->base + FSMC_ECC2R(device));
        ecc_code[4] = (val >>  0) & 0xFF;
        ecc_code[5] = (val >>  8) & 0xFF;
        ecc_code[6] = (val >> 16) & 0xFF;
        ecc_code[7] = (val >> 24) & 0xFF;

        val = readl(info->base + FSMC_ECC3R(device));
        ecc_code[8] = (val >>  0) & 0xFF;
        ecc_code[9] = (val >>  8) & 0xFF;
        ecc_code[10] = (val >> 16) & 0xFF;
        ecc_code[11] = (val >> 24) & 0xFF;

        val = readl(info->base + FSMC_STATUS(device));
        if (!(val & 0x4000)) {
                ecc_code[12] = (val >>  8) & 0xFF;
                ecc_code[13] = (val >> 16) & 0xFF;
        } else {
                ecc_code[12] = (val >> 16) & 0xFF;
                ecc_code[13] = 0x0;
        }
        val = readl(info->base + FSMC_STATUS(device));
        val &= ~0x8000;
        writel(val, info->base + FSMC_STATUS(device));
#else
        val = readl(info->base + FSMC_ECCR(device));
        ecc_code[0] = (val >>  0) & 0xFF;
        ecc_code[1] = (val >>  8) & 0xFF;
        ecc_code[2] = (val >> 16) & 0xFF;
#endif
        return 0;
}
EXPORT_SYMBOL(fsmc_get_parity);











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值