1dcc7d344SVignesh Raghavendra // SPDX-License-Identifier: GPL-2.0 2dcc7d344SVignesh Raghavendra // 3*614a895fSAlexander A. Klimov // Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ 4dcc7d344SVignesh Raghavendra // Author: Vignesh Raghavendra <vigneshr@ti.com> 5dcc7d344SVignesh Raghavendra 6dcc7d344SVignesh Raghavendra #include <linux/err.h> 7dcc7d344SVignesh Raghavendra #include <linux/kernel.h> 8dcc7d344SVignesh Raghavendra #include <linux/module.h> 9dcc7d344SVignesh Raghavendra #include <linux/mtd/hyperbus.h> 10dcc7d344SVignesh Raghavendra #include <linux/mtd/map.h> 11dcc7d344SVignesh Raghavendra #include <linux/mtd/mtd.h> 12dcc7d344SVignesh Raghavendra #include <linux/of.h> 13dcc7d344SVignesh Raghavendra #include <linux/types.h> 14dcc7d344SVignesh Raghavendra 15dcc7d344SVignesh Raghavendra static struct hyperbus_device *map_to_hbdev(struct map_info *map) 16dcc7d344SVignesh Raghavendra { 17dcc7d344SVignesh Raghavendra return container_of(map, struct hyperbus_device, map); 18dcc7d344SVignesh Raghavendra } 19dcc7d344SVignesh Raghavendra 20dcc7d344SVignesh Raghavendra static map_word hyperbus_read16(struct map_info *map, unsigned long addr) 21dcc7d344SVignesh Raghavendra { 22dcc7d344SVignesh Raghavendra struct hyperbus_device *hbdev = map_to_hbdev(map); 23dcc7d344SVignesh Raghavendra struct hyperbus_ctlr *ctlr = hbdev->ctlr; 24dcc7d344SVignesh Raghavendra map_word read_data; 25dcc7d344SVignesh Raghavendra 26dcc7d344SVignesh Raghavendra read_data.x[0] = ctlr->ops->read16(hbdev, addr); 27dcc7d344SVignesh Raghavendra 28dcc7d344SVignesh Raghavendra return read_data; 29dcc7d344SVignesh Raghavendra } 30dcc7d344SVignesh Raghavendra 31dcc7d344SVignesh Raghavendra static void hyperbus_write16(struct map_info *map, map_word d, 32dcc7d344SVignesh Raghavendra unsigned long addr) 33dcc7d344SVignesh Raghavendra { 34dcc7d344SVignesh Raghavendra struct hyperbus_device *hbdev = map_to_hbdev(map); 35dcc7d344SVignesh Raghavendra struct hyperbus_ctlr *ctlr = hbdev->ctlr; 36dcc7d344SVignesh Raghavendra 37dcc7d344SVignesh Raghavendra ctlr->ops->write16(hbdev, addr, d.x[0]); 38dcc7d344SVignesh Raghavendra } 39dcc7d344SVignesh Raghavendra 40dcc7d344SVignesh Raghavendra static void hyperbus_copy_from(struct map_info *map, void *to, 41dcc7d344SVignesh Raghavendra unsigned long from, ssize_t len) 42dcc7d344SVignesh Raghavendra { 43dcc7d344SVignesh Raghavendra struct hyperbus_device *hbdev = map_to_hbdev(map); 44dcc7d344SVignesh Raghavendra struct hyperbus_ctlr *ctlr = hbdev->ctlr; 45dcc7d344SVignesh Raghavendra 46dcc7d344SVignesh Raghavendra ctlr->ops->copy_from(hbdev, to, from, len); 47dcc7d344SVignesh Raghavendra } 48dcc7d344SVignesh Raghavendra 49dcc7d344SVignesh Raghavendra static void hyperbus_copy_to(struct map_info *map, unsigned long to, 50dcc7d344SVignesh Raghavendra const void *from, ssize_t len) 51dcc7d344SVignesh Raghavendra { 52dcc7d344SVignesh Raghavendra struct hyperbus_device *hbdev = map_to_hbdev(map); 53dcc7d344SVignesh Raghavendra struct hyperbus_ctlr *ctlr = hbdev->ctlr; 54dcc7d344SVignesh Raghavendra 55dcc7d344SVignesh Raghavendra ctlr->ops->copy_to(hbdev, to, from, len); 56dcc7d344SVignesh Raghavendra } 57dcc7d344SVignesh Raghavendra 58dcc7d344SVignesh Raghavendra int hyperbus_register_device(struct hyperbus_device *hbdev) 59dcc7d344SVignesh Raghavendra { 60dcc7d344SVignesh Raghavendra const struct hyperbus_ops *ops; 61dcc7d344SVignesh Raghavendra struct hyperbus_ctlr *ctlr; 62dcc7d344SVignesh Raghavendra struct device_node *np; 63dcc7d344SVignesh Raghavendra struct map_info *map; 64dcc7d344SVignesh Raghavendra struct device *dev; 65dcc7d344SVignesh Raghavendra int ret; 66dcc7d344SVignesh Raghavendra 67dcc7d344SVignesh Raghavendra if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) { 68dcc7d344SVignesh Raghavendra pr_err("hyperbus: please fill all the necessary fields!\n"); 69dcc7d344SVignesh Raghavendra return -EINVAL; 70dcc7d344SVignesh Raghavendra } 71dcc7d344SVignesh Raghavendra 72dcc7d344SVignesh Raghavendra np = hbdev->np; 73dcc7d344SVignesh Raghavendra ctlr = hbdev->ctlr; 74cb6176efSDirk Behme if (!of_device_is_compatible(np, "cypress,hyperflash")) { 75cb6176efSDirk Behme dev_err(ctlr->dev, "\"cypress,hyperflash\" compatible missing\n"); 76dcc7d344SVignesh Raghavendra return -ENODEV; 77cb6176efSDirk Behme } 78dcc7d344SVignesh Raghavendra 79dcc7d344SVignesh Raghavendra hbdev->memtype = HYPERFLASH; 80dcc7d344SVignesh Raghavendra 81dcc7d344SVignesh Raghavendra dev = ctlr->dev; 82dcc7d344SVignesh Raghavendra map = &hbdev->map; 83dcc7d344SVignesh Raghavendra map->name = dev_name(dev); 84dcc7d344SVignesh Raghavendra map->bankwidth = 2; 85dcc7d344SVignesh Raghavendra map->device_node = np; 86dcc7d344SVignesh Raghavendra 87dcc7d344SVignesh Raghavendra simple_map_init(map); 88dcc7d344SVignesh Raghavendra ops = ctlr->ops; 89dcc7d344SVignesh Raghavendra if (ops) { 90dcc7d344SVignesh Raghavendra if (ops->read16) 91dcc7d344SVignesh Raghavendra map->read = hyperbus_read16; 92dcc7d344SVignesh Raghavendra if (ops->write16) 93dcc7d344SVignesh Raghavendra map->write = hyperbus_write16; 94dcc7d344SVignesh Raghavendra if (ops->copy_to) 95dcc7d344SVignesh Raghavendra map->copy_to = hyperbus_copy_to; 96dcc7d344SVignesh Raghavendra if (ops->copy_from) 97dcc7d344SVignesh Raghavendra map->copy_from = hyperbus_copy_from; 98dcc7d344SVignesh Raghavendra 99dcc7d344SVignesh Raghavendra if (ops->calibrate && !ctlr->calibrated) { 100dcc7d344SVignesh Raghavendra ret = ops->calibrate(hbdev); 101dcc7d344SVignesh Raghavendra if (!ret) { 102dcc7d344SVignesh Raghavendra dev_err(dev, "Calibration failed\n"); 103dcc7d344SVignesh Raghavendra return -ENODEV; 104dcc7d344SVignesh Raghavendra } 105dcc7d344SVignesh Raghavendra ctlr->calibrated = true; 106dcc7d344SVignesh Raghavendra } 107dcc7d344SVignesh Raghavendra } 108dcc7d344SVignesh Raghavendra 109dcc7d344SVignesh Raghavendra hbdev->mtd = do_map_probe("cfi_probe", map); 110dcc7d344SVignesh Raghavendra if (!hbdev->mtd) { 111dcc7d344SVignesh Raghavendra dev_err(dev, "probing of hyperbus device failed\n"); 112dcc7d344SVignesh Raghavendra return -ENODEV; 113dcc7d344SVignesh Raghavendra } 114dcc7d344SVignesh Raghavendra 115dcc7d344SVignesh Raghavendra hbdev->mtd->dev.parent = dev; 116dcc7d344SVignesh Raghavendra mtd_set_of_node(hbdev->mtd, np); 117dcc7d344SVignesh Raghavendra 118dcc7d344SVignesh Raghavendra ret = mtd_device_register(hbdev->mtd, NULL, 0); 119dcc7d344SVignesh Raghavendra if (ret) { 120dcc7d344SVignesh Raghavendra dev_err(dev, "failed to register mtd device\n"); 121dcc7d344SVignesh Raghavendra map_destroy(hbdev->mtd); 122dcc7d344SVignesh Raghavendra return ret; 123dcc7d344SVignesh Raghavendra } 124dcc7d344SVignesh Raghavendra 125dcc7d344SVignesh Raghavendra return 0; 126dcc7d344SVignesh Raghavendra } 127dcc7d344SVignesh Raghavendra EXPORT_SYMBOL_GPL(hyperbus_register_device); 128dcc7d344SVignesh Raghavendra 129dcc7d344SVignesh Raghavendra int hyperbus_unregister_device(struct hyperbus_device *hbdev) 130dcc7d344SVignesh Raghavendra { 131dcc7d344SVignesh Raghavendra int ret = 0; 132dcc7d344SVignesh Raghavendra 133dcc7d344SVignesh Raghavendra if (hbdev && hbdev->mtd) { 134dcc7d344SVignesh Raghavendra ret = mtd_device_unregister(hbdev->mtd); 135dcc7d344SVignesh Raghavendra map_destroy(hbdev->mtd); 136dcc7d344SVignesh Raghavendra } 137dcc7d344SVignesh Raghavendra 138dcc7d344SVignesh Raghavendra return ret; 139dcc7d344SVignesh Raghavendra } 140dcc7d344SVignesh Raghavendra EXPORT_SYMBOL_GPL(hyperbus_unregister_device); 141dcc7d344SVignesh Raghavendra 142dcc7d344SVignesh Raghavendra MODULE_DESCRIPTION("HyperBus Framework"); 143dcc7d344SVignesh Raghavendra MODULE_LICENSE("GPL v2"); 144dcc7d344SVignesh Raghavendra MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); 145