1b07079f1SVignesh Raghavendra // SPDX-License-Identifier: GPL-2.0 2b07079f1SVignesh Raghavendra // 3*614a895fSAlexander 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/pm_runtime.h> 17b07079f1SVignesh Raghavendra #include <linux/types.h> 18b07079f1SVignesh Raghavendra 19b07079f1SVignesh Raghavendra #define AM654_HBMC_CALIB_COUNT 25 20b07079f1SVignesh Raghavendra 21b07079f1SVignesh Raghavendra struct am654_hbmc_priv { 22b07079f1SVignesh Raghavendra struct hyperbus_ctlr ctlr; 23b07079f1SVignesh Raghavendra struct hyperbus_device hbdev; 24b07079f1SVignesh Raghavendra struct mux_control *mux_ctrl; 25b07079f1SVignesh Raghavendra }; 26b07079f1SVignesh Raghavendra 27b07079f1SVignesh Raghavendra static int am654_hbmc_calibrate(struct hyperbus_device *hbdev) 28b07079f1SVignesh Raghavendra { 29b07079f1SVignesh Raghavendra struct map_info *map = &hbdev->map; 30b07079f1SVignesh Raghavendra struct cfi_private cfi; 31b07079f1SVignesh Raghavendra int count = AM654_HBMC_CALIB_COUNT; 32b07079f1SVignesh Raghavendra int pass_count = 0; 33b07079f1SVignesh Raghavendra int ret; 34b07079f1SVignesh Raghavendra 35b07079f1SVignesh Raghavendra cfi.interleave = 1; 36b07079f1SVignesh Raghavendra cfi.device_type = CFI_DEVICETYPE_X16; 37b07079f1SVignesh Raghavendra cfi_send_gen_cmd(0xF0, 0, 0, map, &cfi, cfi.device_type, NULL); 38b07079f1SVignesh Raghavendra cfi_send_gen_cmd(0x98, 0x55, 0, map, &cfi, cfi.device_type, NULL); 39b07079f1SVignesh Raghavendra 40b07079f1SVignesh Raghavendra while (count--) { 41b07079f1SVignesh Raghavendra ret = cfi_qry_present(map, 0, &cfi); 42b07079f1SVignesh Raghavendra if (ret) 43b07079f1SVignesh Raghavendra pass_count++; 44b07079f1SVignesh Raghavendra else 45b07079f1SVignesh Raghavendra pass_count = 0; 46b07079f1SVignesh Raghavendra if (pass_count == 5) 47b07079f1SVignesh Raghavendra break; 48b07079f1SVignesh Raghavendra } 49b07079f1SVignesh Raghavendra 50b07079f1SVignesh Raghavendra cfi_qry_mode_off(0, map, &cfi); 51b07079f1SVignesh Raghavendra 52b07079f1SVignesh Raghavendra return ret; 53b07079f1SVignesh Raghavendra } 54b07079f1SVignesh Raghavendra 55b07079f1SVignesh Raghavendra static const struct hyperbus_ops am654_hbmc_ops = { 56b07079f1SVignesh Raghavendra .calibrate = am654_hbmc_calibrate, 57b07079f1SVignesh Raghavendra }; 58b07079f1SVignesh Raghavendra 59b07079f1SVignesh Raghavendra static int am654_hbmc_probe(struct platform_device *pdev) 60b07079f1SVignesh Raghavendra { 61b6fe8bc6SSergei Shtylyov struct device_node *np = pdev->dev.of_node; 62b07079f1SVignesh Raghavendra struct device *dev = &pdev->dev; 63b07079f1SVignesh Raghavendra struct am654_hbmc_priv *priv; 64b6fe8bc6SSergei Shtylyov struct resource res; 65b07079f1SVignesh Raghavendra int ret; 66b07079f1SVignesh Raghavendra 67b07079f1SVignesh Raghavendra priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 68b07079f1SVignesh Raghavendra if (!priv) 69b07079f1SVignesh Raghavendra return -ENOMEM; 70b07079f1SVignesh Raghavendra 71b07079f1SVignesh Raghavendra platform_set_drvdata(pdev, priv); 72b07079f1SVignesh Raghavendra 73b6fe8bc6SSergei Shtylyov ret = of_address_to_resource(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 91b07079f1SVignesh Raghavendra pm_runtime_enable(dev); 92b07079f1SVignesh Raghavendra ret = pm_runtime_get_sync(dev); 93b07079f1SVignesh Raghavendra if (ret < 0) { 94b07079f1SVignesh Raghavendra pm_runtime_put_noidle(dev); 95b07079f1SVignesh Raghavendra goto disable_pm; 96b07079f1SVignesh Raghavendra } 97b07079f1SVignesh Raghavendra 98b6fe8bc6SSergei Shtylyov priv->hbdev.map.size = resource_size(&res); 99b6fe8bc6SSergei Shtylyov priv->hbdev.map.virt = devm_ioremap_resource(dev, &res); 100b6fe8bc6SSergei Shtylyov if (IS_ERR(priv->hbdev.map.virt)) 101b6fe8bc6SSergei Shtylyov return PTR_ERR(priv->hbdev.map.virt); 102b6fe8bc6SSergei Shtylyov 103b07079f1SVignesh Raghavendra priv->ctlr.dev = dev; 104b07079f1SVignesh Raghavendra priv->ctlr.ops = &am654_hbmc_ops; 105b07079f1SVignesh Raghavendra priv->hbdev.ctlr = &priv->ctlr; 106b07079f1SVignesh Raghavendra priv->hbdev.np = of_get_next_child(dev->of_node, NULL); 107b07079f1SVignesh Raghavendra ret = hyperbus_register_device(&priv->hbdev); 108b07079f1SVignesh Raghavendra if (ret) { 109b07079f1SVignesh Raghavendra dev_err(dev, "failed to register controller\n"); 110b07079f1SVignesh Raghavendra pm_runtime_put_sync(&pdev->dev); 111b07079f1SVignesh Raghavendra goto disable_pm; 112b07079f1SVignesh Raghavendra } 113b07079f1SVignesh Raghavendra 114b07079f1SVignesh Raghavendra return 0; 115b07079f1SVignesh Raghavendra disable_pm: 116b07079f1SVignesh Raghavendra pm_runtime_disable(dev); 117b07079f1SVignesh Raghavendra if (priv->mux_ctrl) 118b07079f1SVignesh Raghavendra mux_control_deselect(priv->mux_ctrl); 119b07079f1SVignesh Raghavendra return ret; 120b07079f1SVignesh Raghavendra } 121b07079f1SVignesh Raghavendra 122b07079f1SVignesh Raghavendra static int am654_hbmc_remove(struct platform_device *pdev) 123b07079f1SVignesh Raghavendra { 124b07079f1SVignesh Raghavendra struct am654_hbmc_priv *priv = platform_get_drvdata(pdev); 125b07079f1SVignesh Raghavendra int ret; 126b07079f1SVignesh Raghavendra 127b07079f1SVignesh Raghavendra ret = hyperbus_unregister_device(&priv->hbdev); 128b07079f1SVignesh Raghavendra if (priv->mux_ctrl) 129b07079f1SVignesh Raghavendra mux_control_deselect(priv->mux_ctrl); 130b07079f1SVignesh Raghavendra pm_runtime_put_sync(&pdev->dev); 131b07079f1SVignesh Raghavendra pm_runtime_disable(&pdev->dev); 132b07079f1SVignesh Raghavendra 133b07079f1SVignesh Raghavendra return ret; 134b07079f1SVignesh Raghavendra } 135b07079f1SVignesh Raghavendra 136b07079f1SVignesh Raghavendra static const struct of_device_id am654_hbmc_dt_ids[] = { 137b07079f1SVignesh Raghavendra { 138b07079f1SVignesh Raghavendra .compatible = "ti,am654-hbmc", 139b07079f1SVignesh Raghavendra }, 140b07079f1SVignesh Raghavendra { /* end of table */ } 141b07079f1SVignesh Raghavendra }; 142b07079f1SVignesh Raghavendra 143b07079f1SVignesh Raghavendra MODULE_DEVICE_TABLE(of, am654_hbmc_dt_ids); 144b07079f1SVignesh Raghavendra 145b07079f1SVignesh Raghavendra static struct platform_driver am654_hbmc_platform_driver = { 146b07079f1SVignesh Raghavendra .probe = am654_hbmc_probe, 147b07079f1SVignesh Raghavendra .remove = am654_hbmc_remove, 148b07079f1SVignesh Raghavendra .driver = { 149b07079f1SVignesh Raghavendra .name = "hbmc-am654", 150b07079f1SVignesh Raghavendra .of_match_table = am654_hbmc_dt_ids, 151b07079f1SVignesh Raghavendra }, 152b07079f1SVignesh Raghavendra }; 153b07079f1SVignesh Raghavendra 154b07079f1SVignesh Raghavendra module_platform_driver(am654_hbmc_platform_driver); 155b07079f1SVignesh Raghavendra 156b07079f1SVignesh Raghavendra MODULE_DESCRIPTION("HBMC driver for AM654 SoC"); 157b07079f1SVignesh Raghavendra MODULE_LICENSE("GPL v2"); 158b07079f1SVignesh Raghavendra MODULE_ALIAS("platform:hbmc-am654"); 159b07079f1SVignesh Raghavendra MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); 160