|
@@ -24,12 +24,13 @@
|
|
|
* GNU General Public License for more details.
|
|
|
*/
|
|
|
|
|
|
+#include <clk.h>
|
|
|
#include <common.h>
|
|
|
-#include <fdtdec.h>
|
|
|
+#include <dm.h>
|
|
|
#include <malloc.h>
|
|
|
#include <memalign.h>
|
|
|
#include <nand.h>
|
|
|
-#include <asm/global_data.h>
|
|
|
+#include <reset.h>
|
|
|
#include <dm/device_compat.h>
|
|
|
#include <dm/devres.h>
|
|
|
#include <linux/bitops.h>
|
|
@@ -45,8 +46,6 @@
|
|
|
#include <asm/gpio.h>
|
|
|
#include <asm/arch/clock.h>
|
|
|
|
|
|
-DECLARE_GLOBAL_DATA_PTR;
|
|
|
-
|
|
|
#define NFC_REG_CTL 0x0000
|
|
|
#define NFC_REG_ST 0x0004
|
|
|
#define NFC_REG_INT 0x0008
|
|
@@ -263,7 +262,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
|
|
|
* NAND Controller structure: stores sunxi NAND controller information
|
|
|
*
|
|
|
* @controller: base controller structure
|
|
|
- * @dev: parent device (used to print error messages)
|
|
|
+ * @dev: DM device (used to print error messages)
|
|
|
* @regs: NAND controller registers
|
|
|
* @ahb_clk: NAND Controller AHB clock
|
|
|
* @mod_clk: NAND Controller mod clock
|
|
@@ -276,7 +275,7 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
|
|
|
*/
|
|
|
struct sunxi_nfc {
|
|
|
struct nand_hw_control controller;
|
|
|
- struct device *dev;
|
|
|
+ struct udevice *dev;
|
|
|
void __iomem *regs;
|
|
|
struct clk *ahb_clk;
|
|
|
struct clk *mod_clk;
|
|
@@ -1605,24 +1604,24 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
|
|
|
+static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc,
|
|
|
+ ofnode np, int devnum)
|
|
|
{
|
|
|
const struct nand_sdr_timings *timings;
|
|
|
- const void *blob = gd->fdt_blob;
|
|
|
struct sunxi_nand_chip *chip;
|
|
|
struct mtd_info *mtd;
|
|
|
struct nand_chip *nand;
|
|
|
int nsels;
|
|
|
int ret;
|
|
|
int i;
|
|
|
- u32 cs[8], rb[8];
|
|
|
+ u32 tmp;
|
|
|
|
|
|
- if (!fdt_getprop(blob, node, "reg", &nsels))
|
|
|
+ if (!ofnode_get_property(np, "reg", &nsels))
|
|
|
return -EINVAL;
|
|
|
|
|
|
nsels /= sizeof(u32);
|
|
|
if (!nsels || nsels > 8) {
|
|
|
- dev_err(nfc->dev, "invalid reg property size\n");
|
|
|
+ dev_err(dev, "invalid reg property size\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
@@ -1630,7 +1629,7 @@ static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
|
|
|
(nsels * sizeof(struct sunxi_nand_chip_sel)),
|
|
|
GFP_KERNEL);
|
|
|
if (!chip) {
|
|
|
- dev_err(nfc->dev, "could not allocate chip\n");
|
|
|
+ dev_err(dev, "could not allocate chip\n");
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
|
|
@@ -1638,48 +1637,34 @@ static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
|
|
|
chip->selected = -1;
|
|
|
|
|
|
for (i = 0; i < nsels; i++) {
|
|
|
- cs[i] = -1;
|
|
|
- rb[i] = -1;
|
|
|
- }
|
|
|
-
|
|
|
- ret = fdtdec_get_int_array(gd->fdt_blob, node, "reg", cs, nsels);
|
|
|
- if (ret) {
|
|
|
- dev_err(nfc->dev, "could not retrieve reg property: %d\n", ret);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- ret = fdtdec_get_int_array(gd->fdt_blob, node, "allwinner,rb", rb,
|
|
|
- nsels);
|
|
|
- if (ret) {
|
|
|
- dev_err(nfc->dev, "could not retrieve reg property: %d\n", ret);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < nsels; i++) {
|
|
|
- int tmp = cs[i];
|
|
|
+ ret = ofnode_read_u32_index(np, "reg", i, &tmp);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(dev, "could not retrieve reg property: %d\n",
|
|
|
+ ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
if (tmp > NFC_MAX_CS) {
|
|
|
- dev_err(nfc->dev,
|
|
|
+ dev_err(dev,
|
|
|
"invalid reg value: %u (max CS = 7)\n", tmp);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
|
|
|
- dev_err(nfc->dev, "CS %d already assigned\n", tmp);
|
|
|
+ dev_err(dev, "CS %d already assigned\n", tmp);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
chip->sels[i].cs = tmp;
|
|
|
|
|
|
- tmp = rb[i];
|
|
|
- if (tmp >= 0 && tmp < 2) {
|
|
|
+ if (!ofnode_read_u32_index(np, "allwinner,rb", i, &tmp) &&
|
|
|
+ tmp < 2) {
|
|
|
chip->sels[i].rb.type = RB_NATIVE;
|
|
|
chip->sels[i].rb.info.nativeid = tmp;
|
|
|
} else {
|
|
|
- ret = gpio_request_by_name_nodev(offset_to_ofnode(node),
|
|
|
- "rb-gpios", i,
|
|
|
- &chip->sels[i].rb.info.gpio,
|
|
|
- GPIOD_IS_IN);
|
|
|
+ ret = gpio_request_by_name(dev, "rb-gpios", i,
|
|
|
+ &chip->sels[i].rb.info.gpio,
|
|
|
+ GPIOD_IS_IN);
|
|
|
if (ret)
|
|
|
chip->sels[i].rb.type = RB_GPIO;
|
|
|
else
|
|
@@ -1690,7 +1675,7 @@ static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
|
|
|
timings = onfi_async_timing_mode_to_sdr_timings(0);
|
|
|
if (IS_ERR(timings)) {
|
|
|
ret = PTR_ERR(timings);
|
|
|
- dev_err(nfc->dev,
|
|
|
+ dev_err(dev,
|
|
|
"could not retrieve timings for ONFI mode 0: %d\n",
|
|
|
ret);
|
|
|
return ret;
|
|
@@ -1698,7 +1683,7 @@ static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
|
|
|
|
|
|
ret = sunxi_nand_chip_set_timings(nfc, chip, timings);
|
|
|
if (ret) {
|
|
|
- dev_err(nfc->dev, "could not configure chip timings: %d\n", ret);
|
|
|
+ dev_err(dev, "could not configure chip timings: %d\n", ret);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1711,7 +1696,7 @@ static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
|
|
|
* in the DT.
|
|
|
*/
|
|
|
nand->ecc.mode = NAND_ECC_HW;
|
|
|
- nand->flash_node = offset_to_ofnode(node);
|
|
|
+ nand->flash_node = np;
|
|
|
nand->select_chip = sunxi_nfc_select_chip;
|
|
|
nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
|
|
|
nand->read_buf = sunxi_nfc_read_buf;
|
|
@@ -1733,25 +1718,25 @@ static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
|
|
|
|
|
|
ret = sunxi_nand_chip_init_timings(nfc, chip);
|
|
|
if (ret) {
|
|
|
- dev_err(nfc->dev, "could not configure chip timings: %d\n", ret);
|
|
|
+ dev_err(dev, "could not configure chip timings: %d\n", ret);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
ret = sunxi_nand_ecc_init(mtd, &nand->ecc);
|
|
|
if (ret) {
|
|
|
- dev_err(nfc->dev, "ECC init failed: %d\n", ret);
|
|
|
+ dev_err(dev, "ECC init failed: %d\n", ret);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
ret = nand_scan_tail(mtd);
|
|
|
if (ret) {
|
|
|
- dev_err(nfc->dev, "nand_scan_tail failed: %d\n", ret);
|
|
|
+ dev_err(dev, "nand_scan_tail failed: %d\n", ret);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
ret = nand_register(devnum, mtd);
|
|
|
if (ret) {
|
|
|
- dev_err(nfc->dev, "failed to register mtd device: %d\n", ret);
|
|
|
+ dev_err(dev, "failed to register mtd device: %d\n", ret);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1760,25 +1745,13 @@ static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int sunxi_nand_chips_init(int node, struct sunxi_nfc *nfc)
|
|
|
+static int sunxi_nand_chips_init(struct udevice *dev, struct sunxi_nfc *nfc)
|
|
|
{
|
|
|
- const void *blob = gd->fdt_blob;
|
|
|
- int nand_node;
|
|
|
+ ofnode nand_np;
|
|
|
int ret, i = 0;
|
|
|
|
|
|
- for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0;
|
|
|
- nand_node = fdt_next_subnode(blob, nand_node))
|
|
|
- i++;
|
|
|
-
|
|
|
- if (i > 8) {
|
|
|
- dev_err(nfc->dev, "too many NAND chips: %d (max = 8)\n", i);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- i = 0;
|
|
|
- for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0;
|
|
|
- nand_node = fdt_next_subnode(blob, nand_node)) {
|
|
|
- ret = sunxi_nand_chip_init(nand_node, nfc, i++);
|
|
|
+ dev_for_each_subnode(nand_np, dev) {
|
|
|
+ ret = sunxi_nand_chip_init(dev, nfc, nand_np, i++);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
}
|
|
@@ -1802,55 +1775,67 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
|
|
|
}
|
|
|
#endif /* __UBOOT__ */
|
|
|
|
|
|
-void sunxi_nand_init(void)
|
|
|
+static int sunxi_nand_probe(struct udevice *dev)
|
|
|
{
|
|
|
- const void *blob = gd->fdt_blob;
|
|
|
- struct sunxi_nfc *nfc;
|
|
|
- fdt_addr_t regs;
|
|
|
- int node;
|
|
|
+ struct sunxi_nfc *nfc = dev_get_priv(dev);
|
|
|
+ struct reset_ctl_bulk rst_bulk;
|
|
|
+ struct clk_bulk clk_bulk;
|
|
|
int ret;
|
|
|
|
|
|
- nfc = kzalloc(sizeof(*nfc), GFP_KERNEL);
|
|
|
- if (!nfc)
|
|
|
- return;
|
|
|
-
|
|
|
+ nfc->dev = dev;
|
|
|
spin_lock_init(&nfc->controller.lock);
|
|
|
init_waitqueue_head(&nfc->controller.wq);
|
|
|
INIT_LIST_HEAD(&nfc->chips);
|
|
|
|
|
|
- node = fdtdec_next_compatible(blob, 0, COMPAT_SUNXI_NAND);
|
|
|
- if (node < 0) {
|
|
|
- pr_err("unable to find nfc node in device tree\n");
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ nfc->regs = dev_read_addr_ptr(dev);
|
|
|
+ if (!nfc->regs)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- if (!fdtdec_get_is_enabled(blob, node)) {
|
|
|
- pr_err("nfc disabled in device tree\n");
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ ret = reset_get_bulk(dev, &rst_bulk);
|
|
|
+ if (!ret)
|
|
|
+ reset_deassert_bulk(&rst_bulk);
|
|
|
|
|
|
- regs = fdtdec_get_addr(blob, node, "reg");
|
|
|
- if (regs == FDT_ADDR_T_NONE) {
|
|
|
- pr_err("unable to find nfc address in device tree\n");
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- nfc->regs = (void *)regs;
|
|
|
+ ret = clk_get_bulk(dev, &clk_bulk);
|
|
|
+ if (!ret)
|
|
|
+ clk_enable_bulk(&clk_bulk);
|
|
|
|
|
|
ret = sunxi_nfc_rst(nfc);
|
|
|
if (ret)
|
|
|
- goto err;
|
|
|
+ return ret;
|
|
|
|
|
|
- ret = sunxi_nand_chips_init(node, nfc);
|
|
|
+ ret = sunxi_nand_chips_init(dev, nfc);
|
|
|
if (ret) {
|
|
|
- dev_err(nfc->dev, "failed to init nand chips\n");
|
|
|
- goto err;
|
|
|
+ dev_err(dev, "failed to init nand chips\n");
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
- return;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
-err:
|
|
|
- kfree(nfc);
|
|
|
+static const struct udevice_id sunxi_nand_ids[] = {
|
|
|
+ {
|
|
|
+ .compatible = "allwinner,sun4i-a10-nand",
|
|
|
+ },
|
|
|
+ { }
|
|
|
+};
|
|
|
+
|
|
|
+U_BOOT_DRIVER(sunxi_nand) = {
|
|
|
+ .name = "sunxi_nand",
|
|
|
+ .id = UCLASS_MTD,
|
|
|
+ .of_match = sunxi_nand_ids,
|
|
|
+ .probe = sunxi_nand_probe,
|
|
|
+ .priv_auto = sizeof(struct sunxi_nfc),
|
|
|
+};
|
|
|
+
|
|
|
+void board_nand_init(void)
|
|
|
+{
|
|
|
+ struct udevice *dev;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = uclass_get_device_by_driver(UCLASS_MTD,
|
|
|
+ DM_DRIVER_GET(sunxi_nand), &dev);
|
|
|
+ if (ret && ret != -ENODEV)
|
|
|
+ pr_err("Failed to initialize sunxi NAND controller: %d\n", ret);
|
|
|
}
|
|
|
|
|
|
MODULE_LICENSE("GPL v2");
|