From 99a32b73701d541f7d5974181652e7a1194010b3 Mon Sep 17 00:00:00 2001 From: Sunny Luo Date: Mon, 12 Aug 2019 13:04:33 +0800 Subject: [PATCH] spi nor: spicc: change to use spicc to access spi nor [1/1] PD#TV-8401 Problem: Customer needs to access spi nor by the spicc. Solution: add spi nor interfaces in spicc driver. add gd25q80c/FM25Q08A surpport Verify: tl1 x301 Change-Id: If94858d46c31fea6b37034a8b1dfe94a9e9f4603 Signed-off-by: Sunny Luo --- arch/arm/boot/dts/amlogic/mesontl1.dtsi | 25 ++- arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts | 16 +- arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts | 16 +- arch/arm/configs/meson64_a32_defconfig | 1 + arch/arm64/boot/dts/amlogic/mesontl1.dtsi | 25 ++- arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts | 15 +- arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts | 15 +- arch/arm64/configs/meson64_defconfig | 1 + drivers/amlogic/spicc/spicc.c | 236 +++++++++++++++++++-- drivers/mtd/spi-nor/spi-nor.c | 7 + 10 files changed, 327 insertions(+), 30 deletions(-) diff --git a/arch/arm/boot/dts/amlogic/mesontl1.dtsi b/arch/arm/boot/dts/amlogic/mesontl1.dtsi index f65d300..5990fce 100644 --- a/arch/arm/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontl1.dtsi @@ -1198,6 +1198,29 @@ }; }; + spicc_a: spicc@ffd13000 { + compatible = "amlogic, spicc"; + status = "disabled"; + device_id = <0>; + reg = <0xffd13000 0x3c>; + clocks = <&clkc CLKID_SPICC0>, + <&clkc CLKID_SPICC0_COMP>; + clock-names = "cts_spicc_hclk", "spicc_clk"; + clk_rate = <400000000>; + //interrupts = <0 81 1>; + enhance = <1>; + dma_tx_threshold = <3>; + dma_rx_threshold = <3>; + dma_num_per_read_burst = <3>; + dma_num_per_write_burst = <3>; + ssctl = <0>; + dma_en = <0>; + delay_control = <0x15>; + cs_delay = <10>; + enhance_dlyctl = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; spifc: spifc@ffd14000 { compatible = "amlogic,aml-spi-nor"; @@ -2031,7 +2054,7 @@ "spi0_miso_h", "spi0_clk_h"; function = "spi0"; - drive-strength = <1>; + drive-strength = <3>; }; }; diff --git a/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts b/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts index af9ad28..54b0328 100644 --- a/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts +++ b/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts @@ -40,7 +40,7 @@ i2c2 = &i2c2; i2c3 = &i2c3; i2c4 = &i2c_AO; - spi0 = &spicc0; + spi0 = &spicc_a; spi1 = &spicc1; }; @@ -2096,12 +2096,24 @@ }; &spicc0 { - status = "okay"; + status = "disabled"; pinctrl-names = "default"; pinctrl-0 = <&spicc0_pins_h>; cs-gpios = <&gpio GPIOH_20 0>; }; +&spicc_a { + status = "okay"; + pinctrl-names= "default"; + pinctrl-0=<&spicc0_pins_h>; + cs-gpios = <&gpio GPIOH_20 GPIO_ACTIVE_HIGH>; + spi-nor@0 { + compatible = "jedec,spi-nor"; + status = "okay"; + frequency = <40000000>; + }; +}; + &meson_fb { status = "okay"; display_size_default = <1920 1080 1920 2160 32>; diff --git a/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts b/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts index 32e015c..caca321 100644 --- a/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts +++ b/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts @@ -40,7 +40,7 @@ i2c2 = &i2c2; i2c3 = &i2c3; i2c4 = &i2c_AO; - spi0 = &spicc0; + spi0 = &spicc_a; spi1 = &spicc1; }; @@ -2093,12 +2093,24 @@ }; &spicc0 { - status = "okay"; + status = "disabled"; pinctrl-names = "default"; pinctrl-0 = <&spicc0_pins_h>; cs-gpios = <&gpio GPIOH_20 0>; }; +&spicc_a { + status = "okay"; + pinctrl-names= "default"; + pinctrl-0=<&spicc0_pins_h>; + cs-gpios = <&gpio GPIOH_20 GPIO_ACTIVE_HIGH>; + spi-nor@0 { + compatible = "jedec,spi-nor"; + status = "okay"; + frequency = <40000000>; + }; +}; + &meson_fb { status = "okay"; display_size_default = <1920 1080 1920 2160 32>; diff --git a/arch/arm/configs/meson64_a32_defconfig b/arch/arm/configs/meson64_a32_defconfig index c5fba6d..3f244c8 100644 --- a/arch/arm/configs/meson64_a32_defconfig +++ b/arch/arm/configs/meson64_a32_defconfig @@ -396,6 +396,7 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y CONFIG_MTD_OOPS=y CONFIG_MTD_NAND=y +CONFIG_MTD_SPI_NOR=y CONFIG_MTD_UBI=y CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y diff --git a/arch/arm64/boot/dts/amlogic/mesontl1.dtsi b/arch/arm64/boot/dts/amlogic/mesontl1.dtsi index e6dc78a..a02a7ce 100644 --- a/arch/arm64/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontl1.dtsi @@ -1190,6 +1190,29 @@ }; }; + spicc_a: spicc@ffd13000 { + compatible = "amlogic, spicc"; + status = "disabled"; + device_id = <0>; + reg = <0x0 0xffd13000 0x0 0x3c>; + clocks = <&clkc CLKID_SPICC0>, + <&clkc CLKID_SPICC0_COMP>; + clock-names = "cts_spicc_hclk", "spicc_clk"; + clk_rate = <400000000>; + //interrupts = <0 81 1>; + enhance = <1>; + dma_tx_threshold = <3>; + dma_rx_threshold = <3>; + dma_num_per_read_burst = <3>; + dma_num_per_write_burst = <3>; + ssctl = <0>; + dma_en = <0>; + delay_control = <0x15>; + cs_delay = <10>; + enhance_dlyctl = <0>; + #address-cells = <1>; + #size-cells = <0>; + }; spifc: spifc@ffd14000 { compatible = "amlogic,aml-spi-nor"; @@ -2023,7 +2046,7 @@ "spi0_miso_h", "spi0_clk_h"; function = "spi0"; - drive-strength = <1>; + drive-strength = <3>; }; }; diff --git a/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts b/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts index ac9b0da..49c607f 100644 --- a/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts +++ b/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts @@ -40,6 +40,7 @@ i2c2 = &i2c2; i2c3 = &i2c3; i2c4 = &i2c_AO; + spi0 = &spicc_a; }; memory@00000000 { @@ -2092,12 +2093,24 @@ }; &spicc0 { - status = "okay"; + status = "disabled"; pinctrl-names = "default"; pinctrl-0 = <&spicc0_pins_h>; cs-gpios = <&gpio GPIOH_20 0>; }; +&spicc_a { + status = "okay"; + pinctrl-names= "default"; + pinctrl-0=<&spicc0_pins_h>; + cs-gpios = <&gpio GPIOH_20 GPIO_ACTIVE_HIGH>; + spi-nor@0 { + compatible = "jedec,spi-nor"; + status = "okay"; + frequency = <40000000>; + }; +}; + &meson_fb { status = "okay"; display_size_default = <1920 1080 1920 2160 32>; diff --git a/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts b/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts index 6661cca5..9290705 100644 --- a/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts +++ b/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts @@ -40,6 +40,7 @@ i2c2 = &i2c2; i2c3 = &i2c3; i2c4 = &i2c_AO; + spi0 = &spicc_a; }; memory@00000000 { @@ -2085,12 +2086,24 @@ }; &spicc0 { - status = "okay"; + status = "disabled"; pinctrl-names = "default"; pinctrl-0 = <&spicc0_pins_h>; cs-gpios = <&gpio GPIOH_20 0>; }; +&spicc_a { + status = "okay"; + pinctrl-names= "default"; + pinctrl-0=<&spicc0_pins_h>; + cs-gpios = <&gpio GPIOH_20 GPIO_ACTIVE_HIGH>; + spi-nor@0 { + compatible = "jedec,spi-nor"; + status = "okay"; + frequency = <40000000>; + }; +}; + &meson_fb { status = "okay"; display_size_default = <1920 1080 1920 2160 32>; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index e03127d..3f505d1 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -392,6 +392,7 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y CONFIG_MTD_OOPS=y CONFIG_MTD_NAND=y +CONFIG_MTD_SPI_NOR=y CONFIG_MTD_UBI=y CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y diff --git a/drivers/amlogic/spicc/spicc.c b/drivers/amlogic/spicc/spicc.c index d1bf8a63..26c1623 100644 --- a/drivers/amlogic/spicc/spicc.c +++ b/drivers/amlogic/spicc/spicc.c @@ -35,6 +35,9 @@ #include #include #include +#ifdef CONFIG_MTD_SPI_NOR +#include +#endif #include "spicc.h" /* #define CONFIG_SPICC_LOG */ @@ -104,6 +107,13 @@ struct spicc { int log_size; int log_count; #endif +#ifdef CONFIG_MTD_SPI_NOR + struct device *dev; + struct spi_nor *nor; + int nor_cs; + /* used by nor core */ + struct mutex nor_lock; +#endif }; #ifdef CONFIG_SPICC_LOG @@ -339,7 +349,7 @@ static void spicc_set_clk(struct spicc *spicc, int speed) sys_clk_rate = clk_get_rate(spicc->clk); if (spicc_get_flag(spicc, FLAG_ENHANCE)) { - div = sys_clk_rate/speed; + div = DIV_ROUND_UP(sys_clk_rate, speed); if (div < 2) div = 2; div = (div >> 1) - 1; @@ -733,6 +743,136 @@ int dirspi_register_board_info(struct spi_board_info const *info, } EXPORT_SYMBOL_GPL(dirspi_register_board_info); +#ifdef CONFIG_MTD_SPI_NOR +static int meson_snor_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct spicc *spicc = nor->priv; + + mutex_lock(&spicc->nor_lock); + + return 0; +} + +static void meson_snor_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct spicc *spicc = nor->priv; + + mutex_unlock(&spicc->nor_lock); +} + +static int meson_snor_read_reg(struct spi_nor *nor, u8 opcode, + u8 *buf, int len) +{ + struct spicc *spicc = nor->priv; + int ret; + + gpio_direction_output(spicc->nor_cs, 0); + + ret = spicc_hw_xfer(spicc, &opcode, 0, 1); + if (!ret) + ret = spicc_hw_xfer(spicc, 0, buf, len); + + gpio_direction_output(spicc->nor_cs, 1); + + return ret; +} + +ssize_t meson_snor_read(struct spi_nor *nor, loff_t from, + size_t len, u_char *read_buf) +{ + struct spicc *spicc = nor->priv; + u8 tx_buf[4]; + int ret; + + gpio_direction_output(spicc->nor_cs, 0); + + tx_buf[0] = nor->read_opcode; + tx_buf[1] = (from >> 16) & 0xff; + tx_buf[2] = (from >> 8) & 0xff; + tx_buf[3] = from & 0xff; + ret = spicc_hw_xfer(spicc, tx_buf, 0, 4); + if (!ret) + ret = spicc_hw_xfer(spicc, 0, read_buf, len); + + gpio_direction_output(spicc->nor_cs, 1); + + return ret ? 0 : len; +} + +static int meson_snor_write_reg(struct spi_nor *nor, u8 opcode, + u8 *buf, int len) +{ + struct spicc *spicc = nor->priv; + int ret; + + gpio_direction_output(spicc->nor_cs, 0); + + ret = spicc_hw_xfer(spicc, &opcode, 0, 1); + if (!ret) + ret = spicc_hw_xfer(spicc, buf, 0, len); + + gpio_direction_output(spicc->nor_cs, 1); + + return ret; +} + +static ssize_t meson_snor_write(struct spi_nor *nor, loff_t to, + size_t len, const u_char *write_buf) +{ + struct spicc *spicc = nor->priv; + u8 tx_buf[4]; + int ret; + + gpio_direction_output(spicc->nor_cs, 0); + + tx_buf[0] = nor->program_opcode; + tx_buf[1] = (to >> 16) & 0xff; + tx_buf[2] = (to >> 8) & 0xff; + tx_buf[3] = to & 0xff; + ret = spicc_hw_xfer(spicc, tx_buf, 0, 4); + if (!ret) + ret = spicc_hw_xfer(spicc, (u8 *)write_buf, 0, len); + + gpio_direction_output(spicc->nor_cs, 1); + + return ret ? 0 : len; +} + +static struct spi_nor *meson_snor_init(struct spicc *spicc, + struct device_node *np) +{ + struct device *dev = spicc->dev; + struct spi_nor *nor; + struct mtd_info *mtd; + + nor = devm_kzalloc(dev, sizeof(*nor), GFP_KERNEL); + if (!nor) + return 0; + + nor->dev = dev; + spi_nor_set_flash_node(nor, np); + nor->priv = spicc; + nor->prepare = meson_snor_prep; + nor->unprepare = meson_snor_unprep; + nor->read_reg = meson_snor_read_reg; + nor->write_reg = meson_snor_write_reg; + nor->read = meson_snor_read; + nor->write = meson_snor_write; + nor->erase = NULL; + mtd = &nor->mtd; + mtd->name = (np->name) ? np->name : "meson_snor"; + + if (!spi_nor_scan(nor, NULL, SPI_NOR_NORMAL)) { + if (!mtd_device_register(mtd, NULL, 0)) + return nor; + } + + devm_kfree(dev, nor); + return 0; +} + +#endif /* end CONFIG_MTD_SPI_NOR */ + /* setting clock and pinmux here */ static int spicc_setup(struct spi_device *spi) { @@ -1107,25 +1247,84 @@ static int spicc_probe(struct platform_device *pdev) struct spi_master *master; struct spicc *spicc; int i, ret; +#ifdef CONFIG_MTD_SPI_NOR + struct device_node *child, *np = pdev->dev.of_node; + int speed; +#endif WARN_ON(!pdev->dev.of_node); - master = spi_alloc_master(&pdev->dev, sizeof(*spicc)); - if (IS_ERR_OR_NULL(master)) { - dev_err(&pdev->dev, "allocate spi master failed!\n"); + spicc = devm_kzalloc(&pdev->dev, sizeof(*spicc), GFP_KERNEL); + if (!spicc) return -ENOMEM; - } - spicc = spi_master_get_devdata(master); - spicc->master = master; + dev_set_drvdata(&pdev->dev, spicc); ret = of_spicc_get_data(spicc, pdev); if (ret) { dev_err(&pdev->dev, "of error=%d\n", ret); - goto err; + goto err1; } spicc_hw_init(spicc); spicc_log_init(spicc); spicc_log(spicc, 0, 0, PROBE_BEGIN); + spin_lock_init(&spicc->lock); + init_completion(&spicc->completion); + if (spicc->irq) { + if (request_irq(spicc->irq, spicc_xfer_complete_isr, + IRQF_SHARED, "spicc", spicc)) { + dev_err(&pdev->dev, "request irq(%d) failed!\n", + spicc->irq); + spicc->irq = 0; + } else + disable_irq_nosync(spicc->irq); + } + spicc_log(spicc, &spicc->irq, 1, REQUEST_IRQ); + +#ifdef CONFIG_MTD_SPI_NOR + spicc->dev = &pdev->dev; + child = of_get_next_available_child(np, NULL); + if (child && of_device_is_compatible(child, "jedec,spi-nor")) { + if (of_gpio_named_count(np, "cs-gpios") != 1) { + dev_err(&pdev->dev, "nor: cs-gpios error\n"); + ret = -EINVAL; + goto err1; + } + spicc->nor_cs = of_get_named_gpio(np, "cs-gpios", 0); + if (!gpio_is_valid(spicc->nor_cs)) { + dev_err(&pdev->dev, "nor: invalid cs-gpio\n"); + ret = -EINVAL; + goto err1; + } + + ret = gpio_request(spicc->nor_cs, dev_name(&pdev->dev)); + if (ret) { + dev_err(&pdev->dev, "nor: request cs-gpio failed\n"); + goto err1; + } + gpio_direction_output(spicc->nor_cs, 1); + + ret = of_property_read_u32(child, "frequency", &speed); + if (!ret) + spicc_set_clk(spicc, speed); + + mutex_init(&spicc->nor_lock); + spicc->nor = meson_snor_init(spicc, child); + } + + if (spicc->nor) + return 0; + + mutex_destroy(&spicc->nor_lock); + dev_warn(&pdev->dev, "no snor on spicc bus\n"); +#endif + + master = spi_alloc_master(&pdev->dev, sizeof(*spicc)); + if (IS_ERR_OR_NULL(master)) { + dev_err(&pdev->dev, "allocate spi master failed!\n"); + return -ENOMEM; + } + spicc = spi_master_get_devdata(master); + spicc->master = master; master->dev.of_node = pdev->dev.of_node; master->bus_num = spicc->device_id; master->setup = spicc_setup; @@ -1152,21 +1351,8 @@ static int spicc_probe(struct platform_device *pdev) ret = -EBUSY; goto err; } - spin_lock_init(&spicc->lock); INIT_LIST_HEAD(&spicc->msg_queue); - init_completion(&spicc->completion); - if (spicc->irq) { - if (request_irq(spicc->irq, spicc_xfer_complete_isr, - IRQF_SHARED, "spicc", spicc)) { - dev_err(&pdev->dev, "master %d request irq(%d) failed!\n", - master->bus_num, spicc->irq); - spicc->irq = 0; - } else - disable_irq_nosync(spicc->irq); - } - spicc_log(spicc, &spicc->irq, 1, REQUEST_IRQ); - /*setup class*/ spicc->cls.name = kzalloc(10, GFP_KERNEL); sprintf((char *)spicc->cls.name, "spicc%d", master->bus_num); @@ -1185,6 +1371,9 @@ static int spicc_probe(struct platform_device *pdev) return ret; err: spi_master_put(master); + +err1: + devm_kfree(&pdev->dev, spicc); return ret; } @@ -1196,6 +1385,9 @@ static int spicc_remove(struct platform_device *pdev) spicc_log_exit(spicc); spi_unregister_master(spicc->master); destroy_workqueue(spicc->wq); +#ifdef CONFIG_MTD_SPI_NOR + mutex_destroy(&spicc->nor_lock); +#endif if (spicc->pinctrl) devm_pinctrl_put(spicc->pinctrl); return 0; @@ -1254,7 +1446,7 @@ static void __exit spicc_exit(void) platform_driver_unregister(&spicc_driver); } -subsys_initcall(spicc_init); +module_init(spicc_init); module_exit(spicc_exit); MODULE_DESCRIPTION("Amlogic SPICC driver"); diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 21dde52..d0c0ff2 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -831,6 +831,11 @@ static const struct flash_info spi_nor_ids[] = { /* GigaDevice */ { + "gd25q80c", INFO(0xc84014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, + { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) @@ -1009,6 +1014,8 @@ static const struct flash_info spi_nor_ids[] = { { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + + {"FM25Q08A", INFO(0xa14014, 0x0, 64 * 1024, 16, SECT_4K) }, { }, }; -- 2.7.4