162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ 462306a36Sopenharmony_ci// Author: Vignesh Raghavendra <vigneshr@ti.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/err.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/mtd/hyperbus.h> 1062306a36Sopenharmony_ci#include <linux/mtd/map.h> 1162306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic struct hyperbus_device *map_to_hbdev(struct map_info *map) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci return container_of(map, struct hyperbus_device, map); 1862306a36Sopenharmony_ci} 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic map_word hyperbus_read16(struct map_info *map, unsigned long addr) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct hyperbus_device *hbdev = map_to_hbdev(map); 2362306a36Sopenharmony_ci struct hyperbus_ctlr *ctlr = hbdev->ctlr; 2462306a36Sopenharmony_ci map_word read_data; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci read_data.x[0] = ctlr->ops->read16(hbdev, addr); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci return read_data; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void hyperbus_write16(struct map_info *map, map_word d, 3262306a36Sopenharmony_ci unsigned long addr) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct hyperbus_device *hbdev = map_to_hbdev(map); 3562306a36Sopenharmony_ci struct hyperbus_ctlr *ctlr = hbdev->ctlr; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci ctlr->ops->write16(hbdev, addr, d.x[0]); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void hyperbus_copy_from(struct map_info *map, void *to, 4162306a36Sopenharmony_ci unsigned long from, ssize_t len) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct hyperbus_device *hbdev = map_to_hbdev(map); 4462306a36Sopenharmony_ci struct hyperbus_ctlr *ctlr = hbdev->ctlr; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci ctlr->ops->copy_from(hbdev, to, from, len); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void hyperbus_copy_to(struct map_info *map, unsigned long to, 5062306a36Sopenharmony_ci const void *from, ssize_t len) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct hyperbus_device *hbdev = map_to_hbdev(map); 5362306a36Sopenharmony_ci struct hyperbus_ctlr *ctlr = hbdev->ctlr; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ctlr->ops->copy_to(hbdev, to, from, len); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciint hyperbus_register_device(struct hyperbus_device *hbdev) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci const struct hyperbus_ops *ops; 6162306a36Sopenharmony_ci struct hyperbus_ctlr *ctlr; 6262306a36Sopenharmony_ci struct device_node *np; 6362306a36Sopenharmony_ci struct map_info *map; 6462306a36Sopenharmony_ci struct device *dev; 6562306a36Sopenharmony_ci int ret; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) { 6862306a36Sopenharmony_ci pr_err("hyperbus: please fill all the necessary fields!\n"); 6962306a36Sopenharmony_ci return -EINVAL; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci np = hbdev->np; 7362306a36Sopenharmony_ci ctlr = hbdev->ctlr; 7462306a36Sopenharmony_ci if (!of_device_is_compatible(np, "cypress,hyperflash")) { 7562306a36Sopenharmony_ci dev_err(ctlr->dev, "\"cypress,hyperflash\" compatible missing\n"); 7662306a36Sopenharmony_ci return -ENODEV; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci hbdev->memtype = HYPERFLASH; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci dev = ctlr->dev; 8262306a36Sopenharmony_ci map = &hbdev->map; 8362306a36Sopenharmony_ci map->name = dev_name(dev); 8462306a36Sopenharmony_ci map->bankwidth = 2; 8562306a36Sopenharmony_ci map->device_node = np; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci simple_map_init(map); 8862306a36Sopenharmony_ci ops = ctlr->ops; 8962306a36Sopenharmony_ci if (ops) { 9062306a36Sopenharmony_ci if (ops->read16) 9162306a36Sopenharmony_ci map->read = hyperbus_read16; 9262306a36Sopenharmony_ci if (ops->write16) 9362306a36Sopenharmony_ci map->write = hyperbus_write16; 9462306a36Sopenharmony_ci if (ops->copy_to) 9562306a36Sopenharmony_ci map->copy_to = hyperbus_copy_to; 9662306a36Sopenharmony_ci if (ops->copy_from) 9762306a36Sopenharmony_ci map->copy_from = hyperbus_copy_from; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (ops->calibrate && !ctlr->calibrated) { 10062306a36Sopenharmony_ci ret = ops->calibrate(hbdev); 10162306a36Sopenharmony_ci if (!ret) { 10262306a36Sopenharmony_ci dev_err(dev, "Calibration failed\n"); 10362306a36Sopenharmony_ci return -ENODEV; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci ctlr->calibrated = true; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci hbdev->mtd = do_map_probe("cfi_probe", map); 11062306a36Sopenharmony_ci if (!hbdev->mtd) { 11162306a36Sopenharmony_ci dev_err(dev, "probing of hyperbus device failed\n"); 11262306a36Sopenharmony_ci return -ENODEV; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci hbdev->mtd->dev.parent = dev; 11662306a36Sopenharmony_ci mtd_set_of_node(hbdev->mtd, np); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ret = mtd_device_register(hbdev->mtd, NULL, 0); 11962306a36Sopenharmony_ci if (ret) { 12062306a36Sopenharmony_ci dev_err(dev, "failed to register mtd device\n"); 12162306a36Sopenharmony_ci map_destroy(hbdev->mtd); 12262306a36Sopenharmony_ci return ret; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperbus_register_device); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_civoid hyperbus_unregister_device(struct hyperbus_device *hbdev) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci if (hbdev && hbdev->mtd) { 13262306a36Sopenharmony_ci WARN_ON(mtd_device_unregister(hbdev->mtd)); 13362306a36Sopenharmony_ci map_destroy(hbdev->mtd); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hyperbus_unregister_device); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciMODULE_DESCRIPTION("HyperBus Framework"); 13962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 14062306a36Sopenharmony_ciMODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); 141