1*5de15b61SSergei Shtylyov // SPDX-License-Identifier: GPL-2.0 2*5de15b61SSergei Shtylyov /* 3*5de15b61SSergei Shtylyov * Linux driver for RPC-IF HyperFlash 4*5de15b61SSergei Shtylyov * 5*5de15b61SSergei Shtylyov * Copyright (C) 2019-2020 Cogent Embedded, Inc. 6*5de15b61SSergei Shtylyov */ 7*5de15b61SSergei Shtylyov 8*5de15b61SSergei Shtylyov #include <linux/err.h> 9*5de15b61SSergei Shtylyov #include <linux/kernel.h> 10*5de15b61SSergei Shtylyov #include <linux/module.h> 11*5de15b61SSergei Shtylyov #include <linux/mtd/hyperbus.h> 12*5de15b61SSergei Shtylyov #include <linux/mtd/mtd.h> 13*5de15b61SSergei Shtylyov #include <linux/mux/consumer.h> 14*5de15b61SSergei Shtylyov #include <linux/of.h> 15*5de15b61SSergei Shtylyov #include <linux/platform_device.h> 16*5de15b61SSergei Shtylyov #include <linux/types.h> 17*5de15b61SSergei Shtylyov 18*5de15b61SSergei Shtylyov #include <memory/renesas-rpc-if.h> 19*5de15b61SSergei Shtylyov 20*5de15b61SSergei Shtylyov struct rpcif_hyperbus { 21*5de15b61SSergei Shtylyov struct rpcif rpc; 22*5de15b61SSergei Shtylyov struct hyperbus_ctlr ctlr; 23*5de15b61SSergei Shtylyov struct hyperbus_device hbdev; 24*5de15b61SSergei Shtylyov }; 25*5de15b61SSergei Shtylyov 26*5de15b61SSergei Shtylyov static const struct rpcif_op rpcif_op_tmpl = { 27*5de15b61SSergei Shtylyov .cmd = { 28*5de15b61SSergei Shtylyov .buswidth = 8, 29*5de15b61SSergei Shtylyov .ddr = true, 30*5de15b61SSergei Shtylyov }, 31*5de15b61SSergei Shtylyov .ocmd = { 32*5de15b61SSergei Shtylyov .buswidth = 8, 33*5de15b61SSergei Shtylyov .ddr = true, 34*5de15b61SSergei Shtylyov }, 35*5de15b61SSergei Shtylyov .addr = { 36*5de15b61SSergei Shtylyov .nbytes = 1, 37*5de15b61SSergei Shtylyov .buswidth = 8, 38*5de15b61SSergei Shtylyov .ddr = true, 39*5de15b61SSergei Shtylyov }, 40*5de15b61SSergei Shtylyov .data = { 41*5de15b61SSergei Shtylyov .buswidth = 8, 42*5de15b61SSergei Shtylyov .ddr = true, 43*5de15b61SSergei Shtylyov }, 44*5de15b61SSergei Shtylyov }; 45*5de15b61SSergei Shtylyov 46*5de15b61SSergei Shtylyov static void rpcif_hb_prepare_read(struct rpcif *rpc, void *to, 47*5de15b61SSergei Shtylyov unsigned long from, ssize_t len) 48*5de15b61SSergei Shtylyov { 49*5de15b61SSergei Shtylyov struct rpcif_op op = rpcif_op_tmpl; 50*5de15b61SSergei Shtylyov 51*5de15b61SSergei Shtylyov op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM; 52*5de15b61SSergei Shtylyov op.addr.val = from >> 1; 53*5de15b61SSergei Shtylyov op.dummy.buswidth = 1; 54*5de15b61SSergei Shtylyov op.dummy.ncycles = 15; 55*5de15b61SSergei Shtylyov op.data.dir = RPCIF_DATA_IN; 56*5de15b61SSergei Shtylyov op.data.nbytes = len; 57*5de15b61SSergei Shtylyov op.data.buf.in = to; 58*5de15b61SSergei Shtylyov 59*5de15b61SSergei Shtylyov rpcif_prepare(rpc, &op, NULL, NULL); 60*5de15b61SSergei Shtylyov } 61*5de15b61SSergei Shtylyov 62*5de15b61SSergei Shtylyov static void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to, 63*5de15b61SSergei Shtylyov void *from, ssize_t len) 64*5de15b61SSergei Shtylyov { 65*5de15b61SSergei Shtylyov struct rpcif_op op = rpcif_op_tmpl; 66*5de15b61SSergei Shtylyov 67*5de15b61SSergei Shtylyov op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM; 68*5de15b61SSergei Shtylyov op.addr.val = to >> 1; 69*5de15b61SSergei Shtylyov op.data.dir = RPCIF_DATA_OUT; 70*5de15b61SSergei Shtylyov op.data.nbytes = len; 71*5de15b61SSergei Shtylyov op.data.buf.out = from; 72*5de15b61SSergei Shtylyov 73*5de15b61SSergei Shtylyov rpcif_prepare(rpc, &op, NULL, NULL); 74*5de15b61SSergei Shtylyov } 75*5de15b61SSergei Shtylyov 76*5de15b61SSergei Shtylyov static u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr) 77*5de15b61SSergei Shtylyov { 78*5de15b61SSergei Shtylyov struct rpcif_hyperbus *hyperbus = 79*5de15b61SSergei Shtylyov container_of(hbdev, struct rpcif_hyperbus, hbdev); 80*5de15b61SSergei Shtylyov map_word data; 81*5de15b61SSergei Shtylyov 82*5de15b61SSergei Shtylyov rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2); 83*5de15b61SSergei Shtylyov 84*5de15b61SSergei Shtylyov rpcif_manual_xfer(&hyperbus->rpc); 85*5de15b61SSergei Shtylyov 86*5de15b61SSergei Shtylyov return data.x[0]; 87*5de15b61SSergei Shtylyov } 88*5de15b61SSergei Shtylyov 89*5de15b61SSergei Shtylyov static void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr, 90*5de15b61SSergei Shtylyov u16 data) 91*5de15b61SSergei Shtylyov { 92*5de15b61SSergei Shtylyov struct rpcif_hyperbus *hyperbus = 93*5de15b61SSergei Shtylyov container_of(hbdev, struct rpcif_hyperbus, hbdev); 94*5de15b61SSergei Shtylyov 95*5de15b61SSergei Shtylyov rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2); 96*5de15b61SSergei Shtylyov 97*5de15b61SSergei Shtylyov rpcif_manual_xfer(&hyperbus->rpc); 98*5de15b61SSergei Shtylyov } 99*5de15b61SSergei Shtylyov 100*5de15b61SSergei Shtylyov static void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to, 101*5de15b61SSergei Shtylyov unsigned long from, ssize_t len) 102*5de15b61SSergei Shtylyov { 103*5de15b61SSergei Shtylyov struct rpcif_hyperbus *hyperbus = 104*5de15b61SSergei Shtylyov container_of(hbdev, struct rpcif_hyperbus, hbdev); 105*5de15b61SSergei Shtylyov 106*5de15b61SSergei Shtylyov rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len); 107*5de15b61SSergei Shtylyov 108*5de15b61SSergei Shtylyov rpcif_dirmap_read(&hyperbus->rpc, from, len, to); 109*5de15b61SSergei Shtylyov } 110*5de15b61SSergei Shtylyov 111*5de15b61SSergei Shtylyov static const struct hyperbus_ops rpcif_hb_ops = { 112*5de15b61SSergei Shtylyov .read16 = rpcif_hb_read16, 113*5de15b61SSergei Shtylyov .write16 = rpcif_hb_write16, 114*5de15b61SSergei Shtylyov .copy_from = rpcif_hb_copy_from, 115*5de15b61SSergei Shtylyov }; 116*5de15b61SSergei Shtylyov 117*5de15b61SSergei Shtylyov static int rpcif_hb_probe(struct platform_device *pdev) 118*5de15b61SSergei Shtylyov { 119*5de15b61SSergei Shtylyov struct device *dev = &pdev->dev; 120*5de15b61SSergei Shtylyov struct rpcif_hyperbus *hyperbus; 121*5de15b61SSergei Shtylyov int error; 122*5de15b61SSergei Shtylyov 123*5de15b61SSergei Shtylyov hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL); 124*5de15b61SSergei Shtylyov if (!hyperbus) 125*5de15b61SSergei Shtylyov return -ENOMEM; 126*5de15b61SSergei Shtylyov 127*5de15b61SSergei Shtylyov rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent); 128*5de15b61SSergei Shtylyov 129*5de15b61SSergei Shtylyov platform_set_drvdata(pdev, hyperbus); 130*5de15b61SSergei Shtylyov 131*5de15b61SSergei Shtylyov rpcif_enable_rpm(&hyperbus->rpc); 132*5de15b61SSergei Shtylyov 133*5de15b61SSergei Shtylyov rpcif_hw_init(&hyperbus->rpc, true); 134*5de15b61SSergei Shtylyov 135*5de15b61SSergei Shtylyov hyperbus->hbdev.map.size = hyperbus->rpc.size; 136*5de15b61SSergei Shtylyov hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap; 137*5de15b61SSergei Shtylyov 138*5de15b61SSergei Shtylyov hyperbus->ctlr.dev = dev; 139*5de15b61SSergei Shtylyov hyperbus->ctlr.ops = &rpcif_hb_ops; 140*5de15b61SSergei Shtylyov hyperbus->hbdev.ctlr = &hyperbus->ctlr; 141*5de15b61SSergei Shtylyov hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL); 142*5de15b61SSergei Shtylyov error = hyperbus_register_device(&hyperbus->hbdev); 143*5de15b61SSergei Shtylyov if (error) 144*5de15b61SSergei Shtylyov rpcif_disable_rpm(&hyperbus->rpc); 145*5de15b61SSergei Shtylyov 146*5de15b61SSergei Shtylyov return error; 147*5de15b61SSergei Shtylyov } 148*5de15b61SSergei Shtylyov 149*5de15b61SSergei Shtylyov static int rpcif_hb_remove(struct platform_device *pdev) 150*5de15b61SSergei Shtylyov { 151*5de15b61SSergei Shtylyov struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev); 152*5de15b61SSergei Shtylyov int error = hyperbus_unregister_device(&hyperbus->hbdev); 153*5de15b61SSergei Shtylyov struct rpcif *rpc = dev_get_drvdata(pdev->dev.parent); 154*5de15b61SSergei Shtylyov 155*5de15b61SSergei Shtylyov rpcif_disable_rpm(rpc); 156*5de15b61SSergei Shtylyov return error; 157*5de15b61SSergei Shtylyov } 158*5de15b61SSergei Shtylyov 159*5de15b61SSergei Shtylyov static struct platform_driver rpcif_platform_driver = { 160*5de15b61SSergei Shtylyov .probe = rpcif_hb_probe, 161*5de15b61SSergei Shtylyov .remove = rpcif_hb_remove, 162*5de15b61SSergei Shtylyov .driver = { 163*5de15b61SSergei Shtylyov .name = "rpc-if-hyperflash", 164*5de15b61SSergei Shtylyov }, 165*5de15b61SSergei Shtylyov }; 166*5de15b61SSergei Shtylyov 167*5de15b61SSergei Shtylyov module_platform_driver(rpcif_platform_driver); 168*5de15b61SSergei Shtylyov 169*5de15b61SSergei Shtylyov MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver"); 170*5de15b61SSergei Shtylyov MODULE_LICENSE("GPL v2"); 171