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