[PATCH openbmc 3/6] linux-obmc: apply the aspeed-spi-nor controller patchset
Jeremy Kerr
jk at ozlabs.org
Thu Jan 21 16:40:52 AEDT 2016
Hi Milton,
> Thie applies the apseed-spi-nor driver and patches the dts for
> palmetto and barreleye when building the linux image. It also
> enables the config options for the driver and some file systems to
> support the overlay filesystem stack used by obmc-phosphor-initfs.
>
> It also applies the rtc month fix patch.
OK, these should be separate.
However, I'd prefer we apply the patches directly to our kernel repo.
Can you post a patch for that, then this change should just be an
updated kernel reference.
Some minor comments on the kernel patch though:
> +diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
> +new file mode 100644
> +index 0000000..2497648
> +--- /dev/null
> ++++ b/drivers/mtd/spi-nor/aspeed-smc.c
> +@@ -0,0 +1,539 @@
> ++/* ASPEED Static Memory Controller driver
> ++ * Copyright 2015
> ++ * Milton Miller
> ++ *
> ++ */
Make sure you have the right copyright headers on this one.
> ++#define DEBUG
Remove on the final version; we have dynamic debug enabled in the
OpenBMC kernels.
> ++
> ++#include <linux/device.h>
> ++#include <linux/io.h>
> ++#include <linux/module.h>
> ++#include <linux/mutex.h>
> ++#include <linux/mtd/mtd.h>
> ++#include <linux/mtd/partitions.h>
> ++#include <linux/mtd/spi-nor.h>
> ++#include <linux/of.h>
> ++#include <linux/of_platform.h>
> ++#include <linux/sysfs.h>
> ++
> ++enum smc_controller_type {
> ++ smc_type_leg, /* legacy register mode and map */
> ++ smc_type_fmc, /* new FMC 5 CEs multiple flash types */
> ++ smc_type_smc, /* SPI memory controller for BIOS / host */
> ++};
> ++
> ++enum smc_flash_type {
> ++ smc_type_nor = 0, /* controller connected to nor flash */
> ++ smc_type_nand = 1, /* controller connected to nand flash */
> ++ smc_type_spi = 2, /* controller connected to spi flash */
> ++};
> ++
> ++struct aspeed_smc_info {
> ++ unsigned long nce : 3; /* number of chip enables */
> ++ unsigned long hasdma : 1; /* has dma engine */
> ++ unsigned long maxwidth : 3; /* max width of spi bus */
> ++ unsigned long we0 : 5; /* we shift for ce 0 in cfg reg */
> ++ unsigned long hastype : 1; /* type shift for ce 0 in cfg reg */
> ++ u8 ctl0; /* offset in regs of ctl for ce 0 */
> ++ u8 cfg; /* offset in regs of cfg */
> ++ u8 time; /* offset in regs of timing */
> ++};
I wouldn't worry about bitfields here.
> ++
> ++#define E(_ce, _dma, _w, _ctl, _cfg, _we0, _hastype, _time, _misc) \
> ++ .nce = _ce, .ctl0 = _ctl, .cfg = 0, .we0 = _we0, .hastype = _hastype, \
> ++ .hasdma = _dma, .maxwidth = _w, .time = _time
> ++
> ++struct aspeed_smc_info aspeed_table[] = {
> ++ [smc_type_fmc] = { E(5, 1, 4, 0x10, 0x00, 16, 1, 0x54, 0x50) },
> ++ [smc_type_smc] = { E(1, 0, 2, 0x04, 0x00, 0, 0, 0x14, 0x10) },
> ++};
And it'll be a little more readable to expand this:
struct aspeed_smc_info aspeed_table[] = {
[smc_type_fmc] = {
.nce = 5,
.hasdma = 1,
.maxwidth = 0x10,
/* ... etc */
},
};
> ++#if 0 /* ATTR */
> ++static u32 spi_control_to_freq_div(u32 control)
> ++{
> ++ u32 sel;
> ++
> ++ sel = control & CONTROL_SPI_CLOCK_FREQ_SEL_MASK;
> ++ sel >>= CONTROL_SPI_CLOCK_FREQ_SEL_SHIFT;
> ++
> ++ /* 16, 14, 12, 10, 8, 6, 4, 2, 15, 13, 11, 9, 7, 5, 3, 1 */
> ++ return 16 - (((sel & 7) << 1) + ((sel & 8) >> 1));
> ++}
> ++
> ++static u32 spi_control_to_dummy_bytes(u32 control)
> ++{
> ++
> ++ return ((control & CONTROL_SPI_IO_DUMMY_CYCLES_LO) >>
> ++ CONTROL_SPI_IO_DUMMY_CYCLES_LO_SHIFT) |
> ++ ((control & CONTROL_SPI_IO_DUMMY_CYCLES_HI_SHIFT) >>
> ++ CONTROL_SPI_IO_DUMMY_CYCLES_HI_SHIFT);
> ++}
> ++
> ++static size_t show_ctl_div(u32 control, char *buf)
> ++{
> ++ return sprintf(buf, "%u\n", spi_control_to_freq_div(control));
> ++}
> ++
> ++static size_t show_ctl_dummy_bytes(u32 control, char *buf)
> ++{
> ++ return sprintf(buf, "%u\n", spi_control_to_dummy_bytes(control));
> ++}
> ++#endif /* ATTR */
There are a couple of `#if 0`-ed blocks in this patch. Was this just for
development, or something we want to keep?
> ++
> ++static int aspeed_smc_read_user(struct spi_nor *nor, loff_t from, size_t len,
> ++ size_t *retlen, u_char *read_buf)
> ++{
> ++ struct aspeed_smc_chip *chip = nor->priv;
> ++
> ++ aspeed_smc_start_user(nor);
> ++ aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from);
> ++ memcpy_fromio(read_buf, chip->base, len);
> ++ *retlen += len;
> ++ aspeed_smc_stop_user(nor);
> ++
> ++ return 0;
> ++}
> ++
> ++static void aspeed_smc_write_user(struct spi_nor *nor, loff_t to, size_t len,
> ++ size_t *retlen, const u_char *write_buf)
> ++{
> ++ struct aspeed_smc_chip *chip = nor->priv;
> ++
> ++ aspeed_smc_start_user(nor);
> ++ aspeed_smc_send_cmd_addr(nor, nor->program_opcode, to);
> ++ memcpy_toio(chip->base, write_buf, len);
> ++ *retlen += len;
> ++ aspeed_smc_stop_user(nor);
> ++}
> ++
> ++static int aspeed_smc_erase(struct spi_nor *nor, loff_t offs)
> ++{
> ++ aspeed_smc_start_user(nor);
> ++ aspeed_smc_send_cmd_addr(nor, nor->erase_opcode, offs);
> ++ aspeed_smc_stop_user(nor);
> ++
> ++ return 0;
> ++}
> ++
> ++static int aspeed_smc_remove(struct platform_device *dev)
> ++{
> ++ struct aspeed_smc_chip *chip;
> ++ struct aspeed_smc_controller *controller = platform_get_drvdata(dev);
> ++ int n;
> ++
> ++ for (n = 0; n < controller->info->nce; n++) {
> ++ chip = controller->chips[n];
> ++ if (chip)
> ++ mtd_device_unregister(&chip->mtd);
> ++ }
> ++
> ++ return 0;
> ++}
> ++
> ++const struct of_device_id aspeed_smc_matches[] = {
> ++ { .compatible = "aspeed,fmc", .data = &aspeed_table[smc_type_fmc] },
> ++ { .compatible = "aspeed,smc", .data = &aspeed_table[smc_type_smc] },
> ++ { }
> ++};
> ++MODULE_DEVICE_TABLE(of, aspeed_smc_matches);
I'd suggest grouping this with the platform_device definition below.
But in general, all looks good to me. Ben - any thoughts about the
smc controller implementation?
Cheers,
Jeremy
More information about the openbmc
mailing list