1b07079f1SVignesh Raghavendra // SPDX-License-Identifier: GPL-2.0 2b07079f1SVignesh Raghavendra // 3614a895fSAlexander A. Klimov // Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ 4b07079f1SVignesh Raghavendra // Author: Vignesh Raghavendra <vigneshr@ti.com> 5b07079f1SVignesh Raghavendra 6b07079f1SVignesh Raghavendra #include <linux/err.h> 7b07079f1SVignesh Raghavendra #include <linux/kernel.h> 8b07079f1SVignesh Raghavendra #include <linux/module.h> 9b07079f1SVignesh Raghavendra #include <linux/mtd/cfi.h> 10b07079f1SVignesh Raghavendra #include <linux/mtd/hyperbus.h> 11b07079f1SVignesh Raghavendra #include <linux/mtd/mtd.h> 12b07079f1SVignesh Raghavendra #include <linux/mux/consumer.h> 13b07079f1SVignesh Raghavendra #include <linux/of.h> 14b6fe8bc6SSergei Shtylyov #include <linux/of_address.h> 15b07079f1SVignesh Raghavendra #include <linux/platform_device.h> 16b07079f1SVignesh Raghavendra #include <linux/types.h> 17b07079f1SVignesh Raghavendra 18b07079f1SVignesh Raghavendra #define AM654_HBMC_CALIB_COUNT 25 19b07079f1SVignesh Raghavendra 20b07079f1SVignesh Raghavendra struct am654_hbmc_priv { 21b07079f1SVignesh Raghavendra struct hyperbus_ctlr ctlr; 22b07079f1SVignesh Raghavendra struct hyperbus_device hbdev; 23b07079f1SVignesh Raghavendra struct mux_control *mux_ctrl; 24b07079f1SVignesh Raghavendra }; 25b07079f1SVignesh Raghavendra 26b07079f1SVignesh Raghavendra static int am654_hbmc_calibrate(struct hyperbus_device *hbdev) 27b07079f1SVignesh Raghavendra { 28b07079f1SVignesh Raghavendra struct map_info *map = &hbdev->map; 29b07079f1SVignesh Raghavendra struct cfi_private cfi; 30b07079f1SVignesh Raghavendra int count = AM654_HBMC_CALIB_COUNT; 31b07079f1SVignesh Raghavendra int pass_count = 0; 32b07079f1SVignesh Raghavendra int ret; 33b07079f1SVignesh Raghavendra 34b07079f1SVignesh Raghavendra cfi.interleave = 1; 35b07079f1SVignesh Raghavendra cfi.device_type = CFI_DEVICETYPE_X16; 36b07079f1SVignesh Raghavendra cfi_send_gen_cmd(0xF0, 0, 0, map, &cfi, cfi.device_type, NULL); 37b07079f1SVignesh Raghavendra cfi_send_gen_cmd(0x98, 0x55, 0, map, &cfi, cfi.device_type, NULL); 38b07079f1SVignesh Raghavendra 39b07079f1SVignesh Raghavendra while (count--) { 40b07079f1SVignesh Raghavendra ret = cfi_qry_present(map, 0, &cfi); 41b07079f1SVignesh Raghavendra if (ret) 42b07079f1SVignesh Raghavendra pass_count++; 43b07079f1SVignesh Raghavendra else 44b07079f1SVignesh Raghavendra pass_count = 0; 45b07079f1SVignesh Raghavendra if (pass_count == 5) 46b07079f1SVignesh Raghavendra break; 47b07079f1SVignesh Raghavendra } 48b07079f1SVignesh Raghavendra 49b07079f1SVignesh Raghavendra cfi_qry_mode_off(0, map, &cfi); 50b07079f1SVignesh Raghavendra 51b07079f1SVignesh Raghavendra return ret; 52b07079f1SVignesh Raghavendra } 53b07079f1SVignesh Raghavendra 54b07079f1SVignesh Raghavendra static const struct hyperbus_ops am654_hbmc_ops = { 55b07079f1SVignesh Raghavendra .calibrate = am654_hbmc_calibrate, 56b07079f1SVignesh Raghavendra }; 57b07079f1SVignesh Raghavendra 58b07079f1SVignesh Raghavendra static int am654_hbmc_probe(struct platform_device *pdev) 59b07079f1SVignesh Raghavendra { 60b6fe8bc6SSergei Shtylyov struct device_node *np = pdev->dev.of_node; 61b07079f1SVignesh Raghavendra struct device *dev = &pdev->dev; 62b07079f1SVignesh Raghavendra struct am654_hbmc_priv *priv; 63b6fe8bc6SSergei Shtylyov struct resource res; 64b07079f1SVignesh Raghavendra int ret; 65b07079f1SVignesh Raghavendra 66b07079f1SVignesh Raghavendra priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 67b07079f1SVignesh Raghavendra if (!priv) 68b07079f1SVignesh Raghavendra return -ENOMEM; 69b07079f1SVignesh Raghavendra 70b07079f1SVignesh Raghavendra platform_set_drvdata(pdev, priv); 71b07079f1SVignesh Raghavendra 72aca31ce9SVignesh Raghavendra priv->hbdev.np = of_get_next_child(np, NULL); 73aca31ce9SVignesh Raghavendra ret = of_address_to_resource(priv->hbdev.np, 0, &res); 74b6fe8bc6SSergei Shtylyov if (ret) 75b6fe8bc6SSergei Shtylyov return ret; 76b6fe8bc6SSergei Shtylyov 77b07079f1SVignesh Raghavendra if (of_property_read_bool(dev->of_node, "mux-controls")) { 78b07079f1SVignesh Raghavendra struct mux_control *control = devm_mux_control_get(dev, NULL); 79b07079f1SVignesh Raghavendra 80b07079f1SVignesh Raghavendra if (IS_ERR(control)) 81b07079f1SVignesh Raghavendra return PTR_ERR(control); 82b07079f1SVignesh Raghavendra 83b07079f1SVignesh Raghavendra ret = mux_control_select(control, 1); 84b07079f1SVignesh Raghavendra if (ret) { 85b07079f1SVignesh Raghavendra dev_err(dev, "Failed to select HBMC mux\n"); 86b07079f1SVignesh Raghavendra return ret; 87b07079f1SVignesh Raghavendra } 88b07079f1SVignesh Raghavendra priv->mux_ctrl = control; 89b07079f1SVignesh Raghavendra } 90b07079f1SVignesh Raghavendra 91b6fe8bc6SSergei Shtylyov priv->hbdev.map.size = resource_size(&res); 92b6fe8bc6SSergei Shtylyov priv->hbdev.map.virt = devm_ioremap_resource(dev, &res); 93b6fe8bc6SSergei Shtylyov if (IS_ERR(priv->hbdev.map.virt)) 94b6fe8bc6SSergei Shtylyov return PTR_ERR(priv->hbdev.map.virt); 95b6fe8bc6SSergei Shtylyov 96b07079f1SVignesh Raghavendra priv->ctlr.dev = dev; 97b07079f1SVignesh Raghavendra priv->ctlr.ops = &am654_hbmc_ops; 98b07079f1SVignesh Raghavendra priv->hbdev.ctlr = &priv->ctlr; 99b07079f1SVignesh Raghavendra ret = hyperbus_register_device(&priv->hbdev); 100b07079f1SVignesh Raghavendra if (ret) { 101b07079f1SVignesh Raghavendra dev_err(dev, "failed to register controller\n"); 102*992df3bbSVignesh Raghavendra goto disable_mux; 103b07079f1SVignesh Raghavendra } 104b07079f1SVignesh Raghavendra 105b07079f1SVignesh Raghavendra return 0; 106*992df3bbSVignesh Raghavendra disable_mux: 107b07079f1SVignesh Raghavendra if (priv->mux_ctrl) 108b07079f1SVignesh Raghavendra mux_control_deselect(priv->mux_ctrl); 109b07079f1SVignesh Raghavendra return ret; 110b07079f1SVignesh Raghavendra } 111b07079f1SVignesh Raghavendra 112b07079f1SVignesh Raghavendra static int am654_hbmc_remove(struct platform_device *pdev) 113b07079f1SVignesh Raghavendra { 114b07079f1SVignesh Raghavendra struct am654_hbmc_priv *priv = platform_get_drvdata(pdev); 115b07079f1SVignesh Raghavendra int ret; 116b07079f1SVignesh Raghavendra 117b07079f1SVignesh Raghavendra ret = hyperbus_unregister_device(&priv->hbdev); 118b07079f1SVignesh Raghavendra if (priv->mux_ctrl) 119b07079f1SVignesh Raghavendra mux_control_deselect(priv->mux_ctrl); 120b07079f1SVignesh Raghavendra 121b07079f1SVignesh Raghavendra return ret; 122b07079f1SVignesh Raghavendra } 123b07079f1SVignesh Raghavendra 124b07079f1SVignesh Raghavendra static const struct of_device_id am654_hbmc_dt_ids[] = { 125b07079f1SVignesh Raghavendra { 126b07079f1SVignesh Raghavendra .compatible = "ti,am654-hbmc", 127b07079f1SVignesh Raghavendra }, 128b07079f1SVignesh Raghavendra { /* end of table */ } 129b07079f1SVignesh Raghavendra }; 130b07079f1SVignesh Raghavendra 131b07079f1SVignesh Raghavendra MODULE_DEVICE_TABLE(of, am654_hbmc_dt_ids); 132b07079f1SVignesh Raghavendra 133b07079f1SVignesh Raghavendra static struct platform_driver am654_hbmc_platform_driver = { 134b07079f1SVignesh Raghavendra .probe = am654_hbmc_probe, 135b07079f1SVignesh Raghavendra .remove = am654_hbmc_remove, 136b07079f1SVignesh Raghavendra .driver = { 137b07079f1SVignesh Raghavendra .name = "hbmc-am654", 138b07079f1SVignesh Raghavendra .of_match_table = am654_hbmc_dt_ids, 139b07079f1SVignesh Raghavendra }, 140b07079f1SVignesh Raghavendra }; 141b07079f1SVignesh Raghavendra 142b07079f1SVignesh Raghavendra module_platform_driver(am654_hbmc_platform_driver); 143b07079f1SVignesh Raghavendra 144b07079f1SVignesh Raghavendra MODULE_DESCRIPTION("HBMC driver for AM654 SoC"); 145b07079f1SVignesh Raghavendra MODULE_LICENSE("GPL v2"); 146b07079f1SVignesh Raghavendra MODULE_ALIAS("platform:hbmc-am654"); 147b07079f1SVignesh Raghavendra MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); 148