Commit d8e8fd0e authored by Masahiro Yamada's avatar Masahiro Yamada Committed by Miquel Raynal

mtd: rawnand: denali: decouple controller and NAND chips

Currently, this driver sticks to the legacy NAND model because it was
upstreamed before commit 2d472aba ("mtd: nand: document the NAND
controller/NAND chip DT representation"). However, relying on the
dummy_controller is already deprecated.

Switch over to the new controller/chip representation.

The struct denali_nand_info has been split into denali_controller
and denali_chip, to contain the controller data, per-chip data,
respectively.

One problem is, this commit changes the DT binding. So, as always,
the backward compatibility must be taken into consideration.

In the new binding, the controller node expects

  #address-cells = <1>;
  #size-cells = <0>;

... since the child nodes represent NAND chips.

In the old binding, the controller node may have subnodes, but they
are MTD partitions.

The denali_dt_is_legacy_binding() exploits it to distinguish old/new
platforms.

Going forward, the old binding is only allowed for existing DT files.
I updated the binding document.
Signed-off-by: default avatarMasahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: default avatarRob Herring <robh@kernel.org>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
parent 13defd47
...@@ -7,34 +7,48 @@ Required properties: ...@@ -7,34 +7,48 @@ Required properties:
"socionext,uniphier-denali-nand-v5b" - for Socionext UniPhier (v5b) "socionext,uniphier-denali-nand-v5b" - for Socionext UniPhier (v5b)
- reg : should contain registers location and length for data and reg. - reg : should contain registers location and length for data and reg.
- reg-names: Should contain the reg names "nand_data" and "denali_reg" - reg-names: Should contain the reg names "nand_data" and "denali_reg"
- #address-cells: should be 1. The cell encodes the chip select connection.
- #size-cells : should be 0.
- interrupts : The interrupt number. - interrupts : The interrupt number.
- clocks: should contain phandle of the controller core clock, the bus - clocks: should contain phandle of the controller core clock, the bus
interface clock, and the ECC circuit clock. interface clock, and the ECC circuit clock.
- clock-names: should contain "nand", "nand_x", "ecc" - clock-names: should contain "nand", "nand_x", "ecc"
Optional properties: Sub-nodes:
- nand-ecc-step-size: see nand.txt for details. If present, the value must be Sub-nodes represent available NAND chips.
512 for "altr,socfpga-denali-nand"
1024 for "socionext,uniphier-denali-nand-v5a" Required properties:
1024 for "socionext,uniphier-denali-nand-v5b" - reg: should contain the bank ID of the controller to which each chip
- nand-ecc-strength: see nand.txt for details. Valid values are: select is connected.
8, 15 for "altr,socfpga-denali-nand"
8, 16, 24 for "socionext,uniphier-denali-nand-v5a" Optional properties:
8, 16 for "socionext,uniphier-denali-nand-v5b" - nand-ecc-step-size: see nand.txt for details.
- nand-ecc-maximize: see nand.txt for details If present, the value must be
512 for "altr,socfpga-denali-nand"
The device tree may optionally contain sub-nodes describing partitions of the 1024 for "socionext,uniphier-denali-nand-v5a"
1024 for "socionext,uniphier-denali-nand-v5b"
- nand-ecc-strength: see nand.txt for details. Valid values are:
8, 15 for "altr,socfpga-denali-nand"
8, 16, 24 for "socionext,uniphier-denali-nand-v5a"
8, 16 for "socionext,uniphier-denali-nand-v5b"
- nand-ecc-maximize: see nand.txt for details
The chip nodes may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail. address space. See partition.txt for more detail.
Examples: Examples:
nand: nand@ff900000 { nand: nand@ff900000 {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <1>; #size-cells = <0>;
compatible = "altr,socfpga-denali-nand"; compatible = "altr,socfpga-denali-nand";
reg = <0xff900000 0x20>, <0xffb80000 0x1000>; reg = <0xff900000 0x20>, <0xffb80000 0x1000>;
reg-names = "nand_data", "denali_reg"; reg-names = "nand_data", "denali_reg";
clocks = <&nand_clk>, <&nand_x_clk>, <&nand_ecc_clk>; clocks = <&nand_clk>, <&nand_x_clk>, <&nand_ecc_clk>;
clock-names = "nand", "nand_x", "ecc"; clock-names = "nand", "nand_x", "ecc";
interrupts = <0 144 4>; interrupts = <0 144 4>;
nand@0 {
reg = <0>;
}
}; };
This diff is collapsed.
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/list.h>
#include <linux/mtd/rawnand.h> #include <linux/mtd/rawnand.h>
#include <linux/spinlock_types.h> #include <linux/spinlock_types.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -290,29 +291,98 @@ ...@@ -290,29 +291,98 @@
#define CHNL_ACTIVE__CHANNEL2 BIT(2) #define CHNL_ACTIVE__CHANNEL2 BIT(2)
#define CHNL_ACTIVE__CHANNEL3 BIT(3) #define CHNL_ACTIVE__CHANNEL3 BIT(3)
struct denali_nand_info { /**
struct nand_chip nand; * struct denali_chip_sel - per-CS data of Denali NAND
unsigned long clk_rate; /* core clock rate */ *
unsigned long clk_x_rate; /* bus interface clock rate */ * @bank: bank id of the controller this CS is connected to
int active_bank; /* currently selected bank */ * @hwhr2_and_we_2_re: value of timing register HWHR2_AND_WE_2_RE
* @tcwaw_and_addr_2_data: value of timing register TCWAW_AND_ADDR_2_DATA
* @re_2_we: value of timing register RE_2_WE
* @acc_clks: value of timing register ACC_CLKS
* @rdwr_en_lo_cnt: value of timing register RDWR_EN_LO_CNT
* @rdwr_en_hi_cnt: value of timing register RDWR_EN_HI_CNT
* @cs_setup_cnt: value of timing register CS_SETUP_CNT
* @re_2_re: value of timing register RE_2_RE
*/
struct denali_chip_sel {
int bank;
u32 hwhr2_and_we_2_re;
u32 tcwaw_and_addr_2_data;
u32 re_2_we;
u32 acc_clks;
u32 rdwr_en_lo_cnt;
u32 rdwr_en_hi_cnt;
u32 cs_setup_cnt;
u32 re_2_re;
};
/**
* struct denali_chip - per-chip data of Denali NAND
*
* @chip: base NAND chip structure
* @node: node to be used to associate this chip with the controller
* @nsels: the number of CS lines of this chip
* @sels: the array of per-cs data
*/
struct denali_chip {
struct nand_chip chip;
struct list_head node;
unsigned int nsels;
struct denali_chip_sel sels[0];
};
/**
* struct denali_controller - Denali NAND controller data
*
* @controller: base NAND controller structure
* @dev: device
* @chips: the list of chips attached to this controller
* @clk_rate: frequency of core clock
* @clk_x_rate: frequency of bus interface clock
* @reg: base of Register Interface
* @host: base of Host Data/Command interface
* @complete: completion used to wait for interrupts
* @irq: interrupt number
* @irq_mask: interrupt bits the controller is waiting for
* @irq_status: interrupt bits of events that have happened
* @irq_lock: lock to protect @irq_mask and @irq_status
* @dma_avail: set if DMA engine is available
* @devs_per_cs: number of devices connected in parallel
* @oob_skip_bytes: number of bytes in OOB skipped by the ECC engine
* @active_bank: active bank id
* @nbanks: the number of banks supported by this controller
* @revision: IP revision
* @caps: controller capabilities that cannot be detected run-time
* @ecc_caps: ECC engine capabilities
* @host_read: callback for read access of Host Data/Command Interface
* @host_write: callback for write access of Host Data/Command Interface
* @setup_dma: callback for setup of the Data DMA
*/
struct denali_controller {
struct nand_controller controller;
struct device *dev; struct device *dev;
void __iomem *reg; /* Register Interface */ struct list_head chips;
void __iomem *host; /* Host Data/Command Interface */ unsigned long clk_rate;
unsigned long clk_x_rate;
void __iomem *reg;
void __iomem *host;
struct completion complete; struct completion complete;
spinlock_t irq_lock; /* protect irq_mask and irq_status */
u32 irq_mask; /* interrupts we are waiting for */
u32 irq_status; /* interrupts that have happened */
int irq; int irq;
bool dma_avail; /* can support DMA? */ u32 irq_mask;
int devs_per_cs; /* devices connected in parallel */ u32 irq_status;
int oob_skip_bytes; /* number of bytes reserved for BBM */ spinlock_t irq_lock;
int max_banks; bool dma_avail;
unsigned int revision; /* IP revision */ int devs_per_cs;
unsigned int caps; /* IP capability (or quirk) */ int oob_skip_bytes;
int active_bank;
int nbanks;
unsigned int revision;
unsigned int caps;
const struct nand_ecc_caps *ecc_caps; const struct nand_ecc_caps *ecc_caps;
u32 (*host_read)(struct denali_nand_info *denali, u32 addr); u32 (*host_read)(struct denali_controller *denali, u32 addr);
void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data); void (*host_write)(struct denali_controller *denali, u32 addr,
void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr, u32 data);
void (*setup_dma)(struct denali_controller *denali, dma_addr_t dma_addr,
int page, bool write); int page, bool write);
}; };
...@@ -320,7 +390,9 @@ struct denali_nand_info { ...@@ -320,7 +390,9 @@ struct denali_nand_info {
#define DENALI_CAP_DMA_64BIT BIT(1) #define DENALI_CAP_DMA_64BIT BIT(1)
int denali_calc_ecc_bytes(int step_size, int strength); int denali_calc_ecc_bytes(int step_size, int strength);
int denali_init(struct denali_nand_info *denali); int denali_chip_init(struct denali_controller *denali,
void denali_remove(struct denali_nand_info *denali); struct denali_chip *dchip);
int denali_init(struct denali_controller *denali);
void denali_remove(struct denali_controller *denali);
#endif /* __DENALI_H__ */ #endif /* __DENALI_H__ */
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include "denali.h" #include "denali.h"
struct denali_dt { struct denali_dt {
struct denali_nand_info denali; struct denali_controller controller;
struct clk *clk; /* core clock */ struct clk *clk; /* core clock */
struct clk *clk_x; /* bus interface clock */ struct clk *clk_x; /* bus interface clock */
struct clk *clk_ecc; /* ECC circuit clock */ struct clk *clk_ecc; /* ECC circuit clock */
...@@ -71,19 +71,92 @@ static const struct of_device_id denali_nand_dt_ids[] = { ...@@ -71,19 +71,92 @@ static const struct of_device_id denali_nand_dt_ids[] = {
}; };
MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
static int denali_dt_chip_init(struct denali_controller *denali,
struct device_node *chip_np)
{
struct denali_chip *dchip;
u32 bank;
int nsels, i, ret;
nsels = of_property_count_u32_elems(chip_np, "reg");
if (nsels < 0)
return nsels;
dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
GFP_KERNEL);
if (!dchip)
return -ENOMEM;
dchip->nsels = nsels;
for (i = 0; i < nsels; i++) {
ret = of_property_read_u32_index(chip_np, "reg", i, &bank);
if (ret)
return ret;
dchip->sels[i].bank = bank;
nand_set_flash_node(&dchip->chip, chip_np);
}
return denali_chip_init(denali, dchip);
}
/* Backward compatibility for old platforms */
static int denali_dt_legacy_chip_init(struct denali_controller *denali)
{
struct denali_chip *dchip;
int nsels, i;
nsels = denali->nbanks;
dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
GFP_KERNEL);
if (!dchip)
return -ENOMEM;
dchip->nsels = nsels;
for (i = 0; i < nsels; i++)
dchip->sels[i].bank = i;
nand_set_flash_node(&dchip->chip, denali->dev->of_node);
return denali_chip_init(denali, dchip);
}
/*
* Check the DT binding.
* The new binding expects chip subnodes in the controller node.
* So, #address-cells = <1>; #size-cells = <0>; are required.
* Check the #size-cells to distinguish the binding.
*/
static bool denali_dt_is_legacy_binding(struct device_node *np)
{
u32 cells;
int ret;
ret = of_property_read_u32(np, "#size-cells", &cells);
if (ret)
return true;
return cells != 0;
}
static int denali_dt_probe(struct platform_device *pdev) static int denali_dt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct resource *res; struct resource *res;
struct denali_dt *dt; struct denali_dt *dt;
const struct denali_dt_data *data; const struct denali_dt_data *data;
struct denali_nand_info *denali; struct denali_controller *denali;
struct device_node *np;
int ret; int ret;
dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL); dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
if (!dt) if (!dt)
return -ENOMEM; return -ENOMEM;
denali = &dt->denali; denali = &dt->controller;
data = of_device_get_match_data(dev); data = of_device_get_match_data(dev);
if (data) { if (data) {
...@@ -140,9 +213,26 @@ static int denali_dt_probe(struct platform_device *pdev) ...@@ -140,9 +213,26 @@ static int denali_dt_probe(struct platform_device *pdev)
if (ret) if (ret)
goto out_disable_clk_ecc; goto out_disable_clk_ecc;
if (denali_dt_is_legacy_binding(dev->of_node)) {
ret = denali_dt_legacy_chip_init(denali);
if (ret)
goto out_remove_denali;
} else {
for_each_child_of_node(dev->of_node, np) {
ret = denali_dt_chip_init(denali, np);
if (ret) {
of_node_put(np);
goto out_remove_denali;
}
}
}
platform_set_drvdata(pdev, dt); platform_set_drvdata(pdev, dt);
return 0; return 0;
out_remove_denali:
denali_remove(denali);
out_disable_clk_ecc: out_disable_clk_ecc:
clk_disable_unprepare(dt->clk_ecc); clk_disable_unprepare(dt->clk_ecc);
out_disable_clk_x: out_disable_clk_x:
...@@ -157,7 +247,7 @@ static int denali_dt_remove(struct platform_device *pdev) ...@@ -157,7 +247,7 @@ static int denali_dt_remove(struct platform_device *pdev)
{ {
struct denali_dt *dt = platform_get_drvdata(pdev); struct denali_dt *dt = platform_get_drvdata(pdev);
denali_remove(&dt->denali); denali_remove(&dt->controller);
clk_disable_unprepare(dt->clk_ecc); clk_disable_unprepare(dt->clk_ecc);
clk_disable_unprepare(dt->clk_x); clk_disable_unprepare(dt->clk_x);
clk_disable_unprepare(dt->clk); clk_disable_unprepare(dt->clk);
......
...@@ -29,10 +29,11 @@ NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15); ...@@ -29,10 +29,11 @@ NAND_ECC_CAPS_SINGLE(denali_pci_ecc_caps, denali_calc_ecc_bytes, 512, 8, 15);
static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{ {
int ret;
resource_size_t csr_base, mem_base; resource_size_t csr_base, mem_base;
unsigned long csr_len, mem_len; unsigned long csr_len, mem_len;
struct denali_nand_info *denali; struct denali_controller *denali;
struct denali_chip *dchip;
int nsels, ret, i;
denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL); denali = devm_kzalloc(&dev->dev, sizeof(*denali), GFP_KERNEL);
if (!denali) if (!denali)
...@@ -64,7 +65,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -64,7 +65,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
denali->dev = &dev->dev; denali->dev = &dev->dev;
denali->irq = dev->irq; denali->irq = dev->irq;
denali->ecc_caps = &denali_pci_ecc_caps; denali->ecc_caps = &denali_pci_ecc_caps;
denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
denali->clk_rate = 50000000; /* 50 MHz */ denali->clk_rate = 50000000; /* 50 MHz */
denali->clk_x_rate = 200000000; /* 200 MHz */ denali->clk_x_rate = 200000000; /* 200 MHz */
...@@ -91,10 +91,32 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -91,10 +91,32 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (ret) if (ret)
goto out_unmap_host; goto out_unmap_host;
nsels = denali->nbanks;
dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
GFP_KERNEL);
if (!dchip) {
ret = -ENOMEM;
goto out_remove_denali;
}
dchip->chip.ecc.options |= NAND_ECC_MAXIMIZE;
dchip->nsels = nsels;
for (i = 0; i < nsels; i++)
dchip->sels[i].bank = i;
ret = denali_chip_init(denali, dchip);
if (ret)
goto out_remove_denali;
pci_set_drvdata(dev, denali); pci_set_drvdata(dev, denali);
return 0; return 0;
out_remove_denali:
denali_remove(denali);
out_unmap_host: out_unmap_host:
iounmap(denali->host); iounmap(denali->host);
out_unmap_reg: out_unmap_reg:
...@@ -104,7 +126,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -104,7 +126,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
static void denali_pci_remove(struct pci_dev *dev) static void denali_pci_remove(struct pci_dev *dev)
{ {
struct denali_nand_info *denali = pci_get_drvdata(dev); struct denali_controller *denali = pci_get_drvdata(dev);
denali_remove(denali); denali_remove(denali);
iounmap(denali->reg); iounmap(denali->reg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment