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